mirror of
https://gitee.com/openharmony/third_party_f2fs-tools
synced 2024-11-23 01:59:54 +00:00
Remove sg_write_buffer
Remove the sg_write_buffer source code and build rules now that the sg3_utils project has been imported. Signed-off-by: Bart Van Assche <bvanassche@google.com> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
This commit is contained in:
parent
c027dc1905
commit
f3344cf6db
1
.gitignore
vendored
1
.gitignore
vendored
@ -51,7 +51,6 @@ stamp-h1
|
|||||||
/tools/parse.f2fs
|
/tools/parse.f2fs
|
||||||
/tools/f2fscrypt
|
/tools/f2fscrypt
|
||||||
/tools/f2fs_io/f2fs_io
|
/tools/f2fs_io/f2fs_io
|
||||||
/tools/sg_write_buffer/sg_write_buffer
|
|
||||||
|
|
||||||
# cscope files
|
# cscope files
|
||||||
cscope.*
|
cscope.*
|
||||||
|
@ -260,7 +260,6 @@ AC_CONFIG_FILES([
|
|||||||
mkfs/Makefile
|
mkfs/Makefile
|
||||||
fsck/Makefile
|
fsck/Makefile
|
||||||
tools/Makefile
|
tools/Makefile
|
||||||
tools/sg_write_buffer/Makefile
|
|
||||||
tools/f2fs_io/Makefile
|
tools/f2fs_io/Makefile
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -17,4 +17,4 @@ f2fscrypt_LDFLAGS = ${libuuid_LIBS}
|
|||||||
dist_man_MANS = f2fscrypt.8
|
dist_man_MANS = f2fscrypt.8
|
||||||
endif
|
endif
|
||||||
|
|
||||||
SUBDIRS = sg_write_buffer f2fs_io
|
SUBDIRS = f2fs_io
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
## Makefile.am
|
|
||||||
|
|
||||||
if LINUX
|
|
||||||
AM_CPPFLAGS = -I$(srcdir)/include
|
|
||||||
AM_CFLAGS = -Wall
|
|
||||||
sbin_PROGRAMS = sg_write_buffer
|
|
||||||
sg_write_buffer_SOURCES = sg_write_buffer.c \
|
|
||||||
sg_cmds_basic.c \
|
|
||||||
sg_cmds_basic2.c \
|
|
||||||
sg_cmds_extra.c \
|
|
||||||
sg_cmds_mmc.c \
|
|
||||||
sg_io_linux.c \
|
|
||||||
sg_lib.c \
|
|
||||||
sg_lib_data.c \
|
|
||||||
sg_pt_common.c \
|
|
||||||
sg_pt_linux.c \
|
|
||||||
sg_pt_linux_nvme.c
|
|
||||||
endif
|
|
@ -1,156 +0,0 @@
|
|||||||
PROPS-END
|
|
||||||
/*-
|
|
||||||
* Copyright (C) 2012-2013 Intel Corporation
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
* SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
* $FreeBSD$
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include <sys/param.h>
|
|
||||||
|
|
||||||
#define NVME_PASSTHROUGH_CMD _IOWR('n', 0, struct nvme_pt_command)
|
|
||||||
|
|
||||||
#if __FreeBSD_version < 1100110
|
|
||||||
struct nvme_command
|
|
||||||
{
|
|
||||||
/* dword 0 */
|
|
||||||
uint16_t opc : 8; /* opcode */
|
|
||||||
uint16_t fuse : 2; /* fused operation */
|
|
||||||
uint16_t rsvd1 : 6;
|
|
||||||
uint16_t cid; /* command identifier */
|
|
||||||
|
|
||||||
/* dword 1 */
|
|
||||||
uint32_t nsid; /* namespace identifier */
|
|
||||||
|
|
||||||
/* dword 2-3 */
|
|
||||||
uint32_t rsvd2;
|
|
||||||
uint32_t rsvd3;
|
|
||||||
|
|
||||||
/* dword 4-5 */
|
|
||||||
uint64_t mptr; /* metadata pointer */
|
|
||||||
|
|
||||||
/* dword 6-7 */
|
|
||||||
uint64_t prp1; /* prp entry 1 */
|
|
||||||
|
|
||||||
/* dword 8-9 */
|
|
||||||
uint64_t prp2; /* prp entry 2 */
|
|
||||||
|
|
||||||
/* dword 10-15 */
|
|
||||||
uint32_t cdw10; /* command-specific */
|
|
||||||
uint32_t cdw11; /* command-specific */
|
|
||||||
uint32_t cdw12; /* command-specific */
|
|
||||||
uint32_t cdw13; /* command-specific */
|
|
||||||
uint32_t cdw14; /* command-specific */
|
|
||||||
uint32_t cdw15; /* command-specific */
|
|
||||||
} __packed;
|
|
||||||
|
|
||||||
struct nvme_status {
|
|
||||||
|
|
||||||
uint16_t p : 1; /* phase tag */
|
|
||||||
uint16_t sc : 8; /* status code */
|
|
||||||
uint16_t sct : 3; /* status code type */
|
|
||||||
uint16_t rsvd2 : 2;
|
|
||||||
uint16_t m : 1; /* more */
|
|
||||||
uint16_t dnr : 1; /* do not retry */
|
|
||||||
} __packed;
|
|
||||||
|
|
||||||
struct nvme_completion {
|
|
||||||
|
|
||||||
/* dword 0 */
|
|
||||||
uint32_t cdw0; /* command-specific */
|
|
||||||
|
|
||||||
/* dword 1 */
|
|
||||||
uint32_t rsvd1;
|
|
||||||
|
|
||||||
/* dword 2 */
|
|
||||||
uint16_t sqhd; /* submission queue head pointer */
|
|
||||||
uint16_t sqid; /* submission queue identifier */
|
|
||||||
|
|
||||||
/* dword 3 */
|
|
||||||
uint16_t cid; /* command identifier */
|
|
||||||
struct nvme_status status;
|
|
||||||
} __packed;
|
|
||||||
|
|
||||||
struct nvme_pt_command {
|
|
||||||
|
|
||||||
/*
|
|
||||||
* cmd is used to specify a passthrough command to a controller or
|
|
||||||
* namespace.
|
|
||||||
*
|
|
||||||
* The following fields from cmd may be specified by the caller:
|
|
||||||
* * opc (opcode)
|
|
||||||
* * nsid (namespace id) - for admin commands only
|
|
||||||
* * cdw10-cdw15
|
|
||||||
*
|
|
||||||
* Remaining fields must be set to 0 by the caller.
|
|
||||||
*/
|
|
||||||
struct nvme_command cmd;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* cpl returns completion status for the passthrough command
|
|
||||||
* specified by cmd.
|
|
||||||
*
|
|
||||||
* The following fields will be filled out by the driver, for
|
|
||||||
* consumption by the caller:
|
|
||||||
* * cdw0
|
|
||||||
* * status (except for phase)
|
|
||||||
*
|
|
||||||
* Remaining fields will be set to 0 by the driver.
|
|
||||||
*/
|
|
||||||
struct nvme_completion cpl;
|
|
||||||
|
|
||||||
/* buf is the data buffer associated with this passthrough command. */
|
|
||||||
void * buf;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* len is the length of the data buffer associated with this
|
|
||||||
* passthrough command.
|
|
||||||
*/
|
|
||||||
uint32_t len;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* is_read = 1 if the passthrough command will read data into the
|
|
||||||
* supplied buffer from the controller.
|
|
||||||
*
|
|
||||||
* is_read = 0 if the passthrough command will write data from the
|
|
||||||
* supplied buffer to the controller.
|
|
||||||
*/
|
|
||||||
uint32_t is_read;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* driver_lock is used by the driver only. It must be set to 0
|
|
||||||
* by the caller.
|
|
||||||
*/
|
|
||||||
struct mtx * driver_lock;
|
|
||||||
};
|
|
||||||
#else
|
|
||||||
#include <dev/nvme/nvme.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define nvme_completion_is_error(cpl) \
|
|
||||||
((cpl)->status.sc != 0 || (cpl)->status.sct != 0)
|
|
||||||
|
|
||||||
#define NVME_CTRLR_PREFIX "/dev/nvme"
|
|
||||||
#define NVME_NS_PREFIX "ns"
|
|
@ -1,21 +0,0 @@
|
|||||||
#ifndef SG_CMDS_H
|
|
||||||
#define SG_CMDS_H
|
|
||||||
|
|
||||||
/********************************************************************
|
|
||||||
* This header did contain wrapper declarations for many SCSI commands
|
|
||||||
* up until sg3_utils version 1.22 . In that version, the command
|
|
||||||
* wrappers were broken into two groups, the 'basic' ones found in the
|
|
||||||
* "sg_cmds_basic.h" header and the 'extra' ones found in the
|
|
||||||
* "sg_cmds_extra.h" header. This header now simply includes those two
|
|
||||||
* headers.
|
|
||||||
* In sg3_utils version 1.26 the sg_cmds_mmc.h header was added and
|
|
||||||
* contains some MMC specific commands.
|
|
||||||
* The corresponding function definitions are found in the sg_cmds_basic.c,
|
|
||||||
* sg_cmds_extra.c and sg_cmds_mmc.c files.
|
|
||||||
********************************************************************/
|
|
||||||
|
|
||||||
#include "sg_cmds_basic.h"
|
|
||||||
#include "sg_cmds_extra.h"
|
|
||||||
#include "sg_cmds_mmc.h"
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,310 +0,0 @@
|
|||||||
#ifndef SG_CMDS_BASIC_H
|
|
||||||
#define SG_CMDS_BASIC_H
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (c) 2004-2017 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Error, warning and verbose output is sent to the file pointed to by
|
|
||||||
* sg_warnings_strm which is declared in sg_lib.h and can be set with
|
|
||||||
* the sg_set_warnings_strm() function. If not given sg_warnings_strm
|
|
||||||
* defaults to stderr.
|
|
||||||
* If 'noisy' is false and 'verbose' is zero then following functions should
|
|
||||||
* not output anything to sg_warnings_strm. If 'noisy' is true and
|
|
||||||
* 'verbose' is zero then Unit Attention, Recovered, Medium and Hardware
|
|
||||||
* errors (sense keys) send output to sg_warnings_strm. Increasing values
|
|
||||||
* of 'verbose' send increasing amounts of (debug) output to
|
|
||||||
* sg_warnings_strm.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/* Invokes a SCSI INQUIRY command and yields the response
|
|
||||||
* Returns 0 when successful, SG_LIB_CAT_INVALID_OP -> not supported,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
|
|
||||||
* SG_LIB_CAT_ABORTED_COMMAND, -1 -> other errors */
|
|
||||||
int sg_ll_inquiry(int sg_fd, bool cmddt, bool evpd, int pg_op, void * resp,
|
|
||||||
int mx_resp_len, bool noisy, int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI INQUIRY command and yields the response. Returns 0 when
|
|
||||||
* successful, various SG_LIB_CAT_* positive values or -1 -> other errors.
|
|
||||||
* The CMDDT field is obsolete in the INQUIRY cdb (since spc3r16 in 2003) so
|
|
||||||
* an argument to set it has been removed (use the REPORT SUPPORTED OPERATION
|
|
||||||
* CODES command instead). Adds the ability to set the command abort timeout
|
|
||||||
* and the ability to report the residual count. If timeout_secs is zero
|
|
||||||
* or less the default command abort timeout (60 seconds) is used.
|
|
||||||
* If residp is non-NULL then the residual value is written where residp
|
|
||||||
* points. A residual value of 0 implies mx_resp_len bytes have be written
|
|
||||||
* where resp points. If the residual value equals mx_resp_len then no
|
|
||||||
* bytes have been written. */
|
|
||||||
int
|
|
||||||
sg_ll_inquiry_v2(int sg_fd, bool evpd, int pg_op, void * resp,
|
|
||||||
int mx_resp_len, int timeout_secs, int * residp,
|
|
||||||
bool noisy, int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI LOG SELECT command. Return of 0 -> success,
|
|
||||||
* SG_LIB_CAT_INVALID_OP -> Log Select not supported,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
|
||||||
* SG_LIB_CAT_ABORTED_COMMAND, * SG_LIB_CAT_NOT_READY -> device not ready,
|
|
||||||
* -1 -> other failure */
|
|
||||||
int sg_ll_log_select(int sg_fd, bool pcr, bool sp, int pc, int pg_code,
|
|
||||||
int subpg_code, unsigned char * paramp, int param_len,
|
|
||||||
bool noisy, int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI LOG SENSE command. Return of 0 -> success,
|
|
||||||
* SG_LIB_CAT_INVALID_OP -> Log Sense not supported,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
|
||||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* -1 -> other failure */
|
|
||||||
int sg_ll_log_sense(int sg_fd, bool ppc, bool sp, int pc, int pg_code,
|
|
||||||
int subpg_code, int paramp, unsigned char * resp,
|
|
||||||
int mx_resp_len, bool noisy, int verbose);
|
|
||||||
|
|
||||||
/* Same as sg_ll_log_sense() apart from timeout_secs and residp. See
|
|
||||||
* sg_ll_inquiry_v2() for their description */
|
|
||||||
int sg_ll_log_sense_v2(int sg_fd, bool ppc, bool sp, int pc, int pg_code,
|
|
||||||
int subpg_code, int paramp, unsigned char * resp,
|
|
||||||
int mx_resp_len, int timeout_secs, int * residp,
|
|
||||||
bool noisy, int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI MODE SELECT (6) command. Return of 0 -> success,
|
|
||||||
* SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ ->
|
|
||||||
* bad field in cdb, * SG_LIB_CAT_NOT_READY -> device not ready,
|
|
||||||
* SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* -1 -> other failure */
|
|
||||||
int sg_ll_mode_select6(int sg_fd, bool pf, bool sp, void * paramp,
|
|
||||||
int param_len, bool noisy, int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI MODE SELECT (10) command. Return of 0 -> success,
|
|
||||||
* SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ ->
|
|
||||||
* bad field in cdb, * SG_LIB_CAT_NOT_READY -> device not ready,
|
|
||||||
* SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* -1 -> other failure */
|
|
||||||
int sg_ll_mode_select10(int sg_fd, bool pf, bool sp, void * paramp,
|
|
||||||
int param_len, bool noisy, int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI MODE SENSE (6) command. Return of 0 -> success,
|
|
||||||
* SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ ->
|
|
||||||
* bad field in cdb, * SG_LIB_CAT_NOT_READY -> device not ready,
|
|
||||||
* SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* -1 -> other failure */
|
|
||||||
int sg_ll_mode_sense6(int sg_fd, bool dbd, int pc, int pg_code,
|
|
||||||
int sub_pg_code, void * resp, int mx_resp_len,
|
|
||||||
bool noisy, int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI MODE SENSE (10) command. Return of 0 -> success,
|
|
||||||
* SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ ->
|
|
||||||
* bad field in cdb, * SG_LIB_CAT_NOT_READY -> device not ready,
|
|
||||||
* SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* -1 -> other failure */
|
|
||||||
int sg_ll_mode_sense10(int sg_fd, bool llbaa, bool dbd, int pc, int pg_code,
|
|
||||||
int sub_pg_code, void * resp, int mx_resp_len,
|
|
||||||
bool noisy, int verbose);
|
|
||||||
|
|
||||||
/* Same as sg_ll_mode_sense10() apart from timeout_secs and residp. See
|
|
||||||
* sg_ll_inquiry_v2() for their description */
|
|
||||||
int sg_ll_mode_sense10_v2(int sg_fd, bool llbaa, bool dbd, int pc,
|
|
||||||
int pg_code, int sub_pg_code, void * resp,
|
|
||||||
int mx_resp_len, int timeout_secs, int * residp,
|
|
||||||
bool noisy, int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI PREVENT ALLOW MEDIUM REMOVAL command (SPC-3)
|
|
||||||
* prevent==0 allows removal, prevent==1 prevents removal ...
|
|
||||||
* Return of 0 -> success,
|
|
||||||
* SG_LIB_CAT_INVALID_OP -> command not supported
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
|
||||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* -1 -> other failure */
|
|
||||||
int sg_ll_prevent_allow(int sg_fd, int prevent, bool noisy, int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI READ CAPACITY (10) command. Return of 0 -> success,
|
|
||||||
* SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_UNIT_ATTENTION
|
|
||||||
* -> perhaps media changed, SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
|
|
||||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* -1 -> other failure */
|
|
||||||
int sg_ll_readcap_10(int sg_fd, bool pmi, unsigned int lba, void * resp,
|
|
||||||
int mx_resp_len, bool noisy, int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI READ CAPACITY (16) command. Returns 0 -> success,
|
|
||||||
* SG_LIB_CAT_UNIT_ATTENTION -> media changed??, SG_LIB_CAT_INVALID_OP
|
|
||||||
* -> cdb not supported, SG_LIB_CAT_IlLEGAL_REQ -> bad field in cdb
|
|
||||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* -1 -> other failure */
|
|
||||||
int sg_ll_readcap_16(int sg_fd, bool pmi, uint64_t llba, void * resp,
|
|
||||||
int mx_resp_len, bool noisy, int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI REPORT LUNS command. Return of 0 -> success,
|
|
||||||
* SG_LIB_CAT_INVALID_OP -> Report Luns not supported,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* SG_LIB_NOT_READY (shouldn't happen), -1 -> other failure */
|
|
||||||
int sg_ll_report_luns(int sg_fd, int select_report, void * resp,
|
|
||||||
int mx_resp_len, bool noisy, int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI REQUEST SENSE command. Return of 0 -> success,
|
|
||||||
* SG_LIB_CAT_INVALID_OP -> Request Sense not supported??,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* -1 -> other failure */
|
|
||||||
int sg_ll_request_sense(int sg_fd, bool desc, void * resp, int mx_resp_len,
|
|
||||||
bool noisy, int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI START STOP UNIT command (SBC + MMC).
|
|
||||||
* Return of 0 -> success,
|
|
||||||
* SG_LIB_CAT_INVALID_OP -> Start stop unit not supported,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
|
||||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* -1 -> other failure
|
|
||||||
* SBC-3 and MMC partially overlap on the power_condition_modifier(sbc) and
|
|
||||||
* format_layer_number(mmc) fields. They also overlap on the noflush(sbc)
|
|
||||||
* and fl(mmc) one bit field. This is the cause of the awkardly named
|
|
||||||
* pc_mod__fl_num and noflush__fl arguments to this function. */
|
|
||||||
int sg_ll_start_stop_unit(int sg_fd, bool immed, int pc_mod__fl_num,
|
|
||||||
int power_cond, bool noflush__fl, bool loej,
|
|
||||||
bool start, bool noisy, int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI SYNCHRONIZE CACHE (10) command. Return of 0 -> success,
|
|
||||||
* SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* SG_LIB_CAT_INVALID_OP -> cdb not supported,
|
|
||||||
* SG_LIB_CAT_IlLEGAL_REQ -> bad field in cdb
|
|
||||||
* SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
|
|
||||||
int sg_ll_sync_cache_10(int sg_fd, bool sync_nv, bool immed, int group,
|
|
||||||
unsigned int lba, unsigned int count, bool noisy,
|
|
||||||
int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI TEST UNIT READY command.
|
|
||||||
* 'pack_id' is just for diagnostics, safe to set to 0.
|
|
||||||
* Return of 0 -> success, SG_LIB_CAT_UNIT_ATTENTION,
|
|
||||||
* SG_LIB_CAT_NOT_READY -> device not ready,
|
|
||||||
* SG_LIB_CAT_ABORTED_COMMAND, -1 -> other failure */
|
|
||||||
int sg_ll_test_unit_ready(int sg_fd, int pack_id, bool noisy, int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI TEST UNIT READY command.
|
|
||||||
* 'pack_id' is just for diagnostics, safe to set to 0.
|
|
||||||
* Looks for progress indicator if 'progress' non-NULL;
|
|
||||||
* if found writes value [0..65535] else write -1.
|
|
||||||
* Return of 0 -> success, SG_LIB_CAT_UNIT_ATTENTION,
|
|
||||||
* SG_LIB_CAT_ABORTED_COMMAND, SG_LIB_CAT_NOT_READY ->
|
|
||||||
* device not ready, -1 -> other failure */
|
|
||||||
int sg_ll_test_unit_ready_progress(int sg_fd, int pack_id, int * progress,
|
|
||||||
bool noisy, int verbose);
|
|
||||||
|
|
||||||
|
|
||||||
struct sg_simple_inquiry_resp {
|
|
||||||
unsigned char peripheral_qualifier;
|
|
||||||
unsigned char peripheral_type;
|
|
||||||
unsigned char byte_1; /* was 'rmb' prior to version 1.39 */
|
|
||||||
/* now rmb == !!(0x80 & byte_1) */
|
|
||||||
unsigned char version; /* as per recent drafts: whole of byte 2 */
|
|
||||||
unsigned char byte_3;
|
|
||||||
unsigned char byte_5;
|
|
||||||
unsigned char byte_6;
|
|
||||||
unsigned char byte_7;
|
|
||||||
char vendor[9]; /* T10 field is 8 bytes, NUL char appended */
|
|
||||||
char product[17];
|
|
||||||
char revision[5];
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Yields most of first 36 bytes of a standard INQUIRY (evpd==0) response.
|
|
||||||
* Returns 0 when successful, SG_LIB_CAT_INVALID_OP -> not supported,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* -1 -> other errors */
|
|
||||||
int sg_simple_inquiry(int sg_fd, struct sg_simple_inquiry_resp * inq_data,
|
|
||||||
bool noisy, int verbose);
|
|
||||||
|
|
||||||
/* MODE SENSE commands yield a response that has header then zero or more
|
|
||||||
* block descriptors followed by mode pages. In most cases users are
|
|
||||||
* interested in the first mode page. This function returns the (byte)
|
|
||||||
* offset of the start of the first mode page. Set mode_sense_6 to true for
|
|
||||||
* MODE SENSE (6) and false for MODE SENSE (10). Returns >= 0 is successful
|
|
||||||
* or -1 if failure. If there is a failure a message is written to err_buff
|
|
||||||
* if it is non-NULL and err_buff_len > 0. */
|
|
||||||
int sg_mode_page_offset(const unsigned char * resp, int resp_len,
|
|
||||||
bool mode_sense_6, char * err_buff, int err_buff_len);
|
|
||||||
|
|
||||||
/* MODE SENSE commands yield a response that has header then zero or more
|
|
||||||
* block descriptors followed by mode pages. This functions returns the
|
|
||||||
* length (in bytes) of those three components. Note that the return value
|
|
||||||
* can exceed resp_len in which case the MODE SENSE command should be
|
|
||||||
* re-issued with a larger response buffer. If bd_lenp is non-NULL and if
|
|
||||||
* successful the block descriptor length (in bytes) is written to *bd_lenp.
|
|
||||||
* Set mode_sense_6 to true for MODE SENSE (6) and false for MODE SENSE (10)
|
|
||||||
* responses. Returns -1 if there is an error (e.g. response too short). */
|
|
||||||
int sg_msense_calc_length(const unsigned char * resp, int resp_len,
|
|
||||||
bool mode_sense_6, int * bd_lenp);
|
|
||||||
|
|
||||||
/* Fetches current, changeable, default and/or saveable modes pages as
|
|
||||||
* indicated by pcontrol_arr for given pg_code and sub_pg_code. If
|
|
||||||
* mode6==0 then use MODE SENSE (10) else use MODE SENSE (6). If
|
|
||||||
* flexible set and mode data length seems wrong then try and
|
|
||||||
* fix (compensating hack for bad device or driver). pcontrol_arr
|
|
||||||
* should have 4 elements for output of current, changeable, default
|
|
||||||
* and saved values respectively. Each element should be NULL or
|
|
||||||
* at least mx_mpage_len bytes long.
|
|
||||||
* Return of 0 -> overall success, SG_LIB_CAT_INVALID_OP -> invalid opcode,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
|
||||||
* SG_LIB_CAT_NOT_READY -> device not ready,
|
|
||||||
* SG_LIB_CAT_MALFORMED -> bad response, -1 -> other failure.
|
|
||||||
* If success_mask pointer is not NULL then first zeros it. Then set bits
|
|
||||||
* 0, 1, 2 and/or 3 if the current, changeable, default and saved values
|
|
||||||
* respectively have been fetched. If error on current page
|
|
||||||
* then stops and returns that error; otherwise continues if an error is
|
|
||||||
* detected but returns the first error encountered. */
|
|
||||||
int sg_get_mode_page_controls(int sg_fd, bool mode6, int pg_code,
|
|
||||||
int sub_pg_code, bool dbd, bool flexible,
|
|
||||||
int mx_mpage_len, int * success_mask,
|
|
||||||
void * pcontrol_arr[], int * reported_lenp,
|
|
||||||
int verbose);
|
|
||||||
|
|
||||||
/* Returns file descriptor >= 0 if successful. If error in Unix returns
|
|
||||||
negated errno. Implementation calls scsi_pt_open_device(). */
|
|
||||||
int sg_cmds_open_device(const char * device_name, bool read_only, int verbose);
|
|
||||||
|
|
||||||
/* Returns file descriptor >= 0 if successful. If error in Unix returns
|
|
||||||
negated errno. Implementation calls scsi_pt_open_flags(). */
|
|
||||||
int sg_cmds_open_flags(const char * device_name, int flags, int verbose);
|
|
||||||
|
|
||||||
/* Returns 0 if successful. If error in Unix returns negated errno.
|
|
||||||
Implementation calls scsi_pt_close_device(). */
|
|
||||||
int sg_cmds_close_device(int device_fd);
|
|
||||||
|
|
||||||
const char * sg_cmds_version();
|
|
||||||
|
|
||||||
#define SG_NO_DATA_IN 0
|
|
||||||
|
|
||||||
struct sg_pt_base;
|
|
||||||
|
|
||||||
/* This is a helper function used by sg_cmds_* implementations after the
|
|
||||||
* call to the pass-through. pt_res is returned from do_scsi_pt(). If valid
|
|
||||||
* sense data is found it is decoded and output to sg_warnings_strm (def:
|
|
||||||
* stderr); depending on the 'noisy' and 'verbose' settings. Returns -2 for
|
|
||||||
* sense data (may not be fatal), -1 for failed, 0, or a positive number. If
|
|
||||||
* 'mx_di_len > 0' then asks pass-through for resid and returns
|
|
||||||
* (mx_di_len - resid); otherwise returns 0. So for data-in it should return
|
|
||||||
* the actual number of bytes received. For data-out (to device) or no data
|
|
||||||
* call with 'mx_di_len' set to 0 or less. If -2 returned then sense category
|
|
||||||
* output via 'o_sense_cat' pointer (if not NULL). Note that several sense
|
|
||||||
* categories also have data in bytes received; -2 is still returned. */
|
|
||||||
int sg_cmds_process_resp(struct sg_pt_base * ptvp, const char * leadin,
|
|
||||||
int pt_res, int mx_di_len,
|
|
||||||
const unsigned char * sense_b, bool noisy,
|
|
||||||
int verbose, int * o_sense_cat);
|
|
||||||
|
|
||||||
/* NVMe devices use a different command set. This function will return true
|
|
||||||
* if the device associated with 'pvtp' is a NVME device, else it will
|
|
||||||
* return false (e.g. for SCSI devices). */
|
|
||||||
bool sg_cmds_is_nvme(const struct sg_pt_base * ptvp);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,369 +0,0 @@
|
|||||||
#ifndef SG_CMDS_EXTRA_H
|
|
||||||
#define SG_CMDS_EXTRA_H
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (c) 2004-2018 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Note: all functions that have an 'int timeout_secs' argument will use
|
|
||||||
* that value if it is > 0. Otherwise they will set an internal default
|
|
||||||
* which is currently 60 seconds. This timeout is typically applied in the
|
|
||||||
* SCSI stack above the initiator. If it goes off then the SCSI command is
|
|
||||||
* aborted and there can be other unwelcome side effects. Note that some
|
|
||||||
* commands (e.g. FORMAT UNIT and the Third Party copy commands) can take
|
|
||||||
* a lot longer than the default timeout. */
|
|
||||||
|
|
||||||
|
|
||||||
/* Invokes a ATA PASS-THROUGH (12, 16 or 32) SCSI command (SAT). This is
|
|
||||||
* selected by the cdb_len argument that can take values of 12, 16 or 32
|
|
||||||
* only (else -1 is returned). The byte at offset 0 (and bytes 0 to 9
|
|
||||||
* inclusive for ATA PT(32)) pointed to be cdbp are ignored and apart from
|
|
||||||
* the control byte, the rest is copied into an internal cdb which is then
|
|
||||||
* sent to the device. The control byte is byte 11 for ATA PT(12), byte 15
|
|
||||||
* for ATA PT(16) and byte 1 for ATA PT(32). If timeout_secs <= 0 then the
|
|
||||||
* timeout is set to 60 seconds. For data in or out transfers set dinp or
|
|
||||||
* doutp, and dlen to the number of bytes to transfer. If dlen is zero then
|
|
||||||
* no data transfer is assumed. If sense buffer obtained then it is written
|
|
||||||
* to sensep, else sensep[0] is set to 0x0. If ATA return descriptor is
|
|
||||||
* obtained then written to ata_return_dp, else ata_return_dp[0] is set to
|
|
||||||
* 0x0. Either sensep or ata_return_dp (or both) may be NULL pointers.
|
|
||||||
* Returns SCSI status value (>= 0) or -1 if other error. Users are
|
|
||||||
* expected to check the sense buffer themselves. If available the data in
|
|
||||||
* resid is written to residp. Note in SAT-2 and later, fixed format sense
|
|
||||||
* data may be placed in *sensep in which case sensep[0]==0x70, prior to
|
|
||||||
* SAT-2 descriptor sense format was required (i.e. sensep[0]==0x72).
|
|
||||||
*/
|
|
||||||
int sg_ll_ata_pt(int sg_fd, const unsigned char * cdbp, int cdb_len,
|
|
||||||
int timeout_secs, void * dinp, void * doutp, int dlen,
|
|
||||||
unsigned char * sensep, int max_sense_len,
|
|
||||||
unsigned char * ata_return_dp, int max_ata_return_len,
|
|
||||||
int * residp, int verbose);
|
|
||||||
|
|
||||||
/* Invokes a FORMAT UNIT (SBC-3) command. Return of 0 -> success,
|
|
||||||
* SG_LIB_CAT_INVALID_OP -> Format unit not supported,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
|
||||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* -1 -> other failure. Note that sg_ll_format_unit2() and
|
|
||||||
* sg_ll_format_unit_v2() are the same, both add the ffmt argument. */
|
|
||||||
int sg_ll_format_unit(int sg_fd, int fmtpinfo, bool longlist, bool fmtdata,
|
|
||||||
bool cmplist, int dlist_format, int timeout_secs,
|
|
||||||
void * paramp, int param_len, bool noisy, int verbose);
|
|
||||||
int sg_ll_format_unit2(int sg_fd, int fmtpinfo, bool longlist, bool fmtdata,
|
|
||||||
bool cmplist, int dlist_format, int ffmt,
|
|
||||||
int timeout_secs, void * paramp, int param_len,
|
|
||||||
bool noisy, int verbose);
|
|
||||||
int sg_ll_format_unit_v2(int sg_fd, int fmtpinfo, bool longlist, bool fmtdata,
|
|
||||||
bool cmplist, int dlist_format, int ffmt,
|
|
||||||
int timeout_secs, void * paramp, int param_len,
|
|
||||||
bool noisy, int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI GET LBA STATUS(16) or GET LBA STATUS(32) command (SBC).
|
|
||||||
* Returns 0 -> success,
|
|
||||||
* SG_LIB_CAT_INVALID_OP -> GET LBA STATUS(16 or 32) not supported,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure.
|
|
||||||
* sg_ll_get_lba_status() calls the 16 byte variant with rt=0 . */
|
|
||||||
int sg_ll_get_lba_status(int sg_fd, uint64_t start_llba, void * resp,
|
|
||||||
int alloc_len, bool noisy, int verbose);
|
|
||||||
int sg_ll_get_lba_status16(int sg_fd, uint64_t start_llba, uint8_t rt,
|
|
||||||
void * resp, int alloc_len, bool noisy,
|
|
||||||
int verbose);
|
|
||||||
int sg_ll_get_lba_status32(int sg_fd, uint64_t start_llba, uint32_t scan_len,
|
|
||||||
uint32_t element_id, uint8_t rt,
|
|
||||||
void * resp, int alloc_len, bool noisy,
|
|
||||||
int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI PERSISTENT RESERVE IN command (SPC). Returns 0
|
|
||||||
* when successful, SG_LIB_CAT_INVALID_OP if command not supported,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported,
|
|
||||||
* SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, else -1 */
|
|
||||||
int sg_ll_persistent_reserve_in(int sg_fd, int rq_servact, void * resp,
|
|
||||||
int mx_resp_len, bool noisy, int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI PERSISTENT RESERVE OUT command (SPC). Returns 0
|
|
||||||
* when successful, SG_LIB_CAT_INVALID_OP if command not supported,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported,
|
|
||||||
* SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, else -1 */
|
|
||||||
int sg_ll_persistent_reserve_out(int sg_fd, int rq_servact, int rq_scope,
|
|
||||||
unsigned int rq_type, void * paramp,
|
|
||||||
int param_len, bool noisy, int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI READ BLOCK LIMITS command. Return of 0 -> success,
|
|
||||||
* SG_LIB_CAT_INVALID_OP -> READ BLOCK LIMITS not supported,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* SG_LIB_NOT_READY (shouldn't happen), -1 -> other failure */
|
|
||||||
int sg_ll_read_block_limits(int sg_fd, void * resp, int mx_resp_len,
|
|
||||||
bool noisy, int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI READ BUFFER command (SPC). Return of 0 ->
|
|
||||||
* success, SG_LIB_CAT_INVALID_OP -> invalid opcode,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
|
||||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* -1 -> other failure */
|
|
||||||
int sg_ll_read_buffer(int sg_fd, int mode, int buffer_id, int buffer_offset,
|
|
||||||
void * resp, int mx_resp_len, bool noisy, int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI READ DEFECT DATA (10) command (SBC). Return of 0 ->
|
|
||||||
* success, SG_LIB_CAT_INVALID_OP -> invalid opcode,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
|
||||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* -1 -> other failure */
|
|
||||||
int sg_ll_read_defect10(int sg_fd, bool req_plist, bool req_glist,
|
|
||||||
int dl_format, void * resp, int mx_resp_len,
|
|
||||||
bool noisy, int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI READ LONG (10) command (SBC). Note that 'xfer_len'
|
|
||||||
* is in bytes. Returns 0 -> success,
|
|
||||||
* SG_LIB_CAT_INVALID_OP -> READ LONG(10) not supported,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO -> bad field in cdb, with info
|
|
||||||
* field written to 'offsetp', SG_LIB_CAT_UNIT_ATTENTION,
|
|
||||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* -1 -> other failure */
|
|
||||||
int sg_ll_read_long10(int sg_fd, bool pblock, bool correct, unsigned int lba,
|
|
||||||
void * resp, int xfer_len, int * offsetp, bool noisy,
|
|
||||||
int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI READ LONG (16) command (SBC). Note that 'xfer_len'
|
|
||||||
* is in bytes. Returns 0 -> success,
|
|
||||||
* SG_LIB_CAT_INVALID_OP -> READ LONG(16) not supported,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO -> bad field in cdb, with info
|
|
||||||
* field written to 'offsetp', SG_LIB_CAT_UNIT_ATTENTION,
|
|
||||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* -1 -> other failure */
|
|
||||||
int sg_ll_read_long16(int sg_fd, bool pblock, bool correct, uint64_t llba,
|
|
||||||
void * resp, int xfer_len, int * offsetp, bool noisy,
|
|
||||||
int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI READ MEDIA SERIAL NUMBER command. Return of 0 -> success,
|
|
||||||
* SG_LIB_CAT_INVALID_OP -> Read media serial number not supported,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
|
||||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* -1 -> other failure */
|
|
||||||
int sg_ll_read_media_serial_num(int sg_fd, void * resp, int mx_resp_len,
|
|
||||||
bool noisy, int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI REASSIGN BLOCKS command. Return of 0 -> success,
|
|
||||||
* SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_UNIT_ATTENTION,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
|
|
||||||
int sg_ll_reassign_blocks(int sg_fd, bool longlba, bool longlist,
|
|
||||||
void * paramp, int param_len, bool noisy,
|
|
||||||
int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI RECEIVE DIAGNOSTIC RESULTS command. Return of 0 -> success,
|
|
||||||
* SG_LIB_CAT_INVALID_OP -> Receive diagnostic results not supported,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
|
||||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* -1 -> other failure */
|
|
||||||
int sg_ll_receive_diag(int sg_fd, bool pcv, int pg_code, void * resp,
|
|
||||||
int mx_resp_len, bool noisy, int verbose);
|
|
||||||
|
|
||||||
/* Same as sg_ll_receive_diag() but with added timeout_secs and residp
|
|
||||||
* arguments. Adds the ability to set the command abort timeout
|
|
||||||
* and the ability to report the residual count. If timeout_secs is zero
|
|
||||||
* or less the default command abort timeout (60 seconds) is used.
|
|
||||||
* If residp is non-NULL then the residual value is written where residp
|
|
||||||
* points. A residual value of 0 implies mx_resp_len bytes have be written
|
|
||||||
* where resp points. If the residual value equals mx_resp_len then no
|
|
||||||
* bytes have been written. */
|
|
||||||
int sg_ll_receive_diag_v2(int sg_fd, bool pcv, int pg_code, void * resp,
|
|
||||||
int mx_resp_len, int timeout_secs, int * residp,
|
|
||||||
bool noisy, int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI REPORT IDENTIFYING INFORMATION command. This command was
|
|
||||||
* called REPORT DEVICE IDENTIFIER prior to spc4r07. Return of 0 -> success,
|
|
||||||
* SG_LIB_CAT_INVALID_OP -> Report identifying information not supported,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
|
||||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* -1 -> other failure */
|
|
||||||
int sg_ll_report_id_info(int sg_fd, int itype, void * resp, int max_resp_len,
|
|
||||||
bool noisy, int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI REPORT TARGET PORT GROUPS command. Return of 0 -> success,
|
|
||||||
* SG_LIB_CAT_INVALID_OP -> Report Target Port Groups not supported,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* SG_LIB_CAT_UNIT_ATTENTION, -1 -> other failure */
|
|
||||||
int sg_ll_report_tgt_prt_grp(int sg_fd, void * resp, int mx_resp_len,
|
|
||||||
bool noisy, int verbose);
|
|
||||||
int sg_ll_report_tgt_prt_grp2(int sg_fd, void * resp, int mx_resp_len,
|
|
||||||
bool extended, bool noisy, int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI SET TARGET PORT GROUPS command. Return of 0 -> success,
|
|
||||||
* SG_LIB_CAT_INVALID_OP -> Report Target Port Groups not supported,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* SG_LIB_CAT_UNIT_ATTENTION, -1 -> other failure */
|
|
||||||
int sg_ll_set_tgt_prt_grp(int sg_fd, void * paramp, int param_len, bool noisy,
|
|
||||||
int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI REPORT REFERRALS command. Return of 0 -> success,
|
|
||||||
* SG_LIB_CAT_INVALID_OP -> Report Referrals not supported,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* SG_LIB_CAT_UNIT_ATTENTION, -1 -> other failure */
|
|
||||||
int sg_ll_report_referrals(int sg_fd, uint64_t start_llba, bool one_seg,
|
|
||||||
void * resp, int mx_resp_len, bool noisy,
|
|
||||||
int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI SEND DIAGNOSTIC command. Foreground, extended self tests can
|
|
||||||
* take a long time, if so set long_duration flag in which case the timeout
|
|
||||||
* is set to 7200 seconds; if the value of long_duration is > 7200 then that
|
|
||||||
* value is taken as the timeout value in seconds. Return of 0 -> success,
|
|
||||||
* SG_LIB_CAT_INVALID_OP -> Send diagnostic not supported,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
|
||||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* -1 -> other failure */
|
|
||||||
int sg_ll_send_diag(int sg_fd, int st_code, bool pf_bit, bool st_bit,
|
|
||||||
bool devofl_bit, bool unitofl_bit, int long_duration,
|
|
||||||
void * paramp, int param_len, bool noisy, int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI SET IDENTIFYING INFORMATION command. This command was
|
|
||||||
* called SET DEVICE IDENTIFIER prior to spc4r07. Return of 0 -> success,
|
|
||||||
* SG_LIB_CAT_INVALID_OP -> Set identifying information not supported,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
|
||||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* -1 -> other failure */
|
|
||||||
int sg_ll_set_id_info(int sg_fd, int itype, void * paramp, int param_len,
|
|
||||||
bool noisy, int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI UNMAP (SBC-3) command. Return of 0 -> success,
|
|
||||||
* SG_LIB_CAT_INVALID_OP -> command not supported,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* SG_LIB_CAT_UNIT_ATTENTION, -1 -> other failure */
|
|
||||||
int sg_ll_unmap(int sg_fd, int group_num, int timeout_secs, void * paramp,
|
|
||||||
int param_len, bool noisy, int verbose);
|
|
||||||
/* Invokes a SCSI UNMAP (SBC-3) command. Version 2 adds anchor field
|
|
||||||
* (sbc3r22). Otherwise same as sg_ll_unmap() . */
|
|
||||||
int sg_ll_unmap_v2(int sg_fd, bool anchor, int group_num, int timeout_secs,
|
|
||||||
void * paramp, int param_len, bool noisy, int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI VERIFY (10) command (SBC and MMC).
|
|
||||||
* Note that 'veri_len' is in blocks while 'data_out_len' is in bytes.
|
|
||||||
* Returns of 0 -> success,
|
|
||||||
* SG_LIB_CAT_INVALID_OP -> Verify(10) not supported,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
|
||||||
* SG_LIB_CAT_MEDIUM_HARD -> medium or hardware error, no valid info,
|
|
||||||
* SG_LIB_CAT_MEDIUM_HARD_WITH_INFO -> as previous, with valid info,
|
|
||||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* SG_LIB_CAT_MISCOMPARE, -1 -> other failure */
|
|
||||||
int sg_ll_verify10(int sg_fd, int vrprotect, bool dpo, int bytechk,
|
|
||||||
unsigned int lba, int veri_len, void * data_out,
|
|
||||||
int data_out_len, unsigned int * infop, bool noisy,
|
|
||||||
int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI VERIFY (16) command (SBC).
|
|
||||||
* Note that 'veri_len' is in blocks while 'data_out_len' is in bytes.
|
|
||||||
* Returns of 0 -> success,
|
|
||||||
* SG_LIB_CAT_INVALID_OP -> Verify(16) not supported,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
|
||||||
* SG_LIB_CAT_MEDIUM_HARD -> medium or hardware error, no valid info,
|
|
||||||
* SG_LIB_CAT_MEDIUM_HARD_WITH_INFO -> as previous, with valid info,
|
|
||||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* SG_LIB_CAT_MISCOMPARE, -1 -> other failure */
|
|
||||||
int sg_ll_verify16(int sg_fd, int vrprotect, bool dpo, int bytechk,
|
|
||||||
uint64_t llba, int veri_len, int group_num,
|
|
||||||
void * data_out, int data_out_len, uint64_t * infop,
|
|
||||||
bool noisy, int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI WRITE BUFFER command (SPC). Return of 0 ->
|
|
||||||
* success, SG_LIB_CAT_INVALID_OP -> invalid opcode,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
|
||||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* -1 -> other failure */
|
|
||||||
int sg_ll_write_buffer(int sg_fd, int mode, int buffer_id, int buffer_offset,
|
|
||||||
void * paramp, int param_len, bool noisy, int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI WRITE BUFFER command (SPC). Return of 0 ->
|
|
||||||
* success, SG_LIB_CAT_INVALID_OP -> invalid opcode,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
|
||||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* -1 -> other failure. Adds mode specific field (spc4r32) and timeout
|
|
||||||
* to command abort to override default of 60 seconds. If timeout_secs is
|
|
||||||
* 0 or less then the default timeout is used instead. */
|
|
||||||
int
|
|
||||||
sg_ll_write_buffer_v2(int sg_fd, int mode, int m_specific, int buffer_id,
|
|
||||||
uint32_t buffer_offset, void * paramp,
|
|
||||||
uint32_t param_len, int timeout_secs, bool noisy,
|
|
||||||
int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI WRITE LONG (10) command (SBC). Note that 'xfer_len'
|
|
||||||
* is in bytes. Returns 0 -> success,
|
|
||||||
* SG_LIB_CAT_INVALID_OP -> WRITE LONG(10) not supported,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO -> bad field in cdb, with info
|
|
||||||
* field written to 'offsetp', SG_LIB_CAT_UNIT_ATTENTION,
|
|
||||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* -1 -> other failure */
|
|
||||||
int sg_ll_write_long10(int sg_fd, bool cor_dis, bool wr_uncor, bool pblock,
|
|
||||||
unsigned int lba, void * data_out, int xfer_len,
|
|
||||||
int * offsetp, bool noisy, int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI WRITE LONG (16) command (SBC). Note that 'xfer_len'
|
|
||||||
* is in bytes. Returns 0 -> success,
|
|
||||||
* SG_LIB_CAT_INVALID_OP -> WRITE LONG(16) not supported,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO -> bad field in cdb, with info
|
|
||||||
* field written to 'offsetp', SG_LIB_CAT_UNIT_ATTENTION,
|
|
||||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* -1 -> other failure */
|
|
||||||
int sg_ll_write_long16(int sg_fd, bool cor_dis, bool wr_uncor, bool pblock,
|
|
||||||
uint64_t llba, void * data_out, int xfer_len,
|
|
||||||
int * offsetp, bool noisy, int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SPC-3 SCSI RECEIVE COPY RESULTS command. In SPC-4 this function
|
|
||||||
* supports all service action variants of the THIRD-PARTY COPY IN opcode.
|
|
||||||
* SG_LIB_CAT_INVALID_OP -> Receive copy results not supported,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
|
||||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* -1 -> other failure */
|
|
||||||
int sg_ll_receive_copy_results(int sg_fd, int sa, int list_id, void * resp,
|
|
||||||
int mx_resp_len, bool noisy, int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI EXTENDED COPY(LID1) command. For EXTENDED COPY(LID4)
|
|
||||||
* including POPULATE TOKEN and WRITE USING TOKEN use
|
|
||||||
* sg_ll_3party_copy_out(). Return of 0 -> success,
|
|
||||||
* SG_LIB_CAT_INVALID_OP -> Extended copy not supported,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
|
||||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* -1 -> other failure */
|
|
||||||
int sg_ll_extended_copy(int sg_fd, void * paramp, int param_len, bool noisy,
|
|
||||||
int verbose);
|
|
||||||
|
|
||||||
/* Handles various service actions associated with opcode 0x83 which is
|
|
||||||
* called THIRD PARTY COPY OUT. These include the EXTENDED COPY(LID4),
|
|
||||||
* POPULATE TOKEN and WRITE USING TOKEN commands. Return of 0 -> success,
|
|
||||||
* SG_LIB_CAT_INVALID_OP -> opcode 0x83 not supported,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
|
||||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* -1 -> other failure */
|
|
||||||
int sg_ll_3party_copy_out(int sg_fd, int sa, unsigned int list_id,
|
|
||||||
int group_num, int timeout_secs, void * paramp,
|
|
||||||
int param_len, bool noisy, int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI PRE-FETCH(10), PRE-FETCH(16) or SEEK(10) command (SBC).
|
|
||||||
* Returns 0 -> success, 25 (SG_LIB_CAT_CONDITION_MET), various SG_LIB_CAT_*
|
|
||||||
* positive values or -1 -> other errors. Note that CONDITION MET status
|
|
||||||
* is returned when immed=true and num_blocks can fit in device's cache,
|
|
||||||
* somewaht strangely, GOOD status (return 0) is returned if num_blocks
|
|
||||||
* cannot fit in device's cache. If do_seek10==true then does a SEEK(10)
|
|
||||||
* command with given lba, if that LBA is < 2**32 . Unclear what SEEK(10)
|
|
||||||
* does, assume it is like PRE-FETCH. If timeout_secs is 0 (or less) then
|
|
||||||
* use DEF_PT_TIMEOUT (60 seconds) as command timeout. */
|
|
||||||
int sg_ll_pre_fetch_x(int sg_fd, bool do_seek10, bool cdb16, bool immed,
|
|
||||||
uint64_t lba, uint32_t num_blocks, int group_num,
|
|
||||||
int timeout_secs, bool noisy, int verbose);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,52 +0,0 @@
|
|||||||
#ifndef SG_CMDS_MMC_H
|
|
||||||
#define SG_CMDS_MMC_H
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (c) 2008-2017 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/* Invokes a SCSI GET CONFIGURATION command (MMC-3...6).
|
|
||||||
* Returns 0 when successful, SG_LIB_CAT_INVALID_OP if command not
|
|
||||||
* supported, SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported,
|
|
||||||
* SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, else -1 */
|
|
||||||
int sg_ll_get_config(int sg_fd, int rt, int starting, void * resp,
|
|
||||||
int mx_resp_len, bool noisy, int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI GET PERFORMANCE command (MMC-3...6).
|
|
||||||
* Returns 0 when successful, SG_LIB_CAT_INVALID_OP if command not
|
|
||||||
* supported, SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported,
|
|
||||||
* SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, else -1 */
|
|
||||||
int sg_ll_get_performance(int sg_fd, int data_type, unsigned int starting_lba,
|
|
||||||
int max_num_desc, int type, void * resp,
|
|
||||||
int mx_resp_len, bool noisy, int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI SET CD SPEED command (MMC).
|
|
||||||
* Return of 0 -> success, SG_LIB_CAT_INVALID_OP -> command not supported,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
|
||||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* -1 -> other failure */
|
|
||||||
int sg_ll_set_cd_speed(int sg_fd, int rot_control, int drv_read_speed,
|
|
||||||
int drv_write_speed, bool noisy, int verbose);
|
|
||||||
|
|
||||||
/* Invokes a SCSI SET STREAMING command (MMC). Return of 0 -> success,
|
|
||||||
* SG_LIB_CAT_INVALID_OP -> Set Streaming not supported,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_NOT_READY -> device not ready,
|
|
||||||
* -1 -> other failure */
|
|
||||||
int sg_ll_set_streaming(int sg_fd, int type, void * paramp, int param_len,
|
|
||||||
bool noisy, int verbose);
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,185 +0,0 @@
|
|||||||
#ifndef SG_IO_LINUX_H
|
|
||||||
#define SG_IO_LINUX_H
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (c) 2004-2017 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Version 1.05 [20171009]
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This header file contains linux specific information related to the SCSI
|
|
||||||
* command pass through in the SCSI generic (sg) driver and the linux
|
|
||||||
* block layer.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "sg_lib.h"
|
|
||||||
#include "sg_linux_inc.h"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* The following are 'host_status' codes */
|
|
||||||
#ifndef DID_OK
|
|
||||||
#define DID_OK 0x00
|
|
||||||
#endif
|
|
||||||
#ifndef DID_NO_CONNECT
|
|
||||||
#define DID_NO_CONNECT 0x01 /* Unable to connect before timeout */
|
|
||||||
#define DID_BUS_BUSY 0x02 /* Bus remain busy until timeout */
|
|
||||||
#define DID_TIME_OUT 0x03 /* Timed out for some other reason */
|
|
||||||
#define DID_BAD_TARGET 0x04 /* Bad target (id?) */
|
|
||||||
#define DID_ABORT 0x05 /* Told to abort for some other reason */
|
|
||||||
#define DID_PARITY 0x06 /* Parity error (on SCSI bus) */
|
|
||||||
#define DID_ERROR 0x07 /* Internal error */
|
|
||||||
#define DID_RESET 0x08 /* Reset by somebody */
|
|
||||||
#define DID_BAD_INTR 0x09 /* Received an unexpected interrupt */
|
|
||||||
#define DID_PASSTHROUGH 0x0a /* Force command past mid-level */
|
|
||||||
#define DID_SOFT_ERROR 0x0b /* The low-level driver wants a retry */
|
|
||||||
#endif
|
|
||||||
#ifndef DID_IMM_RETRY
|
|
||||||
#define DID_IMM_RETRY 0x0c /* Retry without decrementing retry count */
|
|
||||||
#endif
|
|
||||||
#ifndef DID_REQUEUE
|
|
||||||
#define DID_REQUEUE 0x0d /* Requeue command (no immediate retry) also
|
|
||||||
* without decrementing the retry count */
|
|
||||||
#endif
|
|
||||||
#ifndef DID_TRANSPORT_DISRUPTED
|
|
||||||
#define DID_TRANSPORT_DISRUPTED 0xe
|
|
||||||
#endif
|
|
||||||
#ifndef DID_TRANSPORT_FAILFAST
|
|
||||||
#define DID_TRANSPORT_FAILFAST 0xf
|
|
||||||
#endif
|
|
||||||
#ifndef DID_TARGET_FAILURE
|
|
||||||
#define DID_TARGET_FAILURE 0x10
|
|
||||||
#endif
|
|
||||||
#ifndef DID_NEXUS_FAILURE
|
|
||||||
#define DID_NEXUS_FAILURE 0x11
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* These defines are to isolate applications from kernel define changes */
|
|
||||||
#define SG_LIB_DID_OK DID_OK
|
|
||||||
#define SG_LIB_DID_NO_CONNECT DID_NO_CONNECT
|
|
||||||
#define SG_LIB_DID_BUS_BUSY DID_BUS_BUSY
|
|
||||||
#define SG_LIB_DID_TIME_OUT DID_TIME_OUT
|
|
||||||
#define SG_LIB_DID_BAD_TARGET DID_BAD_TARGET
|
|
||||||
#define SG_LIB_DID_ABORT DID_ABORT
|
|
||||||
#define SG_LIB_DID_PARITY DID_PARITY
|
|
||||||
#define SG_LIB_DID_ERROR DID_ERROR
|
|
||||||
#define SG_LIB_DID_RESET DID_RESET
|
|
||||||
#define SG_LIB_DID_BAD_INTR DID_BAD_INTR
|
|
||||||
#define SG_LIB_DID_PASSTHROUGH DID_PASSTHROUGH
|
|
||||||
#define SG_LIB_DID_SOFT_ERROR DID_SOFT_ERROR
|
|
||||||
#define SG_LIB_DID_IMM_RETRY DID_IMM_RETRY
|
|
||||||
#define SG_LIB_DID_REQUEUE DID_REQUEUE
|
|
||||||
#define SG_LIB_TRANSPORT_DISRUPTED DID_TRANSPORT_DISRUPTED
|
|
||||||
#define SG_LIB_DID_TRANSPORT_FAILFAST DID_TRANSPORT_FAILFAST
|
|
||||||
#define SG_LIB_DID_TARGET_FAILURE DID_TARGET_FAILURE
|
|
||||||
#define SG_LIB_DID_NEXUS_FAILURE DID_NEXUS_FAILURE
|
|
||||||
|
|
||||||
/* The following are 'driver_status' codes */
|
|
||||||
#ifndef DRIVER_OK
|
|
||||||
#define DRIVER_OK 0x00
|
|
||||||
#endif
|
|
||||||
#ifndef DRIVER_BUSY
|
|
||||||
#define DRIVER_BUSY 0x01
|
|
||||||
#define DRIVER_SOFT 0x02
|
|
||||||
#define DRIVER_MEDIA 0x03
|
|
||||||
#define DRIVER_ERROR 0x04
|
|
||||||
#define DRIVER_INVALID 0x05
|
|
||||||
#define DRIVER_TIMEOUT 0x06
|
|
||||||
#define DRIVER_HARD 0x07
|
|
||||||
#define DRIVER_SENSE 0x08 /* Sense_buffer has been set */
|
|
||||||
|
|
||||||
/* N.B. the SUGGEST_* codes are no longer used in Linux and are only kept
|
|
||||||
* to stop compilation breakages.
|
|
||||||
* Following "suggests" are "or-ed" with one of previous 8 entries */
|
|
||||||
#define SUGGEST_RETRY 0x10
|
|
||||||
#define SUGGEST_ABORT 0x20
|
|
||||||
#define SUGGEST_REMAP 0x30
|
|
||||||
#define SUGGEST_DIE 0x40
|
|
||||||
#define SUGGEST_SENSE 0x80
|
|
||||||
#define SUGGEST_IS_OK 0xff
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef DRIVER_MASK
|
|
||||||
#define DRIVER_MASK 0x0f
|
|
||||||
#endif
|
|
||||||
#ifndef SUGGEST_MASK
|
|
||||||
#define SUGGEST_MASK 0xf0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* These defines are to isolate applications from kernel define changes */
|
|
||||||
#define SG_LIB_DRIVER_OK DRIVER_OK
|
|
||||||
#define SG_LIB_DRIVER_BUSY DRIVER_BUSY
|
|
||||||
#define SG_LIB_DRIVER_SOFT DRIVER_SOFT
|
|
||||||
#define SG_LIB_DRIVER_MEDIA DRIVER_MEDIA
|
|
||||||
#define SG_LIB_DRIVER_ERROR DRIVER_ERROR
|
|
||||||
#define SG_LIB_DRIVER_INVALID DRIVER_INVALID
|
|
||||||
#define SG_LIB_DRIVER_TIMEOUT DRIVER_TIMEOUT
|
|
||||||
#define SG_LIB_DRIVER_HARD DRIVER_HARD
|
|
||||||
#define SG_LIB_DRIVER_SENSE DRIVER_SENSE
|
|
||||||
|
|
||||||
|
|
||||||
/* N.B. the SUGGEST_* codes are no longer used in Linux and are only kept
|
|
||||||
* to stop compilation breakages. */
|
|
||||||
#define SG_LIB_SUGGEST_RETRY SUGGEST_RETRY
|
|
||||||
#define SG_LIB_SUGGEST_ABORT SUGGEST_ABORT
|
|
||||||
#define SG_LIB_SUGGEST_REMAP SUGGEST_REMAP
|
|
||||||
#define SG_LIB_SUGGEST_DIE SUGGEST_DIE
|
|
||||||
#define SG_LIB_SUGGEST_SENSE SUGGEST_SENSE
|
|
||||||
#define SG_LIB_SUGGEST_IS_OK SUGGEST_IS_OK
|
|
||||||
#define SG_LIB_DRIVER_MASK DRIVER_MASK
|
|
||||||
#define SG_LIB_SUGGEST_MASK SUGGEST_MASK
|
|
||||||
|
|
||||||
void sg_print_masked_status(int masked_status);
|
|
||||||
void sg_print_host_status(int host_status);
|
|
||||||
void sg_print_driver_status(int driver_status);
|
|
||||||
|
|
||||||
/* sg_chk_n_print() returns 1 quietly if there are no errors/warnings
|
|
||||||
else it prints errors/warnings (prefixed by 'leadin') to
|
|
||||||
'sg_warnings_fd' and returns 0. raw_sinfo indicates whether the
|
|
||||||
raw sense buffer (in ASCII hex) should be printed. */
|
|
||||||
int sg_chk_n_print(const char * leadin, int masked_status, int host_status,
|
|
||||||
int driver_status, const unsigned char * sense_buffer,
|
|
||||||
int sb_len, bool raw_sinfo);
|
|
||||||
|
|
||||||
/* The following function declaration is for the sg version 3 driver. */
|
|
||||||
struct sg_io_hdr;
|
|
||||||
/* sg_chk_n_print3() returns 1 quietly if there are no errors/warnings;
|
|
||||||
else it prints errors/warnings (prefixed by 'leadin') to
|
|
||||||
'sg_warnings_fd' and returns 0. */
|
|
||||||
int sg_chk_n_print3(const char * leadin, struct sg_io_hdr * hp,
|
|
||||||
bool raw_sinfo);
|
|
||||||
|
|
||||||
/* Calls sg_scsi_normalize_sense() after obtaining the sense buffer and
|
|
||||||
its length from the struct sg_io_hdr pointer. If these cannot be
|
|
||||||
obtained, false is returned. */
|
|
||||||
bool sg_normalize_sense(const struct sg_io_hdr * hp,
|
|
||||||
struct sg_scsi_sense_hdr * sshp);
|
|
||||||
|
|
||||||
int sg_err_category(int masked_status, int host_status, int driver_status,
|
|
||||||
const unsigned char * sense_buffer, int sb_len);
|
|
||||||
|
|
||||||
int sg_err_category_new(int scsi_status, int host_status, int driver_status,
|
|
||||||
const unsigned char * sense_buffer, int sb_len);
|
|
||||||
|
|
||||||
/* The following function declaration is for the sg version 3 driver. */
|
|
||||||
int sg_err_category3(struct sg_io_hdr * hp);
|
|
||||||
|
|
||||||
|
|
||||||
/* Note about SCSI status codes found in older versions of Linux.
|
|
||||||
Linux has traditionally used a 1 bit right shifted and masked
|
|
||||||
version of SCSI standard status codes. Now CHECK_CONDITION
|
|
||||||
and friends (in <scsi/scsi.h>) are deprecated. */
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,602 +0,0 @@
|
|||||||
#ifndef SG_LIB_H
|
|
||||||
#define SG_LIB_H
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (c) 2004-2018 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
*
|
|
||||||
* On 5th October 2004 a FreeBSD license was added to this file.
|
|
||||||
* The intention is to keep this file and the related sg_lib.c file
|
|
||||||
* as open source and encourage their unencumbered use.
|
|
||||||
*
|
|
||||||
* Current version number is in the sg_lib.c file and can be accessed
|
|
||||||
* with the sg_lib_version() function.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This header file contains defines and function declarations that may
|
|
||||||
* be useful to applications that communicate with devices that use a
|
|
||||||
* SCSI command set. These command sets have names like SPC-4, SBC-3,
|
|
||||||
* SSC-3, SES-2 and draft standards defining them can be found at
|
|
||||||
* http://www.t10.org . Virtually all devices in the Linux SCSI subsystem
|
|
||||||
* utilize SCSI command sets. Many devices in other Linux device subsystems
|
|
||||||
* utilize SCSI command sets either natively or via emulation (e.g. a
|
|
||||||
* parallel ATA disk in a USB enclosure).
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* SCSI Peripheral Device Types (PDT) [5 bit field] */
|
|
||||||
#define PDT_DISK 0x0 /* direct access block device (disk) */
|
|
||||||
#define PDT_TAPE 0x1 /* sequential access device (magnetic tape) */
|
|
||||||
#define PDT_PRINTER 0x2 /* printer device (see SSC-1) */
|
|
||||||
#define PDT_PROCESSOR 0x3 /* processor device (e.g. SAFTE device) */
|
|
||||||
#define PDT_WO 0x4 /* write once device (some optical disks) */
|
|
||||||
#define PDT_MMC 0x5 /* CD/DVD/BD (multi-media) */
|
|
||||||
#define PDT_SCANNER 0x6 /* obsolete */
|
|
||||||
#define PDT_OPTICAL 0x7 /* optical memory device (some optical disks) */
|
|
||||||
#define PDT_MCHANGER 0x8 /* media changer device (e.g. tape robot) */
|
|
||||||
#define PDT_COMMS 0x9 /* communications device (obsolete) */
|
|
||||||
#define PDT_SAC 0xc /* storage array controller device */
|
|
||||||
#define PDT_SES 0xd /* SCSI Enclosure Services (SES) device */
|
|
||||||
#define PDT_RBC 0xe /* Reduced Block Commands (simplified PDT_DISK) */
|
|
||||||
#define PDT_OCRW 0xf /* optical card read/write device */
|
|
||||||
#define PDT_BCC 0x10 /* bridge controller commands */
|
|
||||||
#define PDT_OSD 0x11 /* Object Storage Device (OSD) */
|
|
||||||
#define PDT_ADC 0x12 /* Automation/drive commands (ADC) */
|
|
||||||
#define PDT_SMD 0x13 /* Security Manager Device (SMD) */
|
|
||||||
#define PDT_ZBC 0x14 /* Zoned Block Commands (ZBC) */
|
|
||||||
#define PDT_WLUN 0x1e /* Well known logical unit (WLUN) */
|
|
||||||
#define PDT_UNKNOWN 0x1f /* Unknown or no device type */
|
|
||||||
|
|
||||||
#ifndef SAM_STAT_GOOD
|
|
||||||
/* The SCSI status codes as found in SAM-4 at www.t10.org */
|
|
||||||
#define SAM_STAT_GOOD 0x0
|
|
||||||
#define SAM_STAT_CHECK_CONDITION 0x2
|
|
||||||
#define SAM_STAT_CONDITION_MET 0x4
|
|
||||||
#define SAM_STAT_BUSY 0x8
|
|
||||||
#define SAM_STAT_INTERMEDIATE 0x10 /* obsolete in SAM-4 */
|
|
||||||
#define SAM_STAT_INTERMEDIATE_CONDITION_MET 0x14 /* obsolete in SAM-4 */
|
|
||||||
#define SAM_STAT_RESERVATION_CONFLICT 0x18
|
|
||||||
#define SAM_STAT_COMMAND_TERMINATED 0x22 /* obsolete in SAM-3 */
|
|
||||||
#define SAM_STAT_TASK_SET_FULL 0x28
|
|
||||||
#define SAM_STAT_ACA_ACTIVE 0x30
|
|
||||||
#define SAM_STAT_TASK_ABORTED 0x40
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* The SCSI sense key codes as found in SPC-4 at www.t10.org */
|
|
||||||
#define SPC_SK_NO_SENSE 0x0
|
|
||||||
#define SPC_SK_RECOVERED_ERROR 0x1
|
|
||||||
#define SPC_SK_NOT_READY 0x2
|
|
||||||
#define SPC_SK_MEDIUM_ERROR 0x3
|
|
||||||
#define SPC_SK_HARDWARE_ERROR 0x4
|
|
||||||
#define SPC_SK_ILLEGAL_REQUEST 0x5
|
|
||||||
#define SPC_SK_UNIT_ATTENTION 0x6
|
|
||||||
#define SPC_SK_DATA_PROTECT 0x7
|
|
||||||
#define SPC_SK_BLANK_CHECK 0x8
|
|
||||||
#define SPC_SK_VENDOR_SPECIFIC 0x9
|
|
||||||
#define SPC_SK_COPY_ABORTED 0xa
|
|
||||||
#define SPC_SK_ABORTED_COMMAND 0xb
|
|
||||||
#define SPC_SK_RESERVED 0xc
|
|
||||||
#define SPC_SK_VOLUME_OVERFLOW 0xd
|
|
||||||
#define SPC_SK_MISCOMPARE 0xe
|
|
||||||
#define SPC_SK_COMPLETED 0xf
|
|
||||||
|
|
||||||
/* Transport protocol identifiers or just Protocol identifiers */
|
|
||||||
#define TPROTO_FCP 0
|
|
||||||
#define TPROTO_SPI 1
|
|
||||||
#define TPROTO_SSA 2
|
|
||||||
#define TPROTO_1394 3
|
|
||||||
#define TPROTO_SRP 4 /* SCSI over RDMA */
|
|
||||||
#define TPROTO_ISCSI 5
|
|
||||||
#define TPROTO_SAS 6
|
|
||||||
#define TPROTO_ADT 7
|
|
||||||
#define TPROTO_ATA 8
|
|
||||||
#define TPROTO_UAS 9 /* USB attached SCSI */
|
|
||||||
#define TPROTO_SOP 0xa /* SCSI over PCIe */
|
|
||||||
#define TPROTO_PCIE 0xb /* includes NVMe */
|
|
||||||
#define TPROTO_NONE 0xf
|
|
||||||
|
|
||||||
/* SCSI Feature Sets (sfs) */
|
|
||||||
#define SCSI_FS_SPC_DISCOVERY_2016 0x1
|
|
||||||
#define SCSI_FS_SBC_BASE_2010 0x102
|
|
||||||
#define SCSI_FS_SBC_BASE_2016 0x101
|
|
||||||
#define SCSI_FS_SBC_BASIC_PROV_2016 0x103
|
|
||||||
#define SCSI_FS_SBC_DRIVE_MAINT_2016 0x104
|
|
||||||
|
|
||||||
/* Often SCSI responses use the highest integer that can fit in a field
|
|
||||||
* to indicate "unbounded" or limit does not apply. Sometimes represented
|
|
||||||
* in output as "-1" for brevity */
|
|
||||||
#define SG_LIB_UNBOUNDED_16BIT 0xffff
|
|
||||||
#define SG_LIB_UNBOUNDED_32BIT 0xffffffffU
|
|
||||||
#define SG_LIB_UNBOUNDED_64BIT 0xffffffffffffffffULL
|
|
||||||
|
|
||||||
#if (__STDC_VERSION__ >= 199901L) /* C99 or later */
|
|
||||||
typedef uintptr_t sg_uintptr_t;
|
|
||||||
#else
|
|
||||||
typedef unsigned long sg_uintptr_t;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/* The format of the version string is like this: "2.26 20170906" */
|
|
||||||
const char * sg_lib_version();
|
|
||||||
|
|
||||||
/* Returns length of SCSI command given the opcode (first byte).
|
|
||||||
* Yields the wrong answer for variable length commands (opcode=0x7f)
|
|
||||||
* and potentially some vendor specific commands. */
|
|
||||||
int sg_get_command_size(unsigned char cdb_byte0);
|
|
||||||
|
|
||||||
/* Command name given pointer to the cdb. Certain command names
|
|
||||||
* depend on peripheral type (give 0 or -1 if unknown). Places command
|
|
||||||
* name into buff and will write no more than buff_len bytes. */
|
|
||||||
void sg_get_command_name(const unsigned char * cdbp, int peri_type,
|
|
||||||
int buff_len, char * buff);
|
|
||||||
|
|
||||||
/* Command name given only the first byte (byte 0) of a cdb and
|
|
||||||
* peripheral type (give 0 or -1 if unknown). */
|
|
||||||
void sg_get_opcode_name(unsigned char cdb_byte0, int peri_type, int buff_len,
|
|
||||||
char * buff);
|
|
||||||
|
|
||||||
/* Command name given opcode (byte 0), service action and peripheral type.
|
|
||||||
* If no service action give 0, if unknown peripheral type give 0 or -1 . */
|
|
||||||
void sg_get_opcode_sa_name(unsigned char cdb_byte0, int service_action,
|
|
||||||
int peri_type, int buff_len, char * buff);
|
|
||||||
|
|
||||||
/* Fetch scsi status string. */
|
|
||||||
void sg_get_scsi_status_str(int scsi_status, int buff_len, char * buff);
|
|
||||||
|
|
||||||
/* This is a slightly stretched SCSI sense "descriptor" format header.
|
|
||||||
* The addition is to allow the 0x70 and 0x71 response codes. The idea
|
|
||||||
* is to place the salient data of both "fixed" and "descriptor" sense
|
|
||||||
* format into one structure to ease application processing.
|
|
||||||
* The original sense buffer should be kept around for those cases
|
|
||||||
* in which more information is required (e.g. the LBA of a MEDIUM ERROR). */
|
|
||||||
struct sg_scsi_sense_hdr {
|
|
||||||
unsigned char response_code; /* permit: 0x0, 0x70, 0x71, 0x72, 0x73 */
|
|
||||||
unsigned char sense_key;
|
|
||||||
unsigned char asc;
|
|
||||||
unsigned char ascq;
|
|
||||||
unsigned char byte4;
|
|
||||||
unsigned char byte5;
|
|
||||||
unsigned char byte6;
|
|
||||||
unsigned char additional_length;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Maps the salient data from a sense buffer which is in either fixed or
|
|
||||||
* descriptor format into a structure mimicking a descriptor format
|
|
||||||
* header (i.e. the first 8 bytes of sense descriptor format).
|
|
||||||
* If zero response code returns false. Otherwise returns true and if 'sshp'
|
|
||||||
* is non-NULL then zero all fields and then set the appropriate fields in
|
|
||||||
* that structure. sshp::additional_length is always 0 for response
|
|
||||||
* codes 0x70 and 0x71 (fixed format). */
|
|
||||||
bool sg_scsi_normalize_sense(const unsigned char * sensep, int sense_len,
|
|
||||||
struct sg_scsi_sense_hdr * sshp);
|
|
||||||
|
|
||||||
/* Attempt to find the first SCSI sense data descriptor that matches the
|
|
||||||
* given 'desc_type'. If found return pointer to start of sense data
|
|
||||||
* descriptor; otherwise (including fixed format sense data) returns NULL. */
|
|
||||||
const unsigned char * sg_scsi_sense_desc_find(const unsigned char * sensep,
|
|
||||||
int sense_len, int desc_type);
|
|
||||||
|
|
||||||
/* Get sense key from sense buffer. If successful returns a sense key value
|
|
||||||
* between 0 and 15. If sense buffer cannot be decode, returns -1 . */
|
|
||||||
int sg_get_sense_key(const unsigned char * sensep, int sense_len);
|
|
||||||
|
|
||||||
/* Yield string associated with sense_key value. Returns 'buff'. */
|
|
||||||
char * sg_get_sense_key_str(int sense_key, int buff_len, char * buff);
|
|
||||||
|
|
||||||
/* Yield string associated with ASC/ASCQ values. Returns 'buff'. */
|
|
||||||
char * sg_get_asc_ascq_str(int asc, int ascq, int buff_len, char * buff);
|
|
||||||
|
|
||||||
/* Returns true if valid bit set, false if valid bit clear. Irrespective the
|
|
||||||
* information field is written out via 'info_outp' (except when it is
|
|
||||||
* NULL). Handles both fixed and descriptor sense formats. */
|
|
||||||
bool sg_get_sense_info_fld(const unsigned char * sensep, int sb_len,
|
|
||||||
uint64_t * info_outp);
|
|
||||||
|
|
||||||
/* Returns true if fixed format or command specific information descriptor
|
|
||||||
* is found in the descriptor sense; else false. If available the command
|
|
||||||
* specific information field (4 byte integer in fixed format, 8 byte
|
|
||||||
* integer in descriptor format) is written out via 'cmd_spec_outp'.
|
|
||||||
* Handles both fixed and descriptor sense formats. */
|
|
||||||
bool sg_get_sense_cmd_spec_fld(const unsigned char * sensep, int sb_len,
|
|
||||||
uint64_t * cmd_spec_outp);
|
|
||||||
|
|
||||||
/* Returns true if any of the 3 bits (i.e. FILEMARK, EOM or ILI) are set.
|
|
||||||
* In descriptor format if the stream commands descriptor not found
|
|
||||||
* then returns false. Writes true or false corresponding to these bits to
|
|
||||||
* the last three arguments if they are non-NULL. */
|
|
||||||
bool sg_get_sense_filemark_eom_ili(const unsigned char * sensep, int sb_len,
|
|
||||||
bool * filemark_p, bool * eom_p,
|
|
||||||
bool * ili_p);
|
|
||||||
|
|
||||||
/* Returns true if SKSV is set and sense key is NO_SENSE or NOT_READY. Also
|
|
||||||
* returns true if progress indication sense data descriptor found. Places
|
|
||||||
* progress field from sense data where progress_outp points. If progress
|
|
||||||
* field is not available returns false. Handles both fixed and descriptor
|
|
||||||
* sense formats. N.B. App should multiply by 100 and divide by 65536
|
|
||||||
* to get percentage completion from given value. */
|
|
||||||
bool sg_get_sense_progress_fld(const unsigned char * sensep, int sb_len,
|
|
||||||
int * progress_outp);
|
|
||||||
|
|
||||||
/* Closely related to sg_print_sense(). Puts decoded sense data in 'buff'.
|
|
||||||
* Usually multiline with multiple '\n' including one trailing. If
|
|
||||||
* 'raw_sinfo' set appends sense buffer in hex. 'leadin' is string prepended
|
|
||||||
* to each line written to 'buff', NULL treated as "". Returns the number of
|
|
||||||
* bytes written to 'buff' excluding the trailing '\0'.
|
|
||||||
* N.B. prior to sg3_utils v 1.42 'leadin' was only prepended to the first
|
|
||||||
* line output. Also this function returned type void. */
|
|
||||||
int sg_get_sense_str(const char * leadin, const unsigned char * sense_buffer,
|
|
||||||
int sb_len, bool raw_sinfo, int buff_len, char * buff);
|
|
||||||
|
|
||||||
/* Decode descriptor format sense descriptors (assumes sense buffer is
|
|
||||||
* in descriptor format). 'leadin' is string prepended to each line written
|
|
||||||
* to 'b', NULL treated as "". Returns the number of bytes written to 'b'
|
|
||||||
* excluding the trailing '\0'. */
|
|
||||||
int sg_get_sense_descriptors_str(const char * leadin,
|
|
||||||
const unsigned char * sense_buffer,
|
|
||||||
int sb_len, int blen, char * b);
|
|
||||||
|
|
||||||
/* Decodes a designation descriptor (e.g. as found in the Device
|
|
||||||
* Identification VPD page (0x83)) into string 'b' whose maximum length is
|
|
||||||
* blen. 'leadin' is string prepended to each line written to 'b', NULL
|
|
||||||
* treated as "". Returns the number of bytes written to 'b' excluding the
|
|
||||||
* trailing '\0'. */
|
|
||||||
int sg_get_designation_descriptor_str(const char * leadin,
|
|
||||||
const unsigned char * ddp, int dd_len,
|
|
||||||
bool print_assoc, bool do_long,
|
|
||||||
int blen, char * b);
|
|
||||||
|
|
||||||
/* Yield string associated with peripheral device type (pdt). Returns
|
|
||||||
* 'buff'. If 'pdt' out of range yields "bad pdt" string. */
|
|
||||||
char * sg_get_pdt_str(int pdt, int buff_len, char * buff);
|
|
||||||
|
|
||||||
/* Some lesser used PDTs share a lot in common with a more used PDT.
|
|
||||||
* Examples are PDT_ADC decaying to PDT_TAPE and PDT_ZBC to PDT_DISK.
|
|
||||||
* If such a lesser used 'pdt' is given to this function, then it will
|
|
||||||
* return the more used PDT (i.e. "decays to"); otherwise 'pdt' is returned.
|
|
||||||
* Valid for 'pdt' 0 to 31, for other values returns 0. */
|
|
||||||
int sg_lib_pdt_decay(int pdt);
|
|
||||||
|
|
||||||
/* Yield string associated with transport protocol identifier (tpi). Returns
|
|
||||||
* 'buff'. If 'tpi' out of range yields "bad tpi" string. */
|
|
||||||
char * sg_get_trans_proto_str(int tpi, int buff_len, char * buff);
|
|
||||||
|
|
||||||
/* Decode TransportID pointed to by 'bp' of length 'bplen'. Place decoded
|
|
||||||
* string output in 'buff' which is also the return value. Each new line
|
|
||||||
* is prefixed by 'leadin'. If leadin NULL treat as "". */
|
|
||||||
char * sg_decode_transportid_str(const char * leadin, unsigned char * bp,
|
|
||||||
int bplen, bool only_one, int buff_len,
|
|
||||||
char * buff);
|
|
||||||
|
|
||||||
/* Returns a designator's type string given 'val' (0 to 15 inclusive),
|
|
||||||
* otherwise returns NULL. */
|
|
||||||
const char * sg_get_desig_type_str(int val);
|
|
||||||
|
|
||||||
/* Returns a designator's code_set string given 'val' (0 to 15 inclusive),
|
|
||||||
* otherwise returns NULL. */
|
|
||||||
const char * sg_get_desig_code_set_str(int val);
|
|
||||||
|
|
||||||
/* Returns a designator's association string given 'val' (0 to 3 inclusive),
|
|
||||||
* otherwise returns NULL. */
|
|
||||||
const char * sg_get_desig_assoc_str(int val);
|
|
||||||
|
|
||||||
/* Yield SCSI Feature Set (sfs) string. When 'peri_type' is < -1 (or > 31)
|
|
||||||
* returns pointer to string (same as 'buff') associated with 'sfs_code'.
|
|
||||||
* When 'peri_type' is between -1 (for SPC) and 31 (inclusive) then a match
|
|
||||||
* on both 'sfs_code' and 'peri_type' is required. If 'foundp' is not NULL
|
|
||||||
* then where it points is set to true if a match is found else it is set to
|
|
||||||
* false. If 'buff' is not NULL then in the case of a match a descriptive
|
|
||||||
* string is written to 'buff' while if there is not a not then a string
|
|
||||||
* ending in "Reserved" is written (and may be prefixed with SPC, SBC, SSC
|
|
||||||
* or ZBC). Returns 'buff' (i.e. a pointer value) even if it is NULL.
|
|
||||||
* Example:
|
|
||||||
* char b[64];
|
|
||||||
* ...
|
|
||||||
* printf("%s\n", sg_get_sfs_str(sfs_code, -2, sizeof(b), b, NULL, 0));
|
|
||||||
*/
|
|
||||||
const char * sg_get_sfs_str(uint16_t sfs_code, int peri_type, int buff_len,
|
|
||||||
char * buff, bool * foundp, int verbose);
|
|
||||||
|
|
||||||
/* This is a heuristic that takes into account the command bytes and length
|
|
||||||
* to decide whether the presented unstructured sequence of bytes could be
|
|
||||||
* a SCSI command. If so it returns true otherwise false. Vendor specific
|
|
||||||
* SCSI commands (i.e. opcodes from 0xc0 to 0xff), if presented, are assumed
|
|
||||||
* to follow SCSI conventions (i.e. length of 6, 10, 12 or 16 bytes). The
|
|
||||||
* only SCSI commands considered above 16 bytes of length are the Variable
|
|
||||||
* Length Commands (opcode 0x7f) and the XCDB wrapped commands (opcode 0x7e).
|
|
||||||
* Both have an inbuilt length field which can be cross checked with clen.
|
|
||||||
* No NVMe commands (64 bytes long plus some extra added by some OSes) have
|
|
||||||
* opcodes 0x7e or 0x7f yet. ATA is register based but SATA has FIS
|
|
||||||
* structures that are sent across the wire. The 'FIS register' structure is
|
|
||||||
* used to move a command from a SATA host to device, but the ATA 'command'
|
|
||||||
* is not the first byte. So it is harder to say what will happen if a
|
|
||||||
* FIS structure is presented as a SCSI command, hopfully there is a low
|
|
||||||
* probability this function will yield true in that case. */
|
|
||||||
bool sg_is_scsi_cdb(const uint8_t * cdbp, int clen);
|
|
||||||
|
|
||||||
/* Yield string associated with NVMe command status value in sct_sc. It
|
|
||||||
* expects to decode DW3 bits 27:17 from the completion queue. Bits 27:25
|
|
||||||
* are the Status Code Type (SCT) and bits 24:17 are the Status Code (SC).
|
|
||||||
* Bit 17 in DW3 should be bit 0 in sct_sc. If no status string is found
|
|
||||||
* a string of the form "Reserved [0x<sct_sc_in_hex>]" is generated.
|
|
||||||
* Returns 'buff'. Does nothing if buff_len<=0 or if buff is NULL.*/
|
|
||||||
char * sg_get_nvme_cmd_status_str(uint16_t sct_sc, int buff_len, char * buff);
|
|
||||||
|
|
||||||
/* Attempts to map NVMe status value ((SCT << 8) | SC) n sct_sc to a SCSI
|
|
||||||
* status, sense_key, asc and ascq tuple. If successful returns true and
|
|
||||||
* writes to non-NULL pointer arguments; otherwise returns false. */
|
|
||||||
bool sg_nvme_status2scsi(uint16_t sct_sc, uint8_t * status_p, uint8_t * sk_p,
|
|
||||||
uint8_t * asc_p, uint8_t * ascq_p);
|
|
||||||
|
|
||||||
extern FILE * sg_warnings_strm;
|
|
||||||
|
|
||||||
void sg_set_warnings_strm(FILE * warnings_strm);
|
|
||||||
|
|
||||||
/* The following "print" functions send ACSII to 'sg_warnings_strm' file
|
|
||||||
* descriptor (default value is stderr). 'leadin' is string prepended to
|
|
||||||
* each line printed out, NULL treated as "". */
|
|
||||||
void sg_print_command(const unsigned char * command);
|
|
||||||
void sg_print_scsi_status(int scsi_status);
|
|
||||||
|
|
||||||
/* 'leadin' is string prepended to each line printed out, NULL treated as
|
|
||||||
* "". N.B. prior to sg3_utils v 1.42 'leadin' was only prepended to the
|
|
||||||
* first line printed. */
|
|
||||||
void sg_print_sense(const char * leadin, const unsigned char * sense_buffer,
|
|
||||||
int sb_len, bool raw_info);
|
|
||||||
|
|
||||||
/* Following examines exit_status and outputs a clear error message to
|
|
||||||
* warnings_strm (usually stderr) if one is known and returns true.
|
|
||||||
* Otherwise it doesn't print anything and returns false. Note that if
|
|
||||||
* exit_status==0 then returns true but prints nothing and if
|
|
||||||
* exit_status<0 ("some error occurred") false is returned. If leadin is
|
|
||||||
* non-NULL is will be printed before error message. */
|
|
||||||
bool sg_if_can2stderr(const char * leadin, int exit_status);
|
|
||||||
|
|
||||||
/* Utilities can use these exit status values for syntax errors and
|
|
||||||
* file (device node) problems (e.g. not found or permissions). */
|
|
||||||
#define SG_LIB_SYNTAX_ERROR 1 /* command line syntax problem */
|
|
||||||
#define SG_LIB_FILE_ERROR 15 /* device or other file problem */
|
|
||||||
|
|
||||||
/* The sg_err_category_sense() function returns one of the following.
|
|
||||||
* These may be used as exit status values (from a process). Notice that
|
|
||||||
* some of the lower values correspond to SCSI sense key values. */
|
|
||||||
#define SG_LIB_CAT_CLEAN 0 /* No errors or other information */
|
|
||||||
/* Value 1 left unused for utilities to use SG_LIB_SYNTAX_ERROR */
|
|
||||||
#define SG_LIB_CAT_NOT_READY 2 /* sense key, unit stopped? */
|
|
||||||
/* [sk,asc,ascq: 0x2,*,*] */
|
|
||||||
#define SG_LIB_CAT_MEDIUM_HARD 3 /* medium or hardware error, blank check */
|
|
||||||
/* [sk,asc,ascq: 0x3/0x4/0x8,*,*] */
|
|
||||||
#define SG_LIB_CAT_ILLEGAL_REQ 5 /* Illegal request (other than invalid */
|
|
||||||
/* opcode): [sk,asc,ascq: 0x5,*,*] */
|
|
||||||
#define SG_LIB_CAT_UNIT_ATTENTION 6 /* sense key, device state changed */
|
|
||||||
/* [sk,asc,ascq: 0x6,*,*] */
|
|
||||||
/* was SG_LIB_CAT_MEDIA_CHANGED earlier [sk,asc,ascq: 0x6,0x28,*] */
|
|
||||||
#define SG_LIB_CAT_DATA_PROTECT 7 /* sense key, media write protected? */
|
|
||||||
/* [sk,asc,ascq: 0x7,*,*] */
|
|
||||||
#define SG_LIB_CAT_INVALID_OP 9 /* (Illegal request,) Invalid opcode: */
|
|
||||||
/* [sk,asc,ascq: 0x5,0x20,0x0] */
|
|
||||||
#define SG_LIB_CAT_COPY_ABORTED 10 /* sense key, some data transferred */
|
|
||||||
/* [sk,asc,ascq: 0xa,*,*] */
|
|
||||||
#define SG_LIB_CAT_ABORTED_COMMAND 11 /* interpreted from sense buffer */
|
|
||||||
/* [sk,asc,ascq: 0xb,! 0x10,*] */
|
|
||||||
#define SG_LIB_CAT_MISCOMPARE 14 /* sense key, probably verify */
|
|
||||||
/* [sk,asc,ascq: 0xe,*,*] */
|
|
||||||
#define SG_LIB_CAT_NO_SENSE 20 /* sense data with key of "no sense" */
|
|
||||||
/* [sk,asc,ascq: 0x0,*,*] */
|
|
||||||
#define SG_LIB_CAT_RECOVERED 21 /* Successful command after recovered err */
|
|
||||||
/* [sk,asc,ascq: 0x1,*,*] */
|
|
||||||
#define SG_LIB_CAT_RES_CONFLICT SAM_STAT_RESERVATION_CONFLICT
|
|
||||||
/* 24: this is a SCSI status, not sense. */
|
|
||||||
/* It indicates reservation by another */
|
|
||||||
/* machine blocks this command */
|
|
||||||
#define SG_LIB_CAT_CONDITION_MET 25 /* SCSI status, not sense key. */
|
|
||||||
/* Only from PRE-FETCH (SBC-4) */
|
|
||||||
#define SG_LIB_CAT_BUSY 26 /* SCSI status, not sense. Invites retry */
|
|
||||||
#define SG_LIB_CAT_TS_FULL 27 /* SCSI status, not sense. Wait then retry */
|
|
||||||
#define SG_LIB_CAT_ACA_ACTIVE 28 /* SCSI status; ACA seldom used */
|
|
||||||
#define SG_LIB_CAT_TASK_ABORTED 29 /* SCSI status, this command aborted by? */
|
|
||||||
#define SG_LIB_CAT_PROTECTION 40 /* subset of aborted command (for PI, DIF) */
|
|
||||||
/* [sk,asc,ascq: 0xb,0x10,*] */
|
|
||||||
#define SG_LIB_NVME_STATUS 48 /* NVMe Status Field (SF) other than 0 */
|
|
||||||
#define SG_LIB_WILD_RESID 49 /* Residual value for data-in transfer of a */
|
|
||||||
/* SCSI command is nonsensical */
|
|
||||||
#define SG_LIB_OS_BASE_ERR 50 /* in Linux: values found in: */
|
|
||||||
/* include/uapi/asm-generic/errno-base.h */
|
|
||||||
/* Example: ENOMEM reported as 62 (=50+12) */
|
|
||||||
#define SG_LIB_CAT_MALFORMED 97 /* Response to SCSI command malformed */
|
|
||||||
#define SG_LIB_CAT_SENSE 98 /* Something else is in the sense buffer */
|
|
||||||
#define SG_LIB_CAT_OTHER 99 /* Some other error/warning has occurred */
|
|
||||||
/* (e.g. a transport or driver error) */
|
|
||||||
|
|
||||||
/* Returns a SG_LIB_CAT_* value. If cannot decode sense_buffer or a less
|
|
||||||
* common sense key then return SG_LIB_CAT_SENSE .*/
|
|
||||||
int sg_err_category_sense(const unsigned char * sense_buffer, int sb_len);
|
|
||||||
|
|
||||||
/* Here are some additional sense data categories that are not returned
|
|
||||||
* by sg_err_category_sense() but are returned by some related functions. */
|
|
||||||
#define SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO 17 /* Illegal request (other than */
|
|
||||||
/* invalid opcode) plus 'info' field: */
|
|
||||||
/* [sk,asc,ascq: 0x5,*,*] */
|
|
||||||
#define SG_LIB_CAT_MEDIUM_HARD_WITH_INFO 18 /* medium or hardware error */
|
|
||||||
/* sense key plus 'info' field: */
|
|
||||||
/* [sk,asc,ascq: 0x3/0x4,*,*] */
|
|
||||||
#define SG_LIB_CAT_PROTECTION_WITH_INFO 41 /* aborted command sense key, */
|
|
||||||
/* protection plus 'info' field: */
|
|
||||||
/* [sk,asc,ascq: 0xb,0x10,*] */
|
|
||||||
#define SG_LIB_CAT_TIMEOUT 33
|
|
||||||
|
|
||||||
/* Yield string associated with sense category. Returns 'buff' (or pointer
|
|
||||||
* to "Bad sense category" if 'buff' is NULL). If sense_cat unknown then
|
|
||||||
* yield "Sense category: <sense_cat>" string. */
|
|
||||||
const char * sg_get_category_sense_str(int sense_cat, int buff_len,
|
|
||||||
char * buff, int verbose);
|
|
||||||
|
|
||||||
|
|
||||||
/* Iterates to next designation descriptor in the device identification
|
|
||||||
* VPD page. The 'initial_desig_desc' should point to start of first
|
|
||||||
* descriptor with 'page_len' being the number of valid bytes in that
|
|
||||||
* and following descriptors. To start, 'off' should point to a negative
|
|
||||||
* value, thereafter it should point to the value yielded by the previous
|
|
||||||
* call. If 0 returned then 'initial_desig_desc + *off' should be a valid
|
|
||||||
* descriptor; returns -1 if normal end condition and -2 for an abnormal
|
|
||||||
* termination. Matches association, designator_type and/or code_set when
|
|
||||||
* any of those values are greater than or equal to zero. */
|
|
||||||
int sg_vpd_dev_id_iter(const unsigned char * initial_desig_desc, int page_len,
|
|
||||||
int * off, int m_assoc, int m_desig_type,
|
|
||||||
int m_code_set);
|
|
||||||
|
|
||||||
|
|
||||||
/* <<< General purpose (i.e. not SCSI specific) utility functions >>> */
|
|
||||||
|
|
||||||
/* Always returns valid string even if errnum is wild (or library problem).
|
|
||||||
* If errnum is negative, flip its sign. */
|
|
||||||
char * safe_strerror(int errnum);
|
|
||||||
|
|
||||||
|
|
||||||
/* Print (to stdout) 'str' of bytes in hex, 16 bytes per line optionally
|
|
||||||
* followed at the right hand side of the line with an ASCII interpretation.
|
|
||||||
* Each line is prefixed with an address, starting at 0 for str[0]..str[15].
|
|
||||||
* All output numbers are in hex. 'no_ascii' allows for 3 output types:
|
|
||||||
* > 0 each line has address then up to 16 ASCII-hex bytes
|
|
||||||
* = 0 in addition, the bytes are listed in ASCII to the right
|
|
||||||
* < 0 only the ASCII-hex bytes are listed (i.e. without address)
|
|
||||||
*/
|
|
||||||
void dStrHex(const char * str, int len, int no_ascii);
|
|
||||||
|
|
||||||
/* Print (to sg_warnings_strm (stderr)) 'str' of bytes in hex, 16 bytes per
|
|
||||||
* line optionally followed at right by its ASCII interpretation. Same
|
|
||||||
* logic as dStrHex() with different output stream (i.e. stderr). */
|
|
||||||
void dStrHexErr(const char * str, int len, int no_ascii);
|
|
||||||
|
|
||||||
/* Read 'len' bytes from 'str' and output as ASCII-Hex bytes (space
|
|
||||||
* separated) to 'b' not to exceed 'b_len' characters. Each line
|
|
||||||
* starts with 'leadin' (NULL for no leadin) and there are 16 bytes
|
|
||||||
* per line with an extra space between the 8th and 9th bytes. 'format'
|
|
||||||
* is 0 for repeat in printable ASCII ('.' for non printable chars) to
|
|
||||||
* right of each line; 1 don't (so just output ASCII hex). Returns
|
|
||||||
* number of bytes written to 'b' excluding the trailing '\0'. */
|
|
||||||
int dStrHexStr(const char * str, int len, const char * leadin, int format,
|
|
||||||
int cb_len, char * cbp);
|
|
||||||
|
|
||||||
/* The following 3 functions are equivalent to dStrHex(), dStrHexErr() and
|
|
||||||
* dStrHexStr() respectively. The difference is the type of the first of
|
|
||||||
* argument: uint8_t instead of char. The name of the argument is changed
|
|
||||||
* to b_str to stress it is a pointer to the start of a binary string. */
|
|
||||||
void hex2stdout(const uint8_t * b_str, int len, int no_ascii);
|
|
||||||
void hex2stderr(const uint8_t * b_str, int len, int no_ascii);
|
|
||||||
int hex2str(const uint8_t * b_str, int len, const char * leadin, int format,
|
|
||||||
int cb_len, char * cbp);
|
|
||||||
|
|
||||||
/* Returns true when executed on big endian machine; else returns false.
|
|
||||||
* Useful for displaying ATA identify words (which need swapping on a
|
|
||||||
* big endian machine). */
|
|
||||||
bool sg_is_big_endian();
|
|
||||||
|
|
||||||
/* Returns true if byte sequence starting at bp with a length of b_len is
|
|
||||||
* all zeros (for sg_all_zeros()) or all 0xff_s (for sg_all_ffs());
|
|
||||||
* otherwise returns false. If bp is NULL ir b_len <= 0 returns false. */
|
|
||||||
bool sg_all_zeros(const uint8_t * bp, int b_len);
|
|
||||||
bool sg_all_ffs(const uint8_t * bp, int b_len);
|
|
||||||
|
|
||||||
/* Extract character sequence from ATA words as in the model string
|
|
||||||
* in a IDENTIFY DEVICE response. Returns number of characters
|
|
||||||
* written to 'ochars' before 0 character is found or 'num' words
|
|
||||||
* are processed. */
|
|
||||||
int sg_ata_get_chars(const uint16_t * word_arr, int start_word,
|
|
||||||
int num_words, bool is_big_endian, char * ochars);
|
|
||||||
|
|
||||||
/* Print (to stdout) 16 bit 'words' in hex, 8 words per line optionally
|
|
||||||
* followed at the right hand side of the line with an ASCII interpretation
|
|
||||||
* (pairs of ASCII characters in big endian order (upper first)).
|
|
||||||
* Each line is prefixed with an address, starting at 0.
|
|
||||||
* All output numbers are in hex. 'no_ascii' allows for 3 output types:
|
|
||||||
* > 0 each line has address then up to 8 ASCII-hex words
|
|
||||||
* = 0 in addition, the words are listed in ASCII pairs to the right
|
|
||||||
* = -1 only the ASCII-hex words are listed (i.e. without address)
|
|
||||||
* = -2 only the ASCII-hex words, formatted for "hdparm --Istdin"
|
|
||||||
* < -2 same as -1
|
|
||||||
* If 'swapb' is true then bytes in each word swapped. Needs to be set
|
|
||||||
* for ATA IDENTIFY DEVICE response on big-endian machines.
|
|
||||||
*/
|
|
||||||
void dWordHex(const uint16_t * words, int num, int no_ascii, bool swapb);
|
|
||||||
|
|
||||||
/* If the number in 'buf' can not be decoded or the multiplier is unknown
|
|
||||||
* then -1 is returned. Accepts a hex prefix (0x or 0X) or a 'h' (or 'H')
|
|
||||||
* suffix. Otherwise a decimal multiplier suffix may be given. Recognised
|
|
||||||
* multipliers: c C *1; w W *2; b B *512; k K KiB *1,024;
|
|
||||||
* KB *1,000; m M MiB *1,048,576; MB *1,000,000; g G GiB *1,073,741,824;
|
|
||||||
* GB *1,000,000,000 and <n>x<m> which multiplies <n> by <m> . Ignore leading
|
|
||||||
* spaces and tabs; accept comma, hyphen, space, tab and hash as terminator.
|
|
||||||
*/
|
|
||||||
int sg_get_num(const char * buf);
|
|
||||||
|
|
||||||
/* If the number in 'buf' can not be decoded then -1 is returned. Accepts a
|
|
||||||
* hex prefix (0x or 0X) or a 'h' (or 'H') suffix; otherwise decimal is
|
|
||||||
* assumed. Does not accept multipliers. Accept a comma (","), hyphen ("-"),
|
|
||||||
* a whitespace or newline as terminator. Only decimal numbers can represent
|
|
||||||
* negative numbers and '-1' must be treated separately. */
|
|
||||||
int sg_get_num_nomult(const char * buf);
|
|
||||||
|
|
||||||
/* If the number in 'buf' can not be decoded or the multiplier is unknown
|
|
||||||
* then -1LL is returned. Accepts a hex prefix (0x or 0X) or a 'h' (or 'H')
|
|
||||||
* suffix. Otherwise a decimal multiplier suffix may be given. In addition
|
|
||||||
* to supporting the multipliers of sg_get_num(), this function supports:
|
|
||||||
* t T TiB *(2**40); TB *(10**12); p P PiB *(2**50); PB *(10**15) .
|
|
||||||
* Ignore leading spaces and tabs; accept comma, hyphen, space, tab and hash
|
|
||||||
* as terminator. */
|
|
||||||
int64_t sg_get_llnum(const char * buf);
|
|
||||||
|
|
||||||
/* If the number in 'buf' can not be decoded then -1 is returned. Accepts a
|
|
||||||
* hex prefix (0x or 0X) or a 'h' (or 'H') suffix; otherwise decimal is
|
|
||||||
* assumed. Does not accept multipliers. Accept a comma (","), hyphen ("-"),
|
|
||||||
* a whitespace or newline as terminator. Only decimal numbers can represent
|
|
||||||
* negative numbers and '-1' must be treated separately. */
|
|
||||||
int64_t sg_get_llnum_nomult(const char * buf);
|
|
||||||
|
|
||||||
/* Returns pointer to heap (or NULL) that is aligned to a align_to byte
|
|
||||||
* boundary. Sends back *buff_to_free pointer in third argument that may be
|
|
||||||
* different from the return value. If it is different then the *buff_to_free
|
|
||||||
* pointer should be freed (rather than the returned value) when the heap is
|
|
||||||
* no longer needed. If align_to is 0 then aligns to OS's page size. Sets all
|
|
||||||
* returned heap to zeros. If num_bytes is 0 then set to page size. */
|
|
||||||
uint8_t * sg_memalign(uint32_t num_bytes, uint32_t align_to,
|
|
||||||
uint8_t ** buff_to_free, bool vb);
|
|
||||||
|
|
||||||
/* Returns OS page size in bytes. If uncertain returns 4096. */
|
|
||||||
uint32_t sg_get_page_size(void);
|
|
||||||
|
|
||||||
/* If os_err_num is within bounds then the returned value is 'os_err_num +
|
|
||||||
* SG_LIB_OS_BASE_ERR' otherwise -1 is returned. If os_err_num is 0 then 0
|
|
||||||
* is returned. */
|
|
||||||
int sg_convert_errno(int os_err_num);
|
|
||||||
|
|
||||||
|
|
||||||
/* <<< Architectural support functions [is there a better place?] >>> */
|
|
||||||
|
|
||||||
/* Non Unix OSes distinguish between text and binary files.
|
|
||||||
* Set text mode on fd. Does nothing in Unix. Returns negative number on
|
|
||||||
* failure. */
|
|
||||||
int sg_set_text_mode(int fd);
|
|
||||||
|
|
||||||
/* Set binary mode on fd. Does nothing in Unix. Returns negative number on
|
|
||||||
* failure. */
|
|
||||||
int sg_set_binary_mode(int fd);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* SG_LIB_H */
|
|
@ -1,121 +0,0 @@
|
|||||||
#ifndef SG_LIB_DATA_H
|
|
||||||
#define SG_LIB_DATA_H
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (c) 2007-2018 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This header file contains some structure declarations and array name
|
|
||||||
* declarations which are defined in the sg_lib_data.c .
|
|
||||||
* Typically this header does not need to be exposed to users of the
|
|
||||||
* sg_lib interface declared in sg_libs.h .
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Operation codes with associated service actions that change or qualify
|
|
||||||
* the command name */
|
|
||||||
#define SG_EXTENDED_COPY 0x83 /* since spc4r34 became next entry */
|
|
||||||
#define SG_3PARTY_COPY_OUT 0x83 /* new in spc4r34: Third party copy out */
|
|
||||||
#define SG_RECEIVE_COPY 0x84 /* since spc4r34 became next entry */
|
|
||||||
#define SG_3PARTY_COPY_IN 0x84 /* new in spc4r34: Third party copy in */
|
|
||||||
#define SG_MAINTENANCE_IN 0xa3
|
|
||||||
#define SG_MAINTENANCE_OUT 0xa4
|
|
||||||
#define SG_PERSISTENT_RESERVE_IN 0x5e
|
|
||||||
#define SG_PERSISTENT_RESERVE_OUT 0x5f
|
|
||||||
#define SG_READ_ATTRIBUTE 0x8c
|
|
||||||
#define SG_READ_BUFFER 0x3c /* now READ BUFFER(10) */
|
|
||||||
#define SG_READ_BUFFER_16 0x9b
|
|
||||||
#define SG_READ_POSITION 0x34 /* SSC command with service actions */
|
|
||||||
#define SG_SANITIZE 0x48
|
|
||||||
#define SG_SERVICE_ACTION_BIDI 0x9d
|
|
||||||
#define SG_SERVICE_ACTION_IN_12 0xab
|
|
||||||
#define SG_SERVICE_ACTION_IN_16 0x9e
|
|
||||||
#define SG_SERVICE_ACTION_OUT_12 0xa9
|
|
||||||
#define SG_SERVICE_ACTION_OUT_16 0x9f
|
|
||||||
#define SG_VARIABLE_LENGTH_CMD 0x7f
|
|
||||||
#define SG_WRITE_BUFFER 0x3b
|
|
||||||
#define SG_ZONING_OUT 0x94
|
|
||||||
#define SG_ZONING_IN 0x95
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct sg_lib_simple_value_name_t {
|
|
||||||
int value;
|
|
||||||
const char * name;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sg_lib_value_name_t {
|
|
||||||
int value;
|
|
||||||
int peri_dev_type; /* 0 -> SPC and/or PDT_DISK, >0 -> PDT */
|
|
||||||
const char * name;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sg_lib_asc_ascq_t {
|
|
||||||
unsigned char asc; /* additional sense code */
|
|
||||||
unsigned char ascq; /* additional sense code qualifier */
|
|
||||||
const char * text;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sg_lib_asc_ascq_range_t {
|
|
||||||
unsigned char asc; /* additional sense code (ASC) */
|
|
||||||
unsigned char ascq_min; /* ASCQ minimum in range */
|
|
||||||
unsigned char ascq_max; /* ASCQ maximum in range */
|
|
||||||
const char * text;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* First use: SCSI status, sense_key, asc, ascq tuple */
|
|
||||||
struct sg_lib_4tuple_u8 {
|
|
||||||
uint8_t t1;
|
|
||||||
uint8_t t2;
|
|
||||||
uint8_t t3;
|
|
||||||
uint8_t t4;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
extern const char * sg_lib_version_str;
|
|
||||||
|
|
||||||
extern struct sg_lib_value_name_t sg_lib_normal_opcodes[];
|
|
||||||
extern struct sg_lib_value_name_t sg_lib_read_buff_arr[];
|
|
||||||
extern struct sg_lib_value_name_t sg_lib_write_buff_arr[];
|
|
||||||
extern struct sg_lib_value_name_t sg_lib_maint_in_arr[];
|
|
||||||
extern struct sg_lib_value_name_t sg_lib_maint_out_arr[];
|
|
||||||
extern struct sg_lib_value_name_t sg_lib_pr_in_arr[];
|
|
||||||
extern struct sg_lib_value_name_t sg_lib_pr_out_arr[];
|
|
||||||
extern struct sg_lib_value_name_t sg_lib_sanitize_sa_arr[];
|
|
||||||
extern struct sg_lib_value_name_t sg_lib_serv_in12_arr[];
|
|
||||||
extern struct sg_lib_value_name_t sg_lib_serv_out12_arr[];
|
|
||||||
extern struct sg_lib_value_name_t sg_lib_serv_in16_arr[];
|
|
||||||
extern struct sg_lib_value_name_t sg_lib_serv_out16_arr[];
|
|
||||||
extern struct sg_lib_value_name_t sg_lib_serv_bidi_arr[];
|
|
||||||
extern struct sg_lib_value_name_t sg_lib_xcopy_sa_arr[];
|
|
||||||
extern struct sg_lib_value_name_t sg_lib_rec_copy_sa_arr[];
|
|
||||||
extern struct sg_lib_value_name_t sg_lib_variable_length_arr[];
|
|
||||||
extern struct sg_lib_value_name_t sg_lib_zoning_out_arr[];
|
|
||||||
extern struct sg_lib_value_name_t sg_lib_zoning_in_arr[];
|
|
||||||
extern struct sg_lib_value_name_t sg_lib_read_attr_arr[];
|
|
||||||
extern struct sg_lib_value_name_t sg_lib_read_pos_arr[];
|
|
||||||
extern struct sg_lib_asc_ascq_range_t sg_lib_asc_ascq_range[];
|
|
||||||
extern struct sg_lib_asc_ascq_t sg_lib_asc_ascq[];
|
|
||||||
extern struct sg_lib_value_name_t sg_lib_scsi_feature_sets[];
|
|
||||||
extern const char * sg_lib_sense_key_desc[];
|
|
||||||
extern const char * sg_lib_pdt_strs[];
|
|
||||||
extern const char * sg_lib_transport_proto_strs[];
|
|
||||||
extern int sg_lib_pdt_decay_arr[];
|
|
||||||
|
|
||||||
extern struct sg_lib_value_name_t sg_lib_nvme_cmd_status_arr[];
|
|
||||||
extern struct sg_lib_4tuple_u8 sg_lib_scsi_status_sense_arr[];
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,56 +0,0 @@
|
|||||||
#ifndef SG_LINUX_INC_H
|
|
||||||
#define SG_LINUX_INC_H
|
|
||||||
|
|
||||||
#ifdef SG_KERNEL_INCLUDES
|
|
||||||
#define __user
|
|
||||||
typedef unsigned char u8;
|
|
||||||
#include "/usr/src/linux/include/scsi/sg.h"
|
|
||||||
#include "/usr/src/linux/include/scsi/scsi.h"
|
|
||||||
#else
|
|
||||||
#ifdef SG_TRICK_GNU_INCLUDES
|
|
||||||
#include <linux/../scsi/sg.h>
|
|
||||||
#include <linux/../scsi/scsi.h>
|
|
||||||
#else
|
|
||||||
#include <scsi/sg.h>
|
|
||||||
#include <scsi/scsi.h>
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef BLKGETSIZE64
|
|
||||||
#ifndef u64
|
|
||||||
#include <stdint.h> /* C99 header for exact integer types */
|
|
||||||
typedef uint64_t u64; /* problems with BLKGETSIZE64 ioctl in lk 2.4 */
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
Getting the correct include files for the sg interface can be an ordeal.
|
|
||||||
In a perfect world, one would just write:
|
|
||||||
#include <scsi/sg.h>
|
|
||||||
#include <scsi/scsi.h>
|
|
||||||
This would include the files found in the /usr/include/scsi directory.
|
|
||||||
Those files are maintained with the GNU library which may or may not
|
|
||||||
agree with the kernel and version of sg driver that is running. Any
|
|
||||||
many cases this will not matter. However in some it might, for example
|
|
||||||
glibc 2.1's include files match the sg driver found in the lk 2.2
|
|
||||||
series. Hence if glibc 2.1 is used with lk 2.4 then the additional
|
|
||||||
sg v3 interface will not be visible.
|
|
||||||
If this is a problem then defining SG_KERNEL_INCLUDES will access the
|
|
||||||
kernel supplied header files (assuming they are in the normal place).
|
|
||||||
The GNU library maintainers and various kernel people don't like
|
|
||||||
this approach (but it does work).
|
|
||||||
The technique selected by defining SG_TRICK_GNU_INCLUDES worked (and
|
|
||||||
was used) prior to glibc 2.2 . Prior to that version /usr/include/linux
|
|
||||||
was a symbolic link to /usr/src/linux/include/linux .
|
|
||||||
|
|
||||||
There are other approaches if this include "mixup" causes pain. These
|
|
||||||
would involve include files being copied or symbolic links being
|
|
||||||
introduced.
|
|
||||||
|
|
||||||
Sorry about the inconvenience. Typically neither SG_KERNEL_INCLUDES
|
|
||||||
nor SG_TRICK_GNU_INCLUDES is defined.
|
|
||||||
|
|
||||||
dpg 20010415, 20030522
|
|
||||||
*/
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,30 +0,0 @@
|
|||||||
#ifndef SG_PR2SERR_H
|
|
||||||
#define SG_PR2SERR_H
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (c) 2004-2018 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#if defined(__GNUC__) || defined(__clang__)
|
|
||||||
int pr2serr(const char * fmt, ...)
|
|
||||||
__attribute__ ((format (printf, 1, 2)));
|
|
||||||
#else
|
|
||||||
int pr2serr(const char * fmt, ...);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,215 +0,0 @@
|
|||||||
#ifndef SG_PT_H
|
|
||||||
#define SG_PT_H
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (c) 2005-2018 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* This declaration hides the fact that each implementation has its own
|
|
||||||
* structure "derived" (using a C++ term) from this one. It compiles
|
|
||||||
* because 'struct sg_pt_base' is only referenced (by pointer: 'objp')
|
|
||||||
* in this interface. An instance of this structure represents the
|
|
||||||
* context of one SCSI command. */
|
|
||||||
struct sg_pt_base;
|
|
||||||
|
|
||||||
|
|
||||||
/* The format of the version string is like this: "2.01 20090201".
|
|
||||||
* The leading digit will be incremented if this interface changes
|
|
||||||
* in a way that may impact backward compatibility. */
|
|
||||||
const char * scsi_pt_version();
|
|
||||||
|
|
||||||
|
|
||||||
/* Returns >= 0 if successful. If error in Unix returns negated errno. */
|
|
||||||
int scsi_pt_open_device(const char * device_name, bool read_only, int verbose);
|
|
||||||
|
|
||||||
/* Similar to scsi_pt_open_device() but takes Unix style open flags OR-ed
|
|
||||||
* together. Returns valid file descriptor( >= 0 ) if successful, otherwise
|
|
||||||
* returns -1 or a negated errno.
|
|
||||||
* In Win32 O_EXCL translated to equivalent. */
|
|
||||||
int scsi_pt_open_flags(const char * device_name, int flags, int verbose);
|
|
||||||
|
|
||||||
/* Returns 0 if successful. If error in Unix returns negated errno. */
|
|
||||||
int scsi_pt_close_device(int device_fd);
|
|
||||||
|
|
||||||
/* Assumes dev_fd is an "open" file handle associated with device_name. If
|
|
||||||
* the implementation (possibly for one OS) cannot determine from dev_fd if
|
|
||||||
* a SCSI or NVMe pass-through is referenced, then it might guess based on
|
|
||||||
* device_name. Returns 1 if SCSI generic pass-though device, returns 2 if
|
|
||||||
* secondary SCSI pass-through device (in Linux a bsg device); returns 3 is
|
|
||||||
* char NVMe device (i.e. no NSID); returns 4 if block NVMe device (includes
|
|
||||||
* NSID), or 0 if something else (e.g. ATA block device) or dev_fd < 0.
|
|
||||||
* If error, returns negated errno (operating system) value. */
|
|
||||||
int check_pt_file_handle(int dev_fd, const char * device_name, int verbose);
|
|
||||||
|
|
||||||
|
|
||||||
/* Creates an object that can be used to issue one or more SCSI commands
|
|
||||||
* (or task management functions). Returns NULL if problem.
|
|
||||||
* Once this object has been created it should be destroyed with
|
|
||||||
* destruct_scsi_pt_obj() when it is no longer needed. */
|
|
||||||
struct sg_pt_base * construct_scsi_pt_obj(void);
|
|
||||||
|
|
||||||
/* An alternate way to create an object that can be used to issue one or
|
|
||||||
* more SCSI commands (or task management functions). This variant
|
|
||||||
* associate a device file descriptor (handle) with the object and a
|
|
||||||
* verbose argument that causes error messages if errors occur. The
|
|
||||||
* reason for this is to optionally allow the detection of NVMe devices
|
|
||||||
* that will cause pt_device_is_nvme() to return true. Set dev_fd to
|
|
||||||
* -1 if no open device file descriptor is available. Caller should
|
|
||||||
* additionally call get_scsi_pt_os_err() after this call. */
|
|
||||||
struct sg_pt_base *
|
|
||||||
construct_scsi_pt_obj_with_fd(int dev_fd, int verbose);
|
|
||||||
|
|
||||||
/* Forget any previous dev_fd and install the one given. May attempt to
|
|
||||||
* find file type (e.g. if pass-though) from OS so there could be an error.
|
|
||||||
* Returns 0 for success or the same value as get_scsi_pt_os_err()
|
|
||||||
* will return. dev_fd should be >= 0 for a valid file handle or -1 . */
|
|
||||||
int set_pt_file_handle(struct sg_pt_base * objp, int dev_fd, int verbose);
|
|
||||||
|
|
||||||
/* Valid file handles (which is the return value) are >= 0 . Returns -1
|
|
||||||
* if there is no valid file handle. */
|
|
||||||
int get_pt_file_handle(const struct sg_pt_base * objp);
|
|
||||||
|
|
||||||
/* Clear state information held in *objp . This allows this object to be
|
|
||||||
* used to issue more than one SCSI command. The dev_fd is remembered.
|
|
||||||
* Use set_pt_file_handle() to change dev_fd. */
|
|
||||||
void clear_scsi_pt_obj(struct sg_pt_base * objp);
|
|
||||||
|
|
||||||
/* Set the CDB (command descriptor block) */
|
|
||||||
void set_scsi_pt_cdb(struct sg_pt_base * objp, const unsigned char * cdb,
|
|
||||||
int cdb_len);
|
|
||||||
/* Set the sense buffer and the maximum length that it can handle */
|
|
||||||
void set_scsi_pt_sense(struct sg_pt_base * objp, unsigned char * sense,
|
|
||||||
int max_sense_len);
|
|
||||||
/* Set a pointer and length to be used for data transferred from device */
|
|
||||||
void set_scsi_pt_data_in(struct sg_pt_base * objp, /* from device */
|
|
||||||
unsigned char * dxferp, int dxfer_ilen);
|
|
||||||
/* Set a pointer and length to be used for data transferred to device */
|
|
||||||
void set_scsi_pt_data_out(struct sg_pt_base * objp, /* to device */
|
|
||||||
const unsigned char * dxferp, int dxfer_olen);
|
|
||||||
/* Set a pointer and length to be used for metadata transferred to
|
|
||||||
* (out_true=true) or from (out_true-false) device */
|
|
||||||
void set_pt_metadata_xfer(struct sg_pt_base * objp, unsigned char * mdxferp,
|
|
||||||
uint32_t mdxfer_len, bool out_true);
|
|
||||||
/* The following "set_"s implementations may be dummies */
|
|
||||||
void set_scsi_pt_packet_id(struct sg_pt_base * objp, int pack_id);
|
|
||||||
void set_scsi_pt_tag(struct sg_pt_base * objp, uint64_t tag);
|
|
||||||
void set_scsi_pt_task_management(struct sg_pt_base * objp, int tmf_code);
|
|
||||||
void set_scsi_pt_task_attr(struct sg_pt_base * objp, int attribute,
|
|
||||||
int priority);
|
|
||||||
|
|
||||||
/* Following is a guard which is defined when set_scsi_pt_flags() is
|
|
||||||
* present. Older versions of this library may not have this function. */
|
|
||||||
#define SCSI_PT_FLAGS_FUNCTION 1
|
|
||||||
/* If neither QUEUE_AT_HEAD nor QUEUE_AT_TAIL are given, or both
|
|
||||||
* are given, use the pass-through default. */
|
|
||||||
#define SCSI_PT_FLAGS_QUEUE_AT_TAIL 0x10
|
|
||||||
#define SCSI_PT_FLAGS_QUEUE_AT_HEAD 0x20
|
|
||||||
/* Set (potentially OS dependent) flags for pass-through mechanism.
|
|
||||||
* Apart from contradictions, flags can be OR-ed together. */
|
|
||||||
void set_scsi_pt_flags(struct sg_pt_base * objp, int flags);
|
|
||||||
|
|
||||||
#define SCSI_PT_DO_START_OK 0
|
|
||||||
#define SCSI_PT_DO_BAD_PARAMS 1
|
|
||||||
#define SCSI_PT_DO_TIMEOUT 2
|
|
||||||
#define SCSI_PT_DO_NVME_STATUS 48 /* == SG_LIB_NVME_STATUS */
|
|
||||||
/* If OS error prior to or during command submission then returns negated
|
|
||||||
* error value (e.g. Unix '-errno'). This includes interrupted system calls
|
|
||||||
* (e.g. by a signal) in which case -EINTR would be returned. Note that
|
|
||||||
* system call errors also can be fetched with get_scsi_pt_os_err().
|
|
||||||
* Return 0 if okay (i.e. at the very least: command sent). Positive
|
|
||||||
* return values are errors (see SCSI_PT_DO_* defines). If a file descriptor
|
|
||||||
* has already been provided by construct_scsi_pt_obj_with_fd() then the
|
|
||||||
* given 'fd' can be -1 or the same value as given to the constructor. */
|
|
||||||
int do_scsi_pt(struct sg_pt_base * objp, int fd, int timeout_secs,
|
|
||||||
int verbose);
|
|
||||||
|
|
||||||
#define SCSI_PT_RESULT_GOOD 0
|
|
||||||
#define SCSI_PT_RESULT_STATUS 1 /* other than GOOD and CHECK CONDITION */
|
|
||||||
#define SCSI_PT_RESULT_SENSE 2
|
|
||||||
#define SCSI_PT_RESULT_TRANSPORT_ERR 3
|
|
||||||
#define SCSI_PT_RESULT_OS_ERR 4
|
|
||||||
/* highest numbered applicable category returned */
|
|
||||||
int get_scsi_pt_result_category(const struct sg_pt_base * objp);
|
|
||||||
|
|
||||||
/* If not available return 0 which implies there is no residual
|
|
||||||
* value. If supported the number of bytes actually sent back by
|
|
||||||
* the device is 'dxfer_ilen - get_scsi_pt_len()' bytes. */
|
|
||||||
int get_scsi_pt_resid(const struct sg_pt_base * objp);
|
|
||||||
|
|
||||||
/* Returns SCSI status value (from device that received the command). If an
|
|
||||||
* NVMe command was issued directly (i.e. through do_scsi_pt() then return
|
|
||||||
* NVMe status (i.e. ((SCT << 8) | SC)) */
|
|
||||||
int get_scsi_pt_status_response(const struct sg_pt_base * objp);
|
|
||||||
|
|
||||||
/* Returns SCSI status value or, if NVMe command given to do_scsi_pt(),
|
|
||||||
* then returns NVMe result (i.e. DWord(0) from completion queue). If
|
|
||||||
* 'objp' is NULL then returns 0xffffffff. */
|
|
||||||
uint32_t get_pt_result(const struct sg_pt_base * objp);
|
|
||||||
|
|
||||||
/* Actual sense length returned. If sense data is present but
|
|
||||||
actual sense length is not known, return 'max_sense_len' */
|
|
||||||
int get_scsi_pt_sense_len(const struct sg_pt_base * objp);
|
|
||||||
|
|
||||||
/* If not available return 0 (for success). */
|
|
||||||
int get_scsi_pt_os_err(const struct sg_pt_base * objp);
|
|
||||||
char * get_scsi_pt_os_err_str(const struct sg_pt_base * objp, int max_b_len,
|
|
||||||
char * b);
|
|
||||||
|
|
||||||
/* If not available return 0 (for success) */
|
|
||||||
int get_scsi_pt_transport_err(const struct sg_pt_base * objp);
|
|
||||||
void set_scsi_pt_transport_err(struct sg_pt_base * objp, int err);
|
|
||||||
char * get_scsi_pt_transport_err_str(const struct sg_pt_base * objp,
|
|
||||||
int max_b_len, char * b);
|
|
||||||
|
|
||||||
/* If not available return -1 */
|
|
||||||
int get_scsi_pt_duration_ms(const struct sg_pt_base * objp);
|
|
||||||
|
|
||||||
/* Return true if device associated with 'objp' uses NVMe command set. To
|
|
||||||
* be useful (in modifying the type of command sent (SCSI or NVMe) then
|
|
||||||
* construct_scsi_pt_obj_with_fd() should be used followed by an invocation
|
|
||||||
* of this function. */
|
|
||||||
bool pt_device_is_nvme(const struct sg_pt_base * objp);
|
|
||||||
|
|
||||||
/* If a NVMe block device (which includes the NSID) handle is associated
|
|
||||||
* with 'objp', then its NSID is returned (values range from 0x1 to
|
|
||||||
* 0xffffffe). Otherwise 0 is returned. */
|
|
||||||
uint32_t get_pt_nvme_nsid(const struct sg_pt_base * objp);
|
|
||||||
|
|
||||||
|
|
||||||
/* Should be invoked once per objp after other processing is complete in
|
|
||||||
* order to clean up resources. For ever successful construct_scsi_pt_obj()
|
|
||||||
* call there should be one destruct_scsi_pt_obj(). If the
|
|
||||||
* construct_scsi_pt_obj_with_fd() function was used to create this object
|
|
||||||
* then the dev_fd provided to that constructor is not altered by this
|
|
||||||
* destructor. So the user should still close dev_fd (perhaps with
|
|
||||||
* scsi_pt_close_device() ). */
|
|
||||||
void destruct_scsi_pt_obj(struct sg_pt_base * objp);
|
|
||||||
|
|
||||||
#ifdef SG_LIB_WIN32
|
|
||||||
#define SG_LIB_WIN32_DIRECT 1
|
|
||||||
|
|
||||||
/* Request SPT direct interface when state_direct is 1, state_direct set
|
|
||||||
* to 0 for the SPT indirect interface. Default setting selected by build
|
|
||||||
* (i.e. library compile time) and is usually indirect. */
|
|
||||||
void scsi_pt_win32_direct(int state_direct);
|
|
||||||
|
|
||||||
/* Returns current SPT interface state, 1 for direct, 0 for indirect */
|
|
||||||
int scsi_pt_win32_spt_state(void);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,171 +0,0 @@
|
|||||||
#ifndef SG_PT_LINUX_H
|
|
||||||
#define SG_PT_LINUX_H
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (c) 2017 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
#include <linux/types.h>
|
|
||||||
|
|
||||||
#include "sg_pt_nvme.h"
|
|
||||||
|
|
||||||
/* This header is for internal use by the sg3_utils library (libsgutils)
|
|
||||||
* and is Linux specific. Best not to include it directly in code that
|
|
||||||
* is meant to be OS independent. */
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef HAVE_LINUX_BSG_H
|
|
||||||
|
|
||||||
#define BSG_PROTOCOL_SCSI 0
|
|
||||||
|
|
||||||
#define BSG_SUB_PROTOCOL_SCSI_CMD 0
|
|
||||||
#define BSG_SUB_PROTOCOL_SCSI_TMF 1
|
|
||||||
#define BSG_SUB_PROTOCOL_SCSI_TRANSPORT 2
|
|
||||||
|
|
||||||
/*
|
|
||||||
* For flag constants below:
|
|
||||||
* sg.h sg_io_hdr also has bits defined for it's flags member. These
|
|
||||||
* two flag values (0x10 and 0x20) have the same meaning in sg.h . For
|
|
||||||
* bsg the BSG_FLAG_Q_AT_HEAD flag is ignored since it is the default.
|
|
||||||
*/
|
|
||||||
#define BSG_FLAG_Q_AT_TAIL 0x10 /* default is Q_AT_HEAD */
|
|
||||||
#define BSG_FLAG_Q_AT_HEAD 0x20
|
|
||||||
|
|
||||||
struct sg_io_v4 {
|
|
||||||
__s32 guard; /* [i] 'Q' to differentiate from v3 */
|
|
||||||
__u32 protocol; /* [i] 0 -> SCSI , .... */
|
|
||||||
__u32 subprotocol; /* [i] 0 -> SCSI command, 1 -> SCSI task
|
|
||||||
management function, .... */
|
|
||||||
|
|
||||||
__u32 request_len; /* [i] in bytes */
|
|
||||||
__u64 request; /* [i], [*i] {SCSI: cdb} */
|
|
||||||
__u64 request_tag; /* [i] {SCSI: task tag (only if flagged)} */
|
|
||||||
__u32 request_attr; /* [i] {SCSI: task attribute} */
|
|
||||||
__u32 request_priority; /* [i] {SCSI: task priority} */
|
|
||||||
__u32 request_extra; /* [i] {spare, for padding} */
|
|
||||||
__u32 max_response_len; /* [i] in bytes */
|
|
||||||
__u64 response; /* [i], [*o] {SCSI: (auto)sense data} */
|
|
||||||
|
|
||||||
/* "dout_": data out (to device); "din_": data in (from device) */
|
|
||||||
__u32 dout_iovec_count; /* [i] 0 -> "flat" dout transfer else
|
|
||||||
dout_xfer points to array of iovec */
|
|
||||||
__u32 dout_xfer_len; /* [i] bytes to be transferred to device */
|
|
||||||
__u32 din_iovec_count; /* [i] 0 -> "flat" din transfer */
|
|
||||||
__u32 din_xfer_len; /* [i] bytes to be transferred from device */
|
|
||||||
__u64 dout_xferp; /* [i], [*i] */
|
|
||||||
__u64 din_xferp; /* [i], [*o] */
|
|
||||||
|
|
||||||
__u32 timeout; /* [i] units: millisecond */
|
|
||||||
__u32 flags; /* [i] bit mask */
|
|
||||||
__u64 usr_ptr; /* [i->o] unused internally */
|
|
||||||
__u32 spare_in; /* [i] */
|
|
||||||
|
|
||||||
__u32 driver_status; /* [o] 0 -> ok */
|
|
||||||
__u32 transport_status; /* [o] 0 -> ok */
|
|
||||||
__u32 device_status; /* [o] {SCSI: command completion status} */
|
|
||||||
__u32 retry_delay; /* [o] {SCSI: status auxiliary information} */
|
|
||||||
__u32 info; /* [o] additional information */
|
|
||||||
__u32 duration; /* [o] time to complete, in milliseconds */
|
|
||||||
__u32 response_len; /* [o] bytes of response actually written */
|
|
||||||
__s32 din_resid; /* [o] din_xfer_len - actual_din_xfer_len */
|
|
||||||
__s32 dout_resid; /* [o] dout_xfer_len - actual_dout_xfer_len */
|
|
||||||
__u64 generated_tag; /* [o] {SCSI: transport generated task tag} */
|
|
||||||
__u32 spare_out; /* [o] */
|
|
||||||
|
|
||||||
__u32 padding;
|
|
||||||
};
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#include <linux/bsg.h>
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
struct sg_pt_linux_scsi {
|
|
||||||
struct sg_io_v4 io_hdr; /* use v4 header as it is more general */
|
|
||||||
/* Leave io_hdr in first place of this structure */
|
|
||||||
bool is_sg;
|
|
||||||
bool is_bsg;
|
|
||||||
bool is_nvme; /* OS device type, if false ignore nvme_direct */
|
|
||||||
bool nvme_direct; /* false: our SNTL; true: received NVMe command */
|
|
||||||
bool mdxfer_out; /* direction of metadata xfer, true->data-out */
|
|
||||||
bool scsi_dsense; /* SCSI descriptor sense active when true */
|
|
||||||
int dev_fd; /* -1 if not given (yet) */
|
|
||||||
int in_err;
|
|
||||||
int os_err;
|
|
||||||
uint32_t nvme_nsid; /* 1 to 0xfffffffe are possibly valid, 0
|
|
||||||
* implies dev_fd is not a NVMe device
|
|
||||||
* (is_nvme=false) or it is a NVMe char
|
|
||||||
* device (e.g. /dev/nvme0 ) */
|
|
||||||
uint32_t nvme_result; /* DW0 from completion queue */
|
|
||||||
uint32_t nvme_status; /* SCT|SC: DW3 27:17 from completion queue,
|
|
||||||
* note: the DNR+More bit are not there.
|
|
||||||
* The whole 16 byte completion q entry is
|
|
||||||
* sent back as sense data */
|
|
||||||
uint32_t mdxfer_len;
|
|
||||||
void * mdxferp;
|
|
||||||
uint8_t * nvme_id_ctlp; /* cached response to controller IDENTIFY */
|
|
||||||
uint8_t * free_nvme_id_ctlp;
|
|
||||||
unsigned char tmf_request[4];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sg_pt_base {
|
|
||||||
struct sg_pt_linux_scsi impl;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef sg_nvme_admin_cmd
|
|
||||||
#define sg_nvme_admin_cmd sg_nvme_passthru_cmd
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Linux NVMe related ioctls */
|
|
||||||
#ifndef NVME_IOCTL_ID
|
|
||||||
#define NVME_IOCTL_ID _IO('N', 0x40)
|
|
||||||
#endif
|
|
||||||
#ifndef NVME_IOCTL_ADMIN_CMD
|
|
||||||
#define NVME_IOCTL_ADMIN_CMD _IOWR('N', 0x41, struct sg_nvme_admin_cmd)
|
|
||||||
#endif
|
|
||||||
#ifndef NVME_IOCTL_SUBMIT_IO
|
|
||||||
#define NVME_IOCTL_SUBMIT_IO _IOW('N', 0x42, struct sg_nvme_user_io)
|
|
||||||
#endif
|
|
||||||
#ifndef NVME_IOCTL_IO_CMD
|
|
||||||
#define NVME_IOCTL_IO_CMD _IOWR('N', 0x43, struct sg_nvme_passthru_cmd)
|
|
||||||
#endif
|
|
||||||
#ifndef NVME_IOCTL_RESET
|
|
||||||
#define NVME_IOCTL_RESET _IO('N', 0x44)
|
|
||||||
#endif
|
|
||||||
#ifndef NVME_IOCTL_SUBSYS_RESET
|
|
||||||
#define NVME_IOCTL_SUBSYS_RESET _IO('N', 0x45)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
extern bool sg_bsg_nvme_char_major_checked;
|
|
||||||
extern int sg_bsg_major;
|
|
||||||
extern volatile int sg_nvme_char_major;
|
|
||||||
extern long sg_lin_page_size;
|
|
||||||
|
|
||||||
void sg_find_bsg_nvme_char_major(int verbose);
|
|
||||||
int sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int time_secs, int vb);
|
|
||||||
|
|
||||||
/* This trims given NVMe block device name in Linux (e.g. /dev/nvme0n1p5)
|
|
||||||
* to the name of its associated char device (e.g. /dev/nvme0). If this
|
|
||||||
* occurs true is returned and the char device name is placed in 'b' (as
|
|
||||||
* long as b_len is sufficient). Otherwise false is returned. */
|
|
||||||
bool sg_get_nvme_char_devname(const char * nvme_block_devname, uint32_t b_len,
|
|
||||||
char * b);
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* end of SG_PT_LINUX_H */
|
|
@ -1,172 +0,0 @@
|
|||||||
#ifndef SG_PT_NVME_H
|
|
||||||
#define SG_PT_NVME_H
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (c) 2017-2018 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* structures copied and slightly modified from <linux/nvme_ioctl.h> which
|
|
||||||
* is Copyright (c) 2011-2014, Intel Corporation. */
|
|
||||||
|
|
||||||
|
|
||||||
/* Note that the command input structure is in (packed) "cpu" format. That
|
|
||||||
* means, for example, if the CPU is little endian (most are) then so is the
|
|
||||||
* structure. However what comes out in the data-in buffer (e.g. for the
|
|
||||||
* Admin Identify command response) is almost all little endian following ATA
|
|
||||||
* (but no SCSI and IP which are big endian) and Intel's preference. There
|
|
||||||
* are exceptions, for example the EUI-64 identifiers in the Admin Identify
|
|
||||||
* response are big endian.
|
|
||||||
*
|
|
||||||
* Code online (e.g. nvme-cli at github.com) seems to like packed strcutures,
|
|
||||||
* the author prefers byte offset plus a range of unaligned integer builders
|
|
||||||
* such as those in sg_unaligned.h .
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef __GNUC__
|
|
||||||
#ifndef __clang__
|
|
||||||
struct __attribute__((__packed__)) sg_nvme_user_io
|
|
||||||
#else
|
|
||||||
struct sg_nvme_user_io
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
struct sg_nvme_user_io
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
uint8_t opcode;
|
|
||||||
uint8_t flags;
|
|
||||||
uint16_t control;
|
|
||||||
uint16_t nblocks;
|
|
||||||
uint16_t rsvd;
|
|
||||||
uint64_t metadata;
|
|
||||||
uint64_t addr;
|
|
||||||
uint64_t slba;
|
|
||||||
uint32_t dsmgmt;
|
|
||||||
uint32_t reftag;
|
|
||||||
uint16_t apptag;
|
|
||||||
uint16_t appmask;
|
|
||||||
}
|
|
||||||
#ifdef SG_LIB_FREEBSD
|
|
||||||
__packed;
|
|
||||||
#else
|
|
||||||
;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Using byte offsets and unaligned be/le copies safer than packed
|
|
||||||
* structures. These are for sg_nvme_user_io . */
|
|
||||||
#define SG_NVME_IO_OPCODE 0
|
|
||||||
#define SG_NVME_IO_FLAGS 1
|
|
||||||
#define SG_NVME_IO_CONTROL 2
|
|
||||||
#define SG_NVME_IO_NBLOCKS 4
|
|
||||||
#define SG_NVME_IO_RSVD 6
|
|
||||||
#define SG_NVME_IO_METADATA 8
|
|
||||||
#define SG_NVME_IO_ADDR 16
|
|
||||||
#define SG_NVME_IO_SLBA 24
|
|
||||||
#define SG_NVME_IO_DSMGMT 32
|
|
||||||
#define SG_NVME_IO_REFTAG 36
|
|
||||||
#define SG_NVME_IO_APPTAG 40
|
|
||||||
#define SG_NVME_IO_APPMASK 42
|
|
||||||
|
|
||||||
#ifdef __GNUC__
|
|
||||||
#ifndef __clang__
|
|
||||||
struct __attribute__((__packed__)) sg_nvme_passthru_cmd
|
|
||||||
#else
|
|
||||||
struct sg_nvme_passthru_cmd
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
struct sg_nvme_passthru_cmd
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
uint8_t opcode;
|
|
||||||
uint8_t flags;
|
|
||||||
uint16_t rsvd1;
|
|
||||||
uint32_t nsid;
|
|
||||||
uint32_t cdw2;
|
|
||||||
uint32_t cdw3;
|
|
||||||
uint64_t metadata;
|
|
||||||
uint64_t addr;
|
|
||||||
uint32_t metadata_len;
|
|
||||||
uint32_t data_len;
|
|
||||||
uint32_t cdw10;
|
|
||||||
uint32_t cdw11;
|
|
||||||
uint32_t cdw12;
|
|
||||||
uint32_t cdw13;
|
|
||||||
uint32_t cdw14;
|
|
||||||
uint32_t cdw15;
|
|
||||||
#ifdef SG_LIB_LINUX
|
|
||||||
uint32_t timeout_ms;
|
|
||||||
uint32_t result; /* out: DWord(0) from completion queue */
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#ifdef SG_LIB_FREEBSD
|
|
||||||
__packed;
|
|
||||||
#else
|
|
||||||
;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/* Using byte offsets and unaligned be/le copies safer than packed
|
|
||||||
* structures. These are for sg_nvme_passthru_cmd . */
|
|
||||||
#define SG_NVME_PT_OPCODE 0 /* length: 1 byte */
|
|
||||||
#define SG_NVME_PT_FLAGS 1 /* length: 1 byte */
|
|
||||||
#define SG_NVME_PT_RSVD1 2 /* length: 2 bytes */
|
|
||||||
#define SG_NVME_PT_NSID 4 /* length: 4 bytes */
|
|
||||||
#define SG_NVME_PT_CDW2 8 /* length: 4 bytes */
|
|
||||||
#define SG_NVME_PT_CDW3 12 /* length: 4 bytes */
|
|
||||||
#define SG_NVME_PT_METADATA 16 /* length: 8 bytes */
|
|
||||||
#define SG_NVME_PT_ADDR 24 /* length: 8 bytes */
|
|
||||||
#define SG_NVME_PT_METADATA_LEN 32 /* length: 4 bytes */
|
|
||||||
#define SG_NVME_PT_DATA_LEN 36 /* length: 4 bytes */
|
|
||||||
#define SG_NVME_PT_CDW10 40 /* length: 4 bytes */
|
|
||||||
#define SG_NVME_PT_CDW11 44 /* length: 4 bytes */
|
|
||||||
#define SG_NVME_PT_CDW12 48 /* length: 4 bytes */
|
|
||||||
#define SG_NVME_PT_CDW13 52 /* length: 4 bytes */
|
|
||||||
#define SG_NVME_PT_CDW14 56 /* length: 4 bytes */
|
|
||||||
#define SG_NVME_PT_CDW15 60 /* length: 4 bytes */
|
|
||||||
|
|
||||||
#ifdef SG_LIB_LINUX
|
|
||||||
/* General references state that "all NVMe commands are 64 bytes long". If
|
|
||||||
* so then the following are add-ons by Linux, go to the OS and not the
|
|
||||||
* the NVMe device. */
|
|
||||||
#define SG_NVME_PT_TIMEOUT_MS 64 /* length: 4 bytes */
|
|
||||||
#define SG_NVME_PT_RESULT 68 /* length: 4 bytes */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Byte offset of Result and Status (plus phase bit) in CQ */
|
|
||||||
#define SG_NVME_PT_CQ_RESULT 0 /* CDW0, length: 4 bytes */
|
|
||||||
#define SG_NVME_PT_CQ_DW0 0 /* CDW0, length: 4 bytes */
|
|
||||||
#define SG_NVME_PT_CQ_DW1 4 /* CDW1, length: 4 bytes */
|
|
||||||
#define SG_NVME_PT_CQ_DW2 8 /* CDW2, length: 4 bytes */
|
|
||||||
#define SG_NVME_PT_CQ_DW3 12 /* CDW3, length: 4 bytes */
|
|
||||||
#define SG_NVME_PT_CQ_STATUS_P 14 /* CDW3 31:16, length: 2 bytes */
|
|
||||||
|
|
||||||
|
|
||||||
/* Valid namespace IDs (nsid_s) range from 1 to 0xfffffffe, leaving: */
|
|
||||||
#define SG_NVME_BROADCAST_NSID 0xffffffff /* all namespaces */
|
|
||||||
#define SG_NVME_CTL_NSID 0x0 /* the "controller's" namespace */
|
|
||||||
|
|
||||||
/* Given the NVMe Identify Controller response and optionally the NVMe
|
|
||||||
* Identify Namespace response (NULL otherwise), generate the SCSI VPD
|
|
||||||
* page 0x83 (device identification) descriptor(s) in dop. Return the
|
|
||||||
* number of bytes written which will not exceed max_do_len. Probably use
|
|
||||||
* Peripheral Device Type (pdt) of 0 (disk) for don't know. Transport
|
|
||||||
* protocol (tproto) should be -1 if not known, else SCSI value.
|
|
||||||
* N.B. Does not write total VPD page length into dop[2:3] . */
|
|
||||||
int sg_make_vpd_devid_for_nvme(const uint8_t * nvme_id_ctl_p,
|
|
||||||
const uint8_t * nvme_id_ns_p, int pdt,
|
|
||||||
int tproto, uint8_t * dop, int max_do_len);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* SG_PT_NVME_H */
|
|
@ -1,473 +0,0 @@
|
|||||||
#ifndef SG_PT_WIN32_H
|
|
||||||
#define SG_PT_WIN32_H
|
|
||||||
/*
|
|
||||||
* The information in this file was obtained from scsi-wnt.h by
|
|
||||||
* Richard Stemmer, rs@epost.de . He in turn gives credit to
|
|
||||||
* Jay A. Key (for scsipt.c).
|
|
||||||
* The plscsi program (by Pat LaVarre <p.lavarre@ieee.org>) has
|
|
||||||
* also been used as a reference.
|
|
||||||
* Much of the information in this header can also be obtained
|
|
||||||
* from msdn.microsoft.com .
|
|
||||||
* Updated for cygwin version 1.7.17 changes 20121026
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* WIN32_LEAN_AND_MEAN may be required to prevent inclusion of <winioctl.h> */
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
#include <windows.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define SCSI_MAX_SENSE_LEN 64
|
|
||||||
#define SCSI_MAX_CDB_LEN 16
|
|
||||||
#define SCSI_MAX_INDIRECT_DATA 16384
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
USHORT Length;
|
|
||||||
UCHAR ScsiStatus;
|
|
||||||
UCHAR PathId;
|
|
||||||
UCHAR TargetId;
|
|
||||||
UCHAR Lun;
|
|
||||||
UCHAR CdbLength;
|
|
||||||
UCHAR SenseInfoLength;
|
|
||||||
UCHAR DataIn;
|
|
||||||
ULONG DataTransferLength;
|
|
||||||
ULONG TimeOutValue;
|
|
||||||
ULONG_PTR DataBufferOffset; /* was ULONG; problem in 64 bit */
|
|
||||||
ULONG SenseInfoOffset;
|
|
||||||
UCHAR Cdb[SCSI_MAX_CDB_LEN];
|
|
||||||
} SCSI_PASS_THROUGH, *PSCSI_PASS_THROUGH;
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
USHORT Length;
|
|
||||||
UCHAR ScsiStatus;
|
|
||||||
UCHAR PathId;
|
|
||||||
UCHAR TargetId;
|
|
||||||
UCHAR Lun;
|
|
||||||
UCHAR CdbLength;
|
|
||||||
UCHAR SenseInfoLength;
|
|
||||||
UCHAR DataIn;
|
|
||||||
ULONG DataTransferLength;
|
|
||||||
ULONG TimeOutValue;
|
|
||||||
PVOID DataBuffer;
|
|
||||||
ULONG SenseInfoOffset;
|
|
||||||
UCHAR Cdb[SCSI_MAX_CDB_LEN];
|
|
||||||
} SCSI_PASS_THROUGH_DIRECT, *PSCSI_PASS_THROUGH_DIRECT;
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
SCSI_PASS_THROUGH spt;
|
|
||||||
/* plscsi shows a follow on 16 bytes allowing 32 byte cdb */
|
|
||||||
ULONG Filler;
|
|
||||||
UCHAR ucSenseBuf[SCSI_MAX_SENSE_LEN];
|
|
||||||
UCHAR ucDataBuf[SCSI_MAX_INDIRECT_DATA];
|
|
||||||
} SCSI_PASS_THROUGH_WITH_BUFFERS, *PSCSI_PASS_THROUGH_WITH_BUFFERS;
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
SCSI_PASS_THROUGH_DIRECT spt;
|
|
||||||
ULONG Filler;
|
|
||||||
UCHAR ucSenseBuf[SCSI_MAX_SENSE_LEN];
|
|
||||||
} SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, *PSCSI_PASS_THROUGH_DIRECT_WITH_BUFFER;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
UCHAR NumberOfLogicalUnits;
|
|
||||||
UCHAR InitiatorBusId;
|
|
||||||
ULONG InquiryDataOffset;
|
|
||||||
} SCSI_BUS_DATA, *PSCSI_BUS_DATA;
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
UCHAR NumberOfBusses;
|
|
||||||
SCSI_BUS_DATA BusData[1];
|
|
||||||
} SCSI_ADAPTER_BUS_INFO, *PSCSI_ADAPTER_BUS_INFO;
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
UCHAR PathId;
|
|
||||||
UCHAR TargetId;
|
|
||||||
UCHAR Lun;
|
|
||||||
BOOLEAN DeviceClaimed;
|
|
||||||
ULONG InquiryDataLength;
|
|
||||||
ULONG NextInquiryDataOffset;
|
|
||||||
UCHAR InquiryData[1];
|
|
||||||
} SCSI_INQUIRY_DATA, *PSCSI_INQUIRY_DATA;
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
ULONG Length;
|
|
||||||
UCHAR PortNumber;
|
|
||||||
UCHAR PathId;
|
|
||||||
UCHAR TargetId;
|
|
||||||
UCHAR Lun;
|
|
||||||
} SCSI_ADDRESS, *PSCSI_ADDRESS;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Standard IOCTL define
|
|
||||||
*/
|
|
||||||
#ifndef CTL_CODE
|
|
||||||
#define CTL_CODE(DevType, Function, Method, Access) \
|
|
||||||
(((DevType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* file access values
|
|
||||||
*/
|
|
||||||
#ifndef FILE_ANY_ACCESS
|
|
||||||
#define FILE_ANY_ACCESS 0
|
|
||||||
#endif
|
|
||||||
#ifndef FILE_READ_ACCESS
|
|
||||||
#define FILE_READ_ACCESS 0x0001
|
|
||||||
#endif
|
|
||||||
#ifndef FILE_WRITE_ACCESS
|
|
||||||
#define FILE_WRITE_ACCESS 0x0002
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// IOCTL_STORAGE_QUERY_PROPERTY
|
|
||||||
|
|
||||||
#define FILE_DEVICE_MASS_STORAGE 0x0000002d
|
|
||||||
#define IOCTL_STORAGE_BASE FILE_DEVICE_MASS_STORAGE
|
|
||||||
#define FILE_ANY_ACCESS 0
|
|
||||||
|
|
||||||
// #define METHOD_BUFFERED 0
|
|
||||||
|
|
||||||
#define IOCTL_STORAGE_QUERY_PROPERTY \
|
|
||||||
CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef _DEVIOCTL_
|
|
||||||
typedef enum _STORAGE_BUS_TYPE {
|
|
||||||
BusTypeUnknown = 0x00,
|
|
||||||
BusTypeScsi = 0x01,
|
|
||||||
BusTypeAtapi = 0x02,
|
|
||||||
BusTypeAta = 0x03,
|
|
||||||
BusType1394 = 0x04,
|
|
||||||
BusTypeSsa = 0x05,
|
|
||||||
BusTypeFibre = 0x06,
|
|
||||||
BusTypeUsb = 0x07,
|
|
||||||
BusTypeRAID = 0x08,
|
|
||||||
BusTypeiScsi = 0x09,
|
|
||||||
BusTypeSas = 0x0A,
|
|
||||||
BusTypeSata = 0x0B,
|
|
||||||
BusTypeSd = 0x0C,
|
|
||||||
BusTypeMmc = 0x0D,
|
|
||||||
BusTypeVirtual = 0xE,
|
|
||||||
BusTypeFileBackedVirtual = 0xF,
|
|
||||||
BusTypeSpaces = 0x10,
|
|
||||||
BusTypeNvme = 0x11,
|
|
||||||
BusTypeSCM = 0x12,
|
|
||||||
BusTypeUfs = 0x13,
|
|
||||||
BusTypeMax = 0x14,
|
|
||||||
BusTypeMaxReserved = 0x7F
|
|
||||||
} STORAGE_BUS_TYPE, *PSTORAGE_BUS_TYPE;
|
|
||||||
|
|
||||||
typedef enum _STORAGE_PROTOCOL_TYPE {
|
|
||||||
ProtocolTypeUnknown = 0,
|
|
||||||
ProtocolTypeScsi,
|
|
||||||
ProtocolTypeAta,
|
|
||||||
ProtocolTypeNvme,
|
|
||||||
ProtocolTypeSd
|
|
||||||
} STORAGE_PROTOCOL_TYPE;
|
|
||||||
|
|
||||||
typedef enum _STORAGE_PROTOCOL_NVME_DATA_TYPE {
|
|
||||||
NVMeDataTypeUnknown = 0,
|
|
||||||
NVMeDataTypeIdentify,
|
|
||||||
NVMeDataTypeLogPage,
|
|
||||||
NVMeDataTypeFeature
|
|
||||||
} STORAGE_PROTOCOL_NVME_DATA_TYPE;
|
|
||||||
|
|
||||||
typedef struct _STORAGE_PROTOCOL_SPECIFIC_DATA {
|
|
||||||
STORAGE_PROTOCOL_TYPE ProtocolType;
|
|
||||||
ULONG DataType;
|
|
||||||
ULONG ProtocolDataRequestValue;
|
|
||||||
ULONG ProtocolDataRequestSubValue;
|
|
||||||
ULONG ProtocolDataOffset;
|
|
||||||
ULONG ProtocolDataLength;
|
|
||||||
ULONG FixedProtocolReturnData;
|
|
||||||
ULONG Reserved[3];
|
|
||||||
} STORAGE_PROTOCOL_SPECIFIC_DATA;
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct _STORAGE_DEVICE_DESCRIPTOR {
|
|
||||||
ULONG Version;
|
|
||||||
ULONG Size;
|
|
||||||
UCHAR DeviceType;
|
|
||||||
UCHAR DeviceTypeModifier;
|
|
||||||
BOOLEAN RemovableMedia;
|
|
||||||
BOOLEAN CommandQueueing;
|
|
||||||
ULONG VendorIdOffset; /* 0 if not available */
|
|
||||||
ULONG ProductIdOffset; /* 0 if not available */
|
|
||||||
ULONG ProductRevisionOffset;/* 0 if not available */
|
|
||||||
ULONG SerialNumberOffset; /* -1 if not available ?? */
|
|
||||||
STORAGE_BUS_TYPE BusType;
|
|
||||||
ULONG RawPropertiesLength;
|
|
||||||
UCHAR RawDeviceProperties[1];
|
|
||||||
} STORAGE_DEVICE_DESCRIPTOR, *PSTORAGE_DEVICE_DESCRIPTOR;
|
|
||||||
|
|
||||||
#define STORAGE_PROTOCOL_STRUCTURE_VERSION 0x1
|
|
||||||
|
|
||||||
#define IOCTL_STORAGE_PROTOCOL_COMMAND \
|
|
||||||
CTL_CODE(IOCTL_STORAGE_BASE, 0x04F0, METHOD_BUFFERED, \
|
|
||||||
FILE_READ_ACCESS | FILE_WRITE_ACCESS)
|
|
||||||
|
|
||||||
typedef struct _STORAGE_PROTOCOL_COMMAND {
|
|
||||||
DWORD Version; /* STORAGE_PROTOCOL_STRUCTURE_VERSION */
|
|
||||||
DWORD Length;
|
|
||||||
STORAGE_PROTOCOL_TYPE ProtocolType;
|
|
||||||
DWORD Flags;
|
|
||||||
DWORD ReturnStatus;
|
|
||||||
DWORD ErrorCode;
|
|
||||||
DWORD CommandLength;
|
|
||||||
DWORD ErrorInfoLength;
|
|
||||||
DWORD DataToDeviceTransferLength;
|
|
||||||
DWORD DataFromDeviceTransferLength;
|
|
||||||
DWORD TimeOutValue;
|
|
||||||
DWORD ErrorInfoOffset;
|
|
||||||
DWORD DataToDeviceBufferOffset;
|
|
||||||
DWORD DataFromDeviceBufferOffset;
|
|
||||||
DWORD CommandSpecific;
|
|
||||||
DWORD Reserved0;
|
|
||||||
DWORD FixedProtocolReturnData;
|
|
||||||
DWORD Reserved1[3];
|
|
||||||
BYTE Command[1]; /* has CommandLength elements */
|
|
||||||
} STORAGE_PROTOCOL_COMMAND, *PSTORAGE_PROTOCOL_COMMAND;
|
|
||||||
|
|
||||||
#endif /* _DEVIOCTL_ */
|
|
||||||
|
|
||||||
typedef struct _STORAGE_DEVICE_UNIQUE_IDENTIFIER {
|
|
||||||
ULONG Version;
|
|
||||||
ULONG Size;
|
|
||||||
ULONG StorageDeviceIdOffset;
|
|
||||||
ULONG StorageDeviceOffset;
|
|
||||||
ULONG DriveLayoutSignatureOffset;
|
|
||||||
} STORAGE_DEVICE_UNIQUE_IDENTIFIER, *PSTORAGE_DEVICE_UNIQUE_IDENTIFIER;
|
|
||||||
|
|
||||||
// Use CompareStorageDuids(PSTORAGE_DEVICE_UNIQUE_IDENTIFIER duid1, duid2)
|
|
||||||
// to test for equality
|
|
||||||
|
|
||||||
#ifndef _DEVIOCTL_
|
|
||||||
typedef enum _STORAGE_QUERY_TYPE {
|
|
||||||
PropertyStandardQuery = 0,
|
|
||||||
PropertyExistsQuery,
|
|
||||||
PropertyMaskQuery,
|
|
||||||
PropertyQueryMaxDefined
|
|
||||||
} STORAGE_QUERY_TYPE, *PSTORAGE_QUERY_TYPE;
|
|
||||||
|
|
||||||
typedef enum _STORAGE_PROPERTY_ID {
|
|
||||||
StorageDeviceProperty = 0,
|
|
||||||
StorageAdapterProperty,
|
|
||||||
StorageDeviceIdProperty,
|
|
||||||
StorageDeviceUniqueIdProperty,
|
|
||||||
StorageDeviceWriteCacheProperty,
|
|
||||||
StorageMiniportProperty,
|
|
||||||
StorageAccessAlignmentProperty,
|
|
||||||
/* Identify controller goes to adapter; Identify namespace to device */
|
|
||||||
StorageAdapterProtocolSpecificProperty = 49,
|
|
||||||
StorageDeviceProtocolSpecificProperty = 50
|
|
||||||
} STORAGE_PROPERTY_ID, *PSTORAGE_PROPERTY_ID;
|
|
||||||
|
|
||||||
typedef struct _STORAGE_PROPERTY_QUERY {
|
|
||||||
STORAGE_PROPERTY_ID PropertyId;
|
|
||||||
STORAGE_QUERY_TYPE QueryType;
|
|
||||||
UCHAR AdditionalParameters[1];
|
|
||||||
} STORAGE_PROPERTY_QUERY, *PSTORAGE_PROPERTY_QUERY;
|
|
||||||
|
|
||||||
typedef struct _STORAGE_PROTOCOL_DATA_DESCRIPTOR {
|
|
||||||
DWORD Version;
|
|
||||||
DWORD Size;
|
|
||||||
STORAGE_PROTOCOL_SPECIFIC_DATA ProtocolSpecificData;
|
|
||||||
} STORAGE_PROTOCOL_DATA_DESCRIPTOR, *PSTORAGE_PROTOCOL_DATA_DESCRIPTOR;
|
|
||||||
|
|
||||||
// Command completion status
|
|
||||||
// The "Phase Tag" field and "Status Field" are separated in spec. We define
|
|
||||||
// them in the same data structure to ease the memory access from software.
|
|
||||||
//
|
|
||||||
typedef union {
|
|
||||||
struct {
|
|
||||||
USHORT P : 1; // Phase Tag (P)
|
|
||||||
|
|
||||||
USHORT SC : 8; // Status Code (SC)
|
|
||||||
USHORT SCT : 3; // Status Code Type (SCT)
|
|
||||||
USHORT Reserved : 2;
|
|
||||||
USHORT M : 1; // More (M)
|
|
||||||
USHORT DNR : 1; // Do Not Retry (DNR)
|
|
||||||
} DUMMYSTRUCTNAME;
|
|
||||||
USHORT AsUshort;
|
|
||||||
} NVME_COMMAND_STATUS, *PNVME_COMMAND_STATUS;
|
|
||||||
|
|
||||||
// Information of log: NVME_LOG_PAGE_ERROR_INFO. Size: 64 bytes
|
|
||||||
//
|
|
||||||
typedef struct {
|
|
||||||
ULONGLONG ErrorCount;
|
|
||||||
USHORT SQID; // Submission Queue ID
|
|
||||||
USHORT CMDID; // Command ID
|
|
||||||
NVME_COMMAND_STATUS Status; // Status Field: This field indicates the
|
|
||||||
// Status Field for the command that
|
|
||||||
// completed. The Status Field is located in
|
|
||||||
// bits 15:01, bit 00 corresponds to the Phase
|
|
||||||
// Tag posted for the command.
|
|
||||||
struct {
|
|
||||||
USHORT Byte : 8; // Byte in command that contained error
|
|
||||||
USHORT Bit : 3; // Bit in command that contained error
|
|
||||||
USHORT Reserved : 5;
|
|
||||||
} ParameterErrorLocation;
|
|
||||||
|
|
||||||
ULONGLONG Lba; // LBA: This field indicates the first LBA
|
|
||||||
// that experienced the error condition, if
|
|
||||||
// applicable.
|
|
||||||
ULONG NameSpace; // Namespace: This field indicates the nsid
|
|
||||||
// that the error is associated with, if
|
|
||||||
// applicable.
|
|
||||||
UCHAR VendorInfoAvailable; // Vendor Specific Information Available
|
|
||||||
UCHAR Reserved0[3];
|
|
||||||
ULONGLONG CommandSpecificInfo; // This field contains command specific
|
|
||||||
// information. If used, the command
|
|
||||||
// definition specifies the information
|
|
||||||
// returned.
|
|
||||||
UCHAR Reserved1[24];
|
|
||||||
} NVME_ERROR_INFO_LOG, *PNVME_ERROR_INFO_LOG;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
|
|
||||||
ULONG DW0;
|
|
||||||
ULONG Reserved;
|
|
||||||
|
|
||||||
union {
|
|
||||||
struct {
|
|
||||||
USHORT SQHD; // SQ Head Pointer (SQHD)
|
|
||||||
USHORT SQID; // SQ Identifier (SQID)
|
|
||||||
} DUMMYSTRUCTNAME;
|
|
||||||
|
|
||||||
ULONG AsUlong;
|
|
||||||
} DW2;
|
|
||||||
|
|
||||||
union {
|
|
||||||
struct {
|
|
||||||
USHORT CID; // Command Identifier (CID)
|
|
||||||
NVME_COMMAND_STATUS Status;
|
|
||||||
} DUMMYSTRUCTNAME;
|
|
||||||
|
|
||||||
ULONG AsUlong;
|
|
||||||
} DW3;
|
|
||||||
|
|
||||||
} NVME_COMPLETION_ENTRY, *PNVME_COMPLETION_ENTRY;
|
|
||||||
|
|
||||||
|
|
||||||
// Bit-mask values for STORAGE_PROTOCOL_COMMAND - "Flags" field.
|
|
||||||
//
|
|
||||||
// Flag indicates the request targeting to adapter instead of device.
|
|
||||||
#define STORAGE_PROTOCOL_COMMAND_FLAG_ADAPTER_REQUEST 0x80000000
|
|
||||||
|
|
||||||
//
|
|
||||||
// Status values for STORAGE_PROTOCOL_COMMAND - "ReturnStatus" field.
|
|
||||||
//
|
|
||||||
#define STORAGE_PROTOCOL_STATUS_PENDING 0x0
|
|
||||||
#define STORAGE_PROTOCOL_STATUS_SUCCESS 0x1
|
|
||||||
#define STORAGE_PROTOCOL_STATUS_ERROR 0x2
|
|
||||||
#define STORAGE_PROTOCOL_STATUS_INVALID_REQUEST 0x3
|
|
||||||
#define STORAGE_PROTOCOL_STATUS_NO_DEVICE 0x4
|
|
||||||
#define STORAGE_PROTOCOL_STATUS_BUSY 0x5
|
|
||||||
#define STORAGE_PROTOCOL_STATUS_DATA_OVERRUN 0x6
|
|
||||||
#define STORAGE_PROTOCOL_STATUS_INSUFFICIENT_RESOURCES 0x7
|
|
||||||
|
|
||||||
#define STORAGE_PROTOCOL_STATUS_NOT_SUPPORTED 0xFF
|
|
||||||
|
|
||||||
// Command Length for Storage Protocols.
|
|
||||||
//
|
|
||||||
// NVMe commands are always 64 bytes.
|
|
||||||
#define STORAGE_PROTOCOL_COMMAND_LENGTH_NVME 0x40
|
|
||||||
|
|
||||||
// Command Specific Information for Storage Protocols - CommandSpecific field
|
|
||||||
//
|
|
||||||
#define STORAGE_PROTOCOL_SPECIFIC_NVME_ADMIN_COMMAND 0x01
|
|
||||||
#define STORAGE_PROTOCOL_SPECIFIC_NVME_NVM_COMMAND 0x02
|
|
||||||
|
|
||||||
#endif /* _DEVIOCTL_ */
|
|
||||||
|
|
||||||
|
|
||||||
// NVME_PASS_THROUGH
|
|
||||||
|
|
||||||
#ifndef STB_IO_CONTROL
|
|
||||||
typedef struct _SRB_IO_CONTROL {
|
|
||||||
ULONG HeaderLength;
|
|
||||||
UCHAR Signature[8];
|
|
||||||
ULONG Timeout;
|
|
||||||
ULONG ControlCode;
|
|
||||||
ULONG ReturnCode;
|
|
||||||
ULONG Length;
|
|
||||||
} SRB_IO_CONTROL, *PSRB_IO_CONTROL;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef NVME_PASS_THROUGH_SRB_IO_CODE
|
|
||||||
|
|
||||||
#define NVME_SIG_STR "NvmeMini"
|
|
||||||
#define NVME_STORPORT_DRIVER 0xe000
|
|
||||||
|
|
||||||
#define NVME_PASS_THROUGH_SRB_IO_CODE \
|
|
||||||
CTL_CODE(NVME_STORPORT_DRIVER, 0x0800, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
|
||||||
|
|
||||||
#pragma pack(1)
|
|
||||||
|
|
||||||
/* Following is pre-Win10; used with DeviceIoControl(IOCTL_SCSI_MINIPORT),
|
|
||||||
* in Win10 need DeviceIoControl(IOCTL_STORAGE_PROTOCOL_COMMAND) for pure
|
|
||||||
* pass-through. Win10 also has "Protocol specific queries" for things like
|
|
||||||
* Identify and Get feature. */
|
|
||||||
typedef struct _NVME_PASS_THROUGH_IOCTL
|
|
||||||
{
|
|
||||||
SRB_IO_CONTROL SrbIoCtrl;
|
|
||||||
ULONG VendorSpecific[6];
|
|
||||||
ULONG NVMeCmd[16]; /* Command DW[0...15] */
|
|
||||||
ULONG CplEntry[4]; /* Completion DW[0...3] */
|
|
||||||
ULONG Direction; /* 0=None, 1=Out, 2=In, 3=I/O */
|
|
||||||
ULONG QueueId; /* 0=AdminQ */
|
|
||||||
ULONG DataBufferLen; /* sizeof(DataBuffer) if Data In */
|
|
||||||
ULONG MetaDataLen;
|
|
||||||
ULONG ReturnBufferLen; /* offsetof(DataBuffer), plus
|
|
||||||
* sizeof(DataBuffer) if Data Out */
|
|
||||||
UCHAR DataBuffer[1];
|
|
||||||
} NVME_PASS_THROUGH_IOCTL;
|
|
||||||
#pragma pack()
|
|
||||||
|
|
||||||
#endif // NVME_PASS_THROUGH_SRB_IO_CODE
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* method codes
|
|
||||||
*/
|
|
||||||
#define METHOD_BUFFERED 0
|
|
||||||
#define METHOD_IN_DIRECT 1
|
|
||||||
#define METHOD_OUT_DIRECT 2
|
|
||||||
#define METHOD_NEITHER 3
|
|
||||||
|
|
||||||
|
|
||||||
#define IOCTL_SCSI_BASE 0x00000004
|
|
||||||
|
|
||||||
/*
|
|
||||||
* constants for DataIn member of SCSI_PASS_THROUGH* structures
|
|
||||||
*/
|
|
||||||
#define SCSI_IOCTL_DATA_OUT 0
|
|
||||||
#define SCSI_IOCTL_DATA_IN 1
|
|
||||||
#define SCSI_IOCTL_DATA_UNSPECIFIED 2
|
|
||||||
|
|
||||||
#define IOCTL_SCSI_PASS_THROUGH CTL_CODE(IOCTL_SCSI_BASE, 0x0401, \
|
|
||||||
METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
|
|
||||||
#define IOCTL_SCSI_MINIPORT CTL_CODE(IOCTL_SCSI_BASE, 0x0402, \
|
|
||||||
METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
|
|
||||||
#define IOCTL_SCSI_GET_INQUIRY_DATA CTL_CODE(IOCTL_SCSI_BASE, 0x0403, \
|
|
||||||
METHOD_BUFFERED, FILE_ANY_ACCESS)
|
|
||||||
#define IOCTL_SCSI_GET_CAPABILITIES CTL_CODE(IOCTL_SCSI_BASE, 0x0404, \
|
|
||||||
METHOD_BUFFERED, FILE_ANY_ACCESS)
|
|
||||||
#define IOCTL_SCSI_PASS_THROUGH_DIRECT CTL_CODE(IOCTL_SCSI_BASE, 0x0405, \
|
|
||||||
METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
|
|
||||||
#define IOCTL_SCSI_GET_ADDRESS CTL_CODE(IOCTL_SCSI_BASE, 0x0406, \
|
|
||||||
METHOD_BUFFERED, FILE_ANY_ACCESS)
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,325 +0,0 @@
|
|||||||
#ifndef SG_UNALIGNED_H
|
|
||||||
#define SG_UNALIGNED_H
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (c) 2014-2017 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Borrowed from the Linux kernel, via mhvtl */
|
|
||||||
|
|
||||||
/* In the first section below, functions that copy unsigned integers in a
|
|
||||||
* computer's native format, to and from an unaligned big endian sequence of
|
|
||||||
* bytes. Big endian byte format "on the wire" is the default used by SCSI
|
|
||||||
* standards (www.t10.org). Big endian is also the network byte order. */
|
|
||||||
|
|
||||||
static inline uint16_t __get_unaligned_be16(const uint8_t *p)
|
|
||||||
{
|
|
||||||
return p[0] << 8 | p[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint32_t __get_unaligned_be32(const uint8_t *p)
|
|
||||||
{
|
|
||||||
return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Assume 48 bit value placed in uint64_t */
|
|
||||||
static inline uint64_t __get_unaligned_be48(const uint8_t *p)
|
|
||||||
{
|
|
||||||
return (uint64_t)__get_unaligned_be16(p) << 32 |
|
|
||||||
__get_unaligned_be32(p + 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint64_t __get_unaligned_be64(const uint8_t *p)
|
|
||||||
{
|
|
||||||
return (uint64_t)__get_unaligned_be32(p) << 32 |
|
|
||||||
__get_unaligned_be32(p + 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void __put_unaligned_be16(uint16_t val, uint8_t *p)
|
|
||||||
{
|
|
||||||
*p++ = val >> 8;
|
|
||||||
*p++ = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void __put_unaligned_be32(uint32_t val, uint8_t *p)
|
|
||||||
{
|
|
||||||
__put_unaligned_be16(val >> 16, p);
|
|
||||||
__put_unaligned_be16(val, p + 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Assume 48 bit value placed in uint64_t */
|
|
||||||
static inline void __put_unaligned_be48(uint64_t val, uint8_t *p)
|
|
||||||
{
|
|
||||||
__put_unaligned_be16(val >> 32, p);
|
|
||||||
__put_unaligned_be32(val, p + 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void __put_unaligned_be64(uint64_t val, uint8_t *p)
|
|
||||||
{
|
|
||||||
__put_unaligned_be32(val >> 32, p);
|
|
||||||
__put_unaligned_be32(val, p + 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint16_t sg_get_unaligned_be16(const void *p)
|
|
||||||
{
|
|
||||||
return __get_unaligned_be16((const uint8_t *)p);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint32_t sg_get_unaligned_be24(const void *p)
|
|
||||||
{
|
|
||||||
return ((const uint8_t *)p)[0] << 16 | ((const uint8_t *)p)[1] << 8 |
|
|
||||||
((const uint8_t *)p)[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint32_t sg_get_unaligned_be32(const void *p)
|
|
||||||
{
|
|
||||||
return __get_unaligned_be32((const uint8_t *)p);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Assume 48 bit value placed in uint64_t */
|
|
||||||
static inline uint64_t sg_get_unaligned_be48(const void *p)
|
|
||||||
{
|
|
||||||
return __get_unaligned_be48((const uint8_t *)p);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint64_t sg_get_unaligned_be64(const void *p)
|
|
||||||
{
|
|
||||||
return __get_unaligned_be64((const uint8_t *)p);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns 0 if 'num_bytes' is less than or equal to 0 or greater than
|
|
||||||
* 8 (i.e. sizeof(uint64_t)). Else returns result in uint64_t which is
|
|
||||||
* an 8 byte unsigned integer. */
|
|
||||||
static inline uint64_t sg_get_unaligned_be(int num_bytes, const void *p)
|
|
||||||
{
|
|
||||||
if ((num_bytes <= 0) || (num_bytes > (int)sizeof(uint64_t)))
|
|
||||||
return 0;
|
|
||||||
else {
|
|
||||||
const uint8_t * xp = (const uint8_t *)p;
|
|
||||||
uint64_t res = *xp;
|
|
||||||
|
|
||||||
for (++xp; num_bytes > 1; ++xp, --num_bytes)
|
|
||||||
res = (res << 8) | *xp;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void sg_put_unaligned_be16(uint16_t val, void *p)
|
|
||||||
{
|
|
||||||
__put_unaligned_be16(val, (uint8_t *)p);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void sg_put_unaligned_be24(uint32_t val, void *p)
|
|
||||||
{
|
|
||||||
((uint8_t *)p)[0] = (val >> 16) & 0xff;
|
|
||||||
((uint8_t *)p)[1] = (val >> 8) & 0xff;
|
|
||||||
((uint8_t *)p)[2] = val & 0xff;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void sg_put_unaligned_be32(uint32_t val, void *p)
|
|
||||||
{
|
|
||||||
__put_unaligned_be32(val, (uint8_t *)p);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Assume 48 bit value placed in uint64_t */
|
|
||||||
static inline void sg_put_unaligned_be48(uint64_t val, void *p)
|
|
||||||
{
|
|
||||||
__put_unaligned_be48(val, (uint8_t *)p);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void sg_put_unaligned_be64(uint64_t val, void *p)
|
|
||||||
{
|
|
||||||
__put_unaligned_be64(val, (uint8_t *)p);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Since cdb and parameter blocks are often memset to zero before these
|
|
||||||
* unaligned function partially fill them, then check for a val of zero
|
|
||||||
* and ignore if it is with these variants. */
|
|
||||||
static inline void sg_nz_put_unaligned_be16(uint16_t val, void *p)
|
|
||||||
{
|
|
||||||
if (val)
|
|
||||||
__put_unaligned_be16(val, (uint8_t *)p);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void sg_nz_put_unaligned_be24(uint32_t val, void *p)
|
|
||||||
{
|
|
||||||
if (val) {
|
|
||||||
((uint8_t *)p)[0] = (val >> 16) & 0xff;
|
|
||||||
((uint8_t *)p)[1] = (val >> 8) & 0xff;
|
|
||||||
((uint8_t *)p)[2] = val & 0xff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void sg_nz_put_unaligned_be32(uint32_t val, void *p)
|
|
||||||
{
|
|
||||||
if (val)
|
|
||||||
__put_unaligned_be32(val, (uint8_t *)p);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void sg_nz_put_unaligned_be64(uint64_t val, void *p)
|
|
||||||
{
|
|
||||||
if (val)
|
|
||||||
__put_unaligned_be64(val, (uint8_t *)p);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Below are the little endian equivalents of the big endian functions
|
|
||||||
* above. Little endian is used by ATA, PCI and NVMe.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static inline uint16_t __get_unaligned_le16(const uint8_t *p)
|
|
||||||
{
|
|
||||||
return p[1] << 8 | p[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint32_t __get_unaligned_le32(const uint8_t *p)
|
|
||||||
{
|
|
||||||
return p[3] << 24 | p[2] << 16 | p[1] << 8 | p[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint64_t __get_unaligned_le64(const uint8_t *p)
|
|
||||||
{
|
|
||||||
return (uint64_t)__get_unaligned_le32(p + 4) << 32 |
|
|
||||||
__get_unaligned_le32(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void __put_unaligned_le16(uint16_t val, uint8_t *p)
|
|
||||||
{
|
|
||||||
*p++ = val;
|
|
||||||
*p++ = val >> 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void __put_unaligned_le32(uint32_t val, uint8_t *p)
|
|
||||||
{
|
|
||||||
__put_unaligned_le16(val >> 16, p + 2);
|
|
||||||
__put_unaligned_le16(val, p);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void __put_unaligned_le64(uint64_t val, uint8_t *p)
|
|
||||||
{
|
|
||||||
__put_unaligned_le32(val >> 32, p + 4);
|
|
||||||
__put_unaligned_le32(val, p);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint16_t sg_get_unaligned_le16(const void *p)
|
|
||||||
{
|
|
||||||
return __get_unaligned_le16((const uint8_t *)p);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint32_t sg_get_unaligned_le24(const void *p)
|
|
||||||
{
|
|
||||||
return (uint32_t)__get_unaligned_le16((const uint8_t *)p) |
|
|
||||||
((const uint8_t *)p)[2] << 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint32_t sg_get_unaligned_le32(const void *p)
|
|
||||||
{
|
|
||||||
return __get_unaligned_le32((const uint8_t *)p);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Assume 48 bit value placed in uint64_t */
|
|
||||||
static inline uint64_t sg_get_unaligned_le48(const void *p)
|
|
||||||
{
|
|
||||||
return (uint64_t)__get_unaligned_le16((const uint8_t *)p + 4) << 32 |
|
|
||||||
__get_unaligned_le32((const uint8_t *)p);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint64_t sg_get_unaligned_le64(const void *p)
|
|
||||||
{
|
|
||||||
return __get_unaligned_le64((const uint8_t *)p);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns 0 if 'num_bytes' is less than or equal to 0 or greater than
|
|
||||||
* 8 (i.e. sizeof(uint64_t)). Else returns result in uint64_t which is
|
|
||||||
* an 8 byte unsigned integer. */
|
|
||||||
static inline uint64_t sg_get_unaligned_le(int num_bytes, const void *p)
|
|
||||||
{
|
|
||||||
if ((num_bytes <= 0) || (num_bytes > (int)sizeof(uint64_t)))
|
|
||||||
return 0;
|
|
||||||
else {
|
|
||||||
const uint8_t * xp = (const uint8_t *)p + (num_bytes - 1);
|
|
||||||
uint64_t res = *xp;
|
|
||||||
|
|
||||||
for (--xp; num_bytes > 1; --xp, --num_bytes)
|
|
||||||
res = (res << 8) | *xp;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void sg_put_unaligned_le16(uint16_t val, void *p)
|
|
||||||
{
|
|
||||||
__put_unaligned_le16(val, (uint8_t *)p);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void sg_put_unaligned_le24(uint32_t val, void *p)
|
|
||||||
{
|
|
||||||
((uint8_t *)p)[2] = (val >> 16) & 0xff;
|
|
||||||
((uint8_t *)p)[1] = (val >> 8) & 0xff;
|
|
||||||
((uint8_t *)p)[0] = val & 0xff;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void sg_put_unaligned_le32(uint32_t val, void *p)
|
|
||||||
{
|
|
||||||
__put_unaligned_le32(val, (uint8_t *)p);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Assume 48 bit value placed in uint64_t */
|
|
||||||
static inline void sg_put_unaligned_le48(uint64_t val, void *p)
|
|
||||||
{
|
|
||||||
((uint8_t *)p)[5] = (val >> 40) & 0xff;
|
|
||||||
((uint8_t *)p)[4] = (val >> 32) & 0xff;
|
|
||||||
((uint8_t *)p)[3] = (val >> 24) & 0xff;
|
|
||||||
((uint8_t *)p)[2] = (val >> 16) & 0xff;
|
|
||||||
((uint8_t *)p)[1] = (val >> 8) & 0xff;
|
|
||||||
((uint8_t *)p)[0] = val & 0xff;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void sg_put_unaligned_le64(uint64_t val, void *p)
|
|
||||||
{
|
|
||||||
__put_unaligned_le64(val, (uint8_t *)p);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Since cdb and parameter blocks are often memset to zero before these
|
|
||||||
* unaligned function partially fill them, then check for a val of zero
|
|
||||||
* and ignore if it is with these variants. */
|
|
||||||
static inline void sg_nz_put_unaligned_le16(uint16_t val, void *p)
|
|
||||||
{
|
|
||||||
if (val)
|
|
||||||
__put_unaligned_le16(val, (uint8_t *)p);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void sg_nz_put_unaligned_le24(uint32_t val, void *p)
|
|
||||||
{
|
|
||||||
if (val) {
|
|
||||||
((uint8_t *)p)[2] = (val >> 16) & 0xff;
|
|
||||||
((uint8_t *)p)[1] = (val >> 8) & 0xff;
|
|
||||||
((uint8_t *)p)[0] = val & 0xff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void sg_nz_put_unaligned_le32(uint32_t val, void *p)
|
|
||||||
{
|
|
||||||
if (val)
|
|
||||||
__put_unaligned_le32(val, (uint8_t *)p);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void sg_nz_put_unaligned_le64(uint64_t val, void *p)
|
|
||||||
{
|
|
||||||
if (val)
|
|
||||||
__put_unaligned_le64(val, (uint8_t *)p);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* SG_UNALIGNED_H */
|
|
@ -1,663 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 1999-2018 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* CONTENTS
|
|
||||||
* Some SCSI commands are executed in many contexts and hence began
|
|
||||||
* to appear in several sg3_utils utilities. This files centralizes
|
|
||||||
* some of the low level command execution code. In most cases the
|
|
||||||
* interpretation of the command response is left to the each
|
|
||||||
* utility.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "sg_lib.h"
|
|
||||||
#include "sg_cmds_basic.h"
|
|
||||||
#include "sg_pt.h"
|
|
||||||
#include "sg_unaligned.h"
|
|
||||||
|
|
||||||
/* Needs to be after config.h */
|
|
||||||
#ifdef SG_LIB_LINUX
|
|
||||||
#include <errno.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
static const char * const version_str = "1.83 20180204";
|
|
||||||
|
|
||||||
|
|
||||||
#define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */
|
|
||||||
#define EBUFF_SZ 256
|
|
||||||
|
|
||||||
#define DEF_PT_TIMEOUT 60 /* 60 seconds */
|
|
||||||
#define START_PT_TIMEOUT 120 /* 120 seconds == 2 minutes */
|
|
||||||
#define LONG_PT_TIMEOUT 7200 /* 7,200 seconds == 120 minutes */
|
|
||||||
|
|
||||||
#define INQUIRY_CMD 0x12
|
|
||||||
#define INQUIRY_CMDLEN 6
|
|
||||||
#define REQUEST_SENSE_CMD 0x3
|
|
||||||
#define REQUEST_SENSE_CMDLEN 6
|
|
||||||
#define REPORT_LUNS_CMD 0xa0
|
|
||||||
#define REPORT_LUNS_CMDLEN 12
|
|
||||||
#define TUR_CMD 0x0
|
|
||||||
#define TUR_CMDLEN 6
|
|
||||||
|
|
||||||
#define SAFE_STD_INQ_RESP_LEN 36 /* other lengths lock up some devices */
|
|
||||||
|
|
||||||
|
|
||||||
const char *
|
|
||||||
sg_cmds_version()
|
|
||||||
{
|
|
||||||
return version_str;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(__GNUC__) || defined(__clang__)
|
|
||||||
static int pr2ws(const char * fmt, ...)
|
|
||||||
__attribute__ ((format (printf, 1, 2)));
|
|
||||||
#else
|
|
||||||
static int pr2ws(const char * fmt, ...);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
static int
|
|
||||||
pr2ws(const char * fmt, ...)
|
|
||||||
{
|
|
||||||
va_list args;
|
|
||||||
int n;
|
|
||||||
|
|
||||||
va_start(args, fmt);
|
|
||||||
n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args);
|
|
||||||
va_end(args);
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns file descriptor >= 0 if successful. If error in Unix returns
|
|
||||||
negated errno. */
|
|
||||||
int
|
|
||||||
sg_cmds_open_device(const char * device_name, bool read_only, int verbose)
|
|
||||||
{
|
|
||||||
/* The following 2 lines are temporary. It is to avoid a NULL pointer
|
|
||||||
* crash when an old utility is used with a newer library built after
|
|
||||||
* the sg_warnings_strm cleanup */
|
|
||||||
if (NULL == sg_warnings_strm)
|
|
||||||
sg_warnings_strm = stderr;
|
|
||||||
|
|
||||||
return scsi_pt_open_device(device_name, read_only, verbose);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns file descriptor >= 0 if successful. If error in Unix returns
|
|
||||||
negated errno. */
|
|
||||||
int
|
|
||||||
sg_cmds_open_flags(const char * device_name, int flags, int verbose)
|
|
||||||
{
|
|
||||||
return scsi_pt_open_flags(device_name, flags, verbose);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns 0 if successful. If error in Unix returns negated errno. */
|
|
||||||
int
|
|
||||||
sg_cmds_close_device(int device_fd)
|
|
||||||
{
|
|
||||||
return scsi_pt_close_device(device_fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char * const pass_through_s = "pass-through";
|
|
||||||
|
|
||||||
static int
|
|
||||||
sg_cmds_process_helper(const char * leadin, int mx_di_len, int resid,
|
|
||||||
const unsigned char * sbp, int slen, bool noisy,
|
|
||||||
int verbose, int * o_sense_cat)
|
|
||||||
{
|
|
||||||
int scat, got;
|
|
||||||
bool n = false;
|
|
||||||
bool check_data_in = false;
|
|
||||||
char b[512];
|
|
||||||
|
|
||||||
scat = sg_err_category_sense(sbp, slen);
|
|
||||||
switch (scat) {
|
|
||||||
case SG_LIB_CAT_NOT_READY:
|
|
||||||
case SG_LIB_CAT_INVALID_OP:
|
|
||||||
case SG_LIB_CAT_ILLEGAL_REQ:
|
|
||||||
case SG_LIB_CAT_ABORTED_COMMAND:
|
|
||||||
case SG_LIB_CAT_COPY_ABORTED:
|
|
||||||
case SG_LIB_CAT_DATA_PROTECT:
|
|
||||||
case SG_LIB_CAT_PROTECTION:
|
|
||||||
case SG_LIB_CAT_NO_SENSE:
|
|
||||||
case SG_LIB_CAT_MISCOMPARE:
|
|
||||||
n = false;
|
|
||||||
break;
|
|
||||||
case SG_LIB_CAT_RECOVERED:
|
|
||||||
case SG_LIB_CAT_MEDIUM_HARD:
|
|
||||||
check_data_in = true;
|
|
||||||
#if defined(__GNUC__)
|
|
||||||
#if (__GNUC__ >= 7)
|
|
||||||
__attribute__((fallthrough));
|
|
||||||
/* FALL THROUGH */
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
case SG_LIB_CAT_UNIT_ATTENTION:
|
|
||||||
case SG_LIB_CAT_SENSE:
|
|
||||||
default:
|
|
||||||
n = noisy;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (verbose || n) {
|
|
||||||
if (leadin && (strlen(leadin) > 0))
|
|
||||||
pr2ws("%s:\n", leadin);
|
|
||||||
sg_get_sense_str(NULL, sbp, slen, (verbose > 1),
|
|
||||||
sizeof(b), b);
|
|
||||||
pr2ws("%s", b);
|
|
||||||
if ((mx_di_len > 0) && (resid > 0)) {
|
|
||||||
got = mx_di_len - resid;
|
|
||||||
if ((verbose > 2) || check_data_in || (got > 0))
|
|
||||||
pr2ws(" %s requested %d bytes (data-in) but got %d "
|
|
||||||
"bytes\n", pass_through_s, mx_di_len, got);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (o_sense_cat)
|
|
||||||
*o_sense_cat = scat;
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This is a helper function used by sg_cmds_* implementations after the
|
|
||||||
* call to the pass-through. pt_res is returned from do_scsi_pt(). If valid
|
|
||||||
* sense data is found it is decoded and output to sg_warnings_strm (def:
|
|
||||||
* stderr); depending on the 'noisy' and 'verbose' settings. Returns -2 for
|
|
||||||
* "sense" category (may not be fatal), -1 for failed, 0, or a positive
|
|
||||||
* number. If 'mx_di_len > 0' then asks pass-through for resid and returns
|
|
||||||
* (mx_di_len - resid); otherwise returns 0. So for data-in it should return
|
|
||||||
* the actual number of bytes received. For data-out (to device) or no data
|
|
||||||
* call with 'mx_di_len' set to 0 or less. If -2 returned then sense category
|
|
||||||
* output via 'o_sense_cat' pointer (if not NULL). Note that several sense
|
|
||||||
* categories also have data in bytes received; -2 is still returned. */
|
|
||||||
int
|
|
||||||
sg_cmds_process_resp(struct sg_pt_base * ptvp, const char * leadin,
|
|
||||||
int pt_res, int mx_di_len, const unsigned char * sbp,
|
|
||||||
bool noisy, int verbose, int * o_sense_cat)
|
|
||||||
{
|
|
||||||
int got, cat, duration, slen, resid, resp_code, sstat;
|
|
||||||
bool transport_sense;
|
|
||||||
char b[1024];
|
|
||||||
|
|
||||||
if (NULL == leadin)
|
|
||||||
leadin = "";
|
|
||||||
if (pt_res < 0) {
|
|
||||||
#ifdef SG_LIB_LINUX
|
|
||||||
if (verbose)
|
|
||||||
pr2ws("%s: %s os error: %s\n", leadin, pass_through_s,
|
|
||||||
safe_strerror(-pt_res));
|
|
||||||
if ((-ENXIO == pt_res) && o_sense_cat) {
|
|
||||||
if (verbose > 2)
|
|
||||||
pr2ws("map ENXIO to SG_LIB_CAT_NOT_READY\n");
|
|
||||||
*o_sense_cat = SG_LIB_CAT_NOT_READY;
|
|
||||||
return -2;
|
|
||||||
} else if (noisy && (0 == verbose))
|
|
||||||
pr2ws("%s: %s os error: %s\n", leadin, pass_through_s,
|
|
||||||
safe_strerror(-pt_res));
|
|
||||||
#else
|
|
||||||
if (noisy || verbose)
|
|
||||||
pr2ws("%s: %s os error: %s\n", leadin, pass_through_s,
|
|
||||||
safe_strerror(-pt_res));
|
|
||||||
#endif
|
|
||||||
return -1;
|
|
||||||
} else if (SCSI_PT_DO_BAD_PARAMS == pt_res) {
|
|
||||||
pr2ws("%s: bad %s setup\n", leadin, pass_through_s);
|
|
||||||
return -1;
|
|
||||||
} else if (SCSI_PT_DO_TIMEOUT == pt_res) {
|
|
||||||
pr2ws("%s: %s timeout\n", leadin, pass_through_s);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if ((verbose > 2) && ((duration = get_scsi_pt_duration_ms(ptvp)) >= 0))
|
|
||||||
pr2ws(" duration=%d ms\n", duration);
|
|
||||||
resid = (mx_di_len > 0) ? get_scsi_pt_resid(ptvp) : 0;
|
|
||||||
slen = get_scsi_pt_sense_len(ptvp);
|
|
||||||
switch ((cat = get_scsi_pt_result_category(ptvp))) {
|
|
||||||
case SCSI_PT_RESULT_GOOD:
|
|
||||||
if (sbp && (slen > 7)) {
|
|
||||||
resp_code = sbp[0] & 0x7f;
|
|
||||||
/* SBC referrals can have status=GOOD and sense_key=COMPLETED */
|
|
||||||
if (resp_code >= 0x70) {
|
|
||||||
if (resp_code < 0x72) {
|
|
||||||
if (SPC_SK_NO_SENSE != (0xf & sbp[2]))
|
|
||||||
sg_err_category_sense(sbp, slen);
|
|
||||||
} else if (resp_code < 0x74) {
|
|
||||||
if (SPC_SK_NO_SENSE != (0xf & sbp[1]))
|
|
||||||
sg_err_category_sense(sbp, slen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (mx_di_len > 0) {
|
|
||||||
got = mx_di_len - resid;
|
|
||||||
if ((verbose > 1) && (resid != 0))
|
|
||||||
pr2ws(" %s: %s requested %d bytes (data-in) but got %d "
|
|
||||||
"bytes\n", leadin, pass_through_s, mx_di_len, got);
|
|
||||||
if (got >= 0)
|
|
||||||
return got;
|
|
||||||
else {
|
|
||||||
if (verbose)
|
|
||||||
pr2ws(" %s: %s can't get negative bytes, say it got "
|
|
||||||
"none\n", leadin, pass_through_s);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
return 0;
|
|
||||||
case SCSI_PT_RESULT_STATUS: /* other than GOOD and CHECK CONDITION */
|
|
||||||
sstat = get_scsi_pt_status_response(ptvp);
|
|
||||||
if (o_sense_cat) {
|
|
||||||
switch (sstat) {
|
|
||||||
case SAM_STAT_RESERVATION_CONFLICT:
|
|
||||||
*o_sense_cat = SG_LIB_CAT_RES_CONFLICT;
|
|
||||||
return -2;
|
|
||||||
case SAM_STAT_CONDITION_MET:
|
|
||||||
*o_sense_cat = SG_LIB_CAT_CONDITION_MET;
|
|
||||||
return -2;
|
|
||||||
case SAM_STAT_BUSY:
|
|
||||||
*o_sense_cat = SG_LIB_CAT_BUSY;
|
|
||||||
return -2;
|
|
||||||
case SAM_STAT_TASK_SET_FULL:
|
|
||||||
*o_sense_cat = SG_LIB_CAT_TS_FULL;
|
|
||||||
return -2;
|
|
||||||
case SAM_STAT_ACA_ACTIVE:
|
|
||||||
*o_sense_cat = SG_LIB_CAT_ACA_ACTIVE;
|
|
||||||
return -2;
|
|
||||||
case SAM_STAT_TASK_ABORTED:
|
|
||||||
*o_sense_cat = SG_LIB_CAT_TASK_ABORTED;
|
|
||||||
return -2;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (verbose || noisy) {
|
|
||||||
sg_get_scsi_status_str(sstat, sizeof(b), b);
|
|
||||||
pr2ws("%s: scsi status: %s\n", leadin, b);
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
case SCSI_PT_RESULT_SENSE:
|
|
||||||
return sg_cmds_process_helper(leadin, mx_di_len, resid, sbp, slen,
|
|
||||||
noisy, verbose, o_sense_cat);
|
|
||||||
case SCSI_PT_RESULT_TRANSPORT_ERR:
|
|
||||||
if (verbose || noisy) {
|
|
||||||
get_scsi_pt_transport_err_str(ptvp, sizeof(b), b);
|
|
||||||
pr2ws("%s: transport: %s\n", leadin, b);
|
|
||||||
}
|
|
||||||
#ifdef SG_LIB_LINUX
|
|
||||||
transport_sense = (slen > 0);
|
|
||||||
#else
|
|
||||||
transport_sense = ((SAM_STAT_CHECK_CONDITION ==
|
|
||||||
get_scsi_pt_status_response(ptvp)) && (slen > 0));
|
|
||||||
#endif
|
|
||||||
if (transport_sense)
|
|
||||||
return sg_cmds_process_helper(leadin, mx_di_len, resid, sbp,
|
|
||||||
slen, noisy, verbose, o_sense_cat);
|
|
||||||
else
|
|
||||||
return -1;
|
|
||||||
case SCSI_PT_RESULT_OS_ERR:
|
|
||||||
if (verbose || noisy) {
|
|
||||||
get_scsi_pt_os_err_str(ptvp, sizeof(b), b);
|
|
||||||
pr2ws("%s: os: %s\n", leadin, b);
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
default:
|
|
||||||
pr2ws("%s: unknown %s result category (%d)\n", leadin, pass_through_s,
|
|
||||||
cat);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
sg_cmds_is_nvme(const struct sg_pt_base * ptvp)
|
|
||||||
{
|
|
||||||
return pt_device_is_nvme(ptvp);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct sg_pt_base *
|
|
||||||
create_pt_obj(const char * cname)
|
|
||||||
{
|
|
||||||
struct sg_pt_base * ptvp = construct_scsi_pt_obj();
|
|
||||||
if (NULL == ptvp)
|
|
||||||
pr2ws("%s: out of memory\n", cname);
|
|
||||||
return ptvp;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char * const inquiry_s = "inquiry";
|
|
||||||
|
|
||||||
static int
|
|
||||||
sg_ll_inquiry_com(int sg_fd, bool cmddt, bool evpd, int pg_op, void * resp,
|
|
||||||
int mx_resp_len, int timeout_secs, int * residp,
|
|
||||||
bool noisy, int verbose)
|
|
||||||
{
|
|
||||||
int res, ret, k, sense_cat, resid;
|
|
||||||
unsigned char inq_cdb[INQUIRY_CMDLEN] = {INQUIRY_CMD, 0, 0, 0, 0, 0};
|
|
||||||
unsigned char sense_b[SENSE_BUFF_LEN];
|
|
||||||
unsigned char * up;
|
|
||||||
struct sg_pt_base * ptvp;
|
|
||||||
|
|
||||||
if (cmddt)
|
|
||||||
inq_cdb[1] |= 0x2;
|
|
||||||
if (evpd)
|
|
||||||
inq_cdb[1] |= 0x1;
|
|
||||||
inq_cdb[2] = (unsigned char)pg_op;
|
|
||||||
/* 16 bit allocation length (was 8, increased in spc3r09, 200209) */
|
|
||||||
sg_put_unaligned_be16((uint16_t)mx_resp_len, inq_cdb + 3);
|
|
||||||
if (verbose) {
|
|
||||||
pr2ws(" %s cdb: ", inquiry_s);
|
|
||||||
for (k = 0; k < INQUIRY_CMDLEN; ++k)
|
|
||||||
pr2ws("%02x ", inq_cdb[k]);
|
|
||||||
pr2ws("\n");
|
|
||||||
}
|
|
||||||
if (resp && (mx_resp_len > 0)) {
|
|
||||||
up = (unsigned char *)resp;
|
|
||||||
up[0] = 0x7f; /* defensive prefill */
|
|
||||||
if (mx_resp_len > 4)
|
|
||||||
up[4] = 0;
|
|
||||||
}
|
|
||||||
if (timeout_secs <= 0)
|
|
||||||
timeout_secs = DEF_PT_TIMEOUT;
|
|
||||||
ptvp = construct_scsi_pt_obj();
|
|
||||||
if (NULL == ptvp) {
|
|
||||||
pr2ws("%s: out of memory\n", __func__);
|
|
||||||
if (residp)
|
|
||||||
*residp = 0;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
set_scsi_pt_cdb(ptvp, inq_cdb, sizeof(inq_cdb));
|
|
||||||
set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
|
|
||||||
set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
|
|
||||||
res = do_scsi_pt(ptvp, sg_fd, timeout_secs, verbose);
|
|
||||||
ret = sg_cmds_process_resp(ptvp, inquiry_s, res, mx_resp_len, sense_b,
|
|
||||||
noisy, verbose, &sense_cat);
|
|
||||||
resid = get_scsi_pt_resid(ptvp);
|
|
||||||
if (residp)
|
|
||||||
*residp = resid;
|
|
||||||
if (-1 == ret)
|
|
||||||
ret = sg_convert_errno(get_scsi_pt_os_err(ptvp));
|
|
||||||
else if (-2 == ret) {
|
|
||||||
switch (sense_cat) {
|
|
||||||
case SG_LIB_CAT_RECOVERED:
|
|
||||||
case SG_LIB_CAT_NO_SENSE:
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ret = sense_cat;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (ret < 4) {
|
|
||||||
if (verbose)
|
|
||||||
pr2ws("%s: got too few bytes (%d)\n", __func__, ret);
|
|
||||||
ret = SG_LIB_CAT_MALFORMED;
|
|
||||||
} else
|
|
||||||
ret = 0;
|
|
||||||
destruct_scsi_pt_obj(ptvp);
|
|
||||||
|
|
||||||
if (resid > 0) {
|
|
||||||
if (resid > mx_resp_len) {
|
|
||||||
pr2ws("%s resid (%d) should never exceed requested "
|
|
||||||
"len=%d\n", inquiry_s, resid, mx_resp_len);
|
|
||||||
return ret ? ret : SG_LIB_CAT_MALFORMED;
|
|
||||||
}
|
|
||||||
/* zero unfilled section of response buffer, based on resid */
|
|
||||||
memset((unsigned char *)resp + (mx_resp_len - resid), 0, resid);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Invokes a SCSI INQUIRY command and yields the response. Returns 0 when
|
|
||||||
* successful, various SG_LIB_CAT_* positive values or -1 -> other errors.
|
|
||||||
* The CMDDT field is obsolete in the INQUIRY cdb. */
|
|
||||||
int
|
|
||||||
sg_ll_inquiry(int sg_fd, bool cmddt, bool evpd, int pg_op, void * resp,
|
|
||||||
int mx_resp_len, bool noisy, int verbose)
|
|
||||||
{
|
|
||||||
return sg_ll_inquiry_com(sg_fd, cmddt, evpd, pg_op, resp, mx_resp_len,
|
|
||||||
0 /* timeout_sec */, NULL, noisy, verbose);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Yields most of first 36 bytes of a standard INQUIRY (evpd==0) response.
|
|
||||||
* Returns 0 when successful, various SG_LIB_CAT_* positive values or
|
|
||||||
* -1 -> other errors */
|
|
||||||
int
|
|
||||||
sg_simple_inquiry(int sg_fd, struct sg_simple_inquiry_resp * inq_data,
|
|
||||||
bool noisy, int verbose)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
unsigned char inq_resp[SAFE_STD_INQ_RESP_LEN];
|
|
||||||
|
|
||||||
if (inq_data) {
|
|
||||||
memset(inq_data, 0, sizeof(* inq_data));
|
|
||||||
inq_data->peripheral_qualifier = 0x3;
|
|
||||||
inq_data->peripheral_type = 0x1f;
|
|
||||||
}
|
|
||||||
ret = sg_ll_inquiry_com(sg_fd, false, false, 0, inq_resp,
|
|
||||||
sizeof(inq_resp), 0, NULL, noisy, verbose);
|
|
||||||
|
|
||||||
if (inq_data && (0 == ret)) {
|
|
||||||
inq_data->peripheral_qualifier = (inq_resp[0] >> 5) & 0x7;
|
|
||||||
inq_data->peripheral_type = inq_resp[0] & 0x1f;
|
|
||||||
inq_data->byte_1 = inq_resp[1];
|
|
||||||
inq_data->version = inq_resp[2];
|
|
||||||
inq_data->byte_3 = inq_resp[3];
|
|
||||||
inq_data->byte_5 = inq_resp[5];
|
|
||||||
inq_data->byte_6 = inq_resp[6];
|
|
||||||
inq_data->byte_7 = inq_resp[7];
|
|
||||||
memcpy(inq_data->vendor, inq_resp + 8, 8);
|
|
||||||
memcpy(inq_data->product, inq_resp + 16, 16);
|
|
||||||
memcpy(inq_data->revision, inq_resp + 32, 4);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Invokes a SCSI INQUIRY command and yields the response. Returns 0 when
|
|
||||||
* successful, various SG_LIB_CAT_* positive values or -1 -> other errors.
|
|
||||||
* The CMDDT field is obsolete in the INQUIRY cdb (since spc3r16 in 2003) so
|
|
||||||
* an argument to set it has been removed (use the REPORT SUPPORTED OPERATION
|
|
||||||
* CODES command instead). Adds the ability to set the command abort timeout
|
|
||||||
* and the ability to report the residual count. If timeout_secs is zero
|
|
||||||
* or less the default command abort timeout (60 seconds) is used.
|
|
||||||
* If residp is non-NULL then the residual value is written where residp
|
|
||||||
* points. A residual value of 0 implies mx_resp_len bytes have be written
|
|
||||||
* where resp points. If the residual value equals mx_resp_len then no
|
|
||||||
* bytes have been written. */
|
|
||||||
int
|
|
||||||
sg_ll_inquiry_v2(int sg_fd, bool evpd, int pg_op, void * resp,
|
|
||||||
int mx_resp_len, int timeout_secs, int * residp,
|
|
||||||
bool noisy, int verbose)
|
|
||||||
{
|
|
||||||
return sg_ll_inquiry_com(sg_fd, false, evpd, pg_op, resp, mx_resp_len,
|
|
||||||
timeout_secs, residp, noisy, verbose);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Invokes a SCSI TEST UNIT READY command.
|
|
||||||
* 'pack_id' is just for diagnostics, safe to set to 0.
|
|
||||||
* Looks for progress indicator if 'progress' non-NULL;
|
|
||||||
* if found writes value [0..65535] else write -1.
|
|
||||||
* Returns 0 when successful, various SG_LIB_CAT_* positive values or
|
|
||||||
* -1 -> other errors */
|
|
||||||
int
|
|
||||||
sg_ll_test_unit_ready_progress(int sg_fd, int pack_id, int * progress,
|
|
||||||
bool noisy, int verbose)
|
|
||||||
{
|
|
||||||
static const char * const tur_s = "test unit ready";
|
|
||||||
int res, ret, k, sense_cat;
|
|
||||||
unsigned char tur_cdb[TUR_CMDLEN] = {TUR_CMD, 0, 0, 0, 0, 0};
|
|
||||||
unsigned char sense_b[SENSE_BUFF_LEN];
|
|
||||||
struct sg_pt_base * ptvp;
|
|
||||||
|
|
||||||
if (verbose) {
|
|
||||||
pr2ws(" %s cdb: ", tur_s);
|
|
||||||
for (k = 0; k < TUR_CMDLEN; ++k)
|
|
||||||
pr2ws("%02x ", tur_cdb[k]);
|
|
||||||
pr2ws("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NULL == ((ptvp = create_pt_obj(tur_s))))
|
|
||||||
return -1;
|
|
||||||
set_scsi_pt_cdb(ptvp, tur_cdb, sizeof(tur_cdb));
|
|
||||||
set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
|
|
||||||
set_scsi_pt_packet_id(ptvp, pack_id);
|
|
||||||
res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
|
|
||||||
ret = sg_cmds_process_resp(ptvp, tur_s, res, SG_NO_DATA_IN, sense_b,
|
|
||||||
noisy, verbose, &sense_cat);
|
|
||||||
if (-1 == ret) {
|
|
||||||
int os_err = get_scsi_pt_os_err(ptvp);
|
|
||||||
|
|
||||||
if ((os_err > 0) && (os_err < 47))
|
|
||||||
ret = SG_LIB_OS_BASE_ERR + os_err;
|
|
||||||
} else if (-2 == ret) {
|
|
||||||
if (progress) {
|
|
||||||
int slen = get_scsi_pt_sense_len(ptvp);
|
|
||||||
|
|
||||||
if (! sg_get_sense_progress_fld(sense_b, slen, progress))
|
|
||||||
*progress = -1;
|
|
||||||
}
|
|
||||||
switch (sense_cat) {
|
|
||||||
case SG_LIB_CAT_RECOVERED:
|
|
||||||
case SG_LIB_CAT_NO_SENSE:
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ret = sense_cat;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
ret = 0;
|
|
||||||
|
|
||||||
destruct_scsi_pt_obj(ptvp);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Invokes a SCSI TEST UNIT READY command.
|
|
||||||
* 'pack_id' is just for diagnostics, safe to set to 0.
|
|
||||||
* Returns 0 when successful, various SG_LIB_CAT_* positive values or
|
|
||||||
* -1 -> other errors */
|
|
||||||
int
|
|
||||||
sg_ll_test_unit_ready(int sg_fd, int pack_id, bool noisy, int verbose)
|
|
||||||
{
|
|
||||||
return sg_ll_test_unit_ready_progress(sg_fd, pack_id, NULL, noisy,
|
|
||||||
verbose);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Invokes a SCSI REQUEST SENSE command. Returns 0 when successful, various
|
|
||||||
* SG_LIB_CAT_* positive values or -1 -> other errors */
|
|
||||||
int
|
|
||||||
sg_ll_request_sense(int sg_fd, bool desc, void * resp, int mx_resp_len,
|
|
||||||
bool noisy, int verbose)
|
|
||||||
{
|
|
||||||
static const char * const rq_s = "request sense";
|
|
||||||
int k, ret, res, sense_cat;
|
|
||||||
unsigned char rs_cdb[REQUEST_SENSE_CMDLEN] =
|
|
||||||
{REQUEST_SENSE_CMD, 0, 0, 0, 0, 0};
|
|
||||||
unsigned char sense_b[SENSE_BUFF_LEN];
|
|
||||||
struct sg_pt_base * ptvp;
|
|
||||||
|
|
||||||
if (desc)
|
|
||||||
rs_cdb[1] |= 0x1;
|
|
||||||
if (mx_resp_len > 0xff) {
|
|
||||||
pr2ws("mx_resp_len cannot exceed 255\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
rs_cdb[4] = mx_resp_len & 0xff;
|
|
||||||
if (verbose) {
|
|
||||||
pr2ws(" %s cmd: ", rq_s);
|
|
||||||
for (k = 0; k < REQUEST_SENSE_CMDLEN; ++k)
|
|
||||||
pr2ws("%02x ", rs_cdb[k]);
|
|
||||||
pr2ws("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NULL == ((ptvp = create_pt_obj(rq_s))))
|
|
||||||
return -1;
|
|
||||||
set_scsi_pt_cdb(ptvp, rs_cdb, sizeof(rs_cdb));
|
|
||||||
set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
|
|
||||||
set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
|
|
||||||
res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
|
|
||||||
ret = sg_cmds_process_resp(ptvp, rq_s, res, mx_resp_len, sense_b, noisy,
|
|
||||||
verbose, &sense_cat);
|
|
||||||
if (-1 == ret) {
|
|
||||||
int os_err = get_scsi_pt_os_err(ptvp);
|
|
||||||
|
|
||||||
if ((os_err > 0) && (os_err < 47))
|
|
||||||
ret = SG_LIB_OS_BASE_ERR + os_err;
|
|
||||||
} else if (-2 == ret) {
|
|
||||||
switch (sense_cat) {
|
|
||||||
case SG_LIB_CAT_RECOVERED:
|
|
||||||
case SG_LIB_CAT_NO_SENSE:
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ret = sense_cat;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ((mx_resp_len >= 8) && (ret < 8)) {
|
|
||||||
if (verbose)
|
|
||||||
pr2ws(" %s: got %d bytes in response, too short\n", rq_s,
|
|
||||||
ret);
|
|
||||||
ret = -1;
|
|
||||||
} else
|
|
||||||
ret = 0;
|
|
||||||
}
|
|
||||||
destruct_scsi_pt_obj(ptvp);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Invokes a SCSI REPORT LUNS command. Return of 0 -> success,
|
|
||||||
* various SG_LIB_CAT_* positive values or -1 -> other errors */
|
|
||||||
int
|
|
||||||
sg_ll_report_luns(int sg_fd, int select_report, void * resp, int mx_resp_len,
|
|
||||||
bool noisy, int verbose)
|
|
||||||
{
|
|
||||||
static const char * const report_luns_s = "report luns";
|
|
||||||
int k, ret, res, sense_cat;
|
|
||||||
unsigned char rl_cdb[REPORT_LUNS_CMDLEN] =
|
|
||||||
{REPORT_LUNS_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
||||||
unsigned char sense_b[SENSE_BUFF_LEN];
|
|
||||||
struct sg_pt_base * ptvp;
|
|
||||||
|
|
||||||
rl_cdb[2] = select_report & 0xff;
|
|
||||||
sg_put_unaligned_be32((uint32_t)mx_resp_len, rl_cdb + 6);
|
|
||||||
if (verbose) {
|
|
||||||
pr2ws(" %s cdb: ", report_luns_s);
|
|
||||||
for (k = 0; k < REPORT_LUNS_CMDLEN; ++k)
|
|
||||||
pr2ws("%02x ", rl_cdb[k]);
|
|
||||||
pr2ws("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NULL == ((ptvp = create_pt_obj(report_luns_s))))
|
|
||||||
return -1;
|
|
||||||
set_scsi_pt_cdb(ptvp, rl_cdb, sizeof(rl_cdb));
|
|
||||||
set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
|
|
||||||
set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
|
|
||||||
res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
|
|
||||||
ret = sg_cmds_process_resp(ptvp, report_luns_s, res, mx_resp_len,
|
|
||||||
sense_b, noisy, verbose, &sense_cat);
|
|
||||||
if (-1 == ret) {
|
|
||||||
int os_err = get_scsi_pt_os_err(ptvp);
|
|
||||||
|
|
||||||
if ((os_err > 0) && (os_err < 47))
|
|
||||||
ret = SG_LIB_OS_BASE_ERR + os_err;
|
|
||||||
} else if (-2 == ret) {
|
|
||||||
switch (sense_cat) {
|
|
||||||
case SG_LIB_CAT_RECOVERED:
|
|
||||||
case SG_LIB_CAT_NO_SENSE:
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ret = sense_cat;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
ret = 0;
|
|
||||||
destruct_scsi_pt_obj(ptvp);
|
|
||||||
return ret;
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,382 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2008-2018 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#define __STDC_FORMAT_MACROS 1
|
|
||||||
#include <inttypes.h>
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "sg_lib.h"
|
|
||||||
#include "sg_cmds_basic.h"
|
|
||||||
#include "sg_cmds_mmc.h"
|
|
||||||
#include "sg_pt.h"
|
|
||||||
#include "sg_unaligned.h"
|
|
||||||
|
|
||||||
|
|
||||||
#define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */
|
|
||||||
|
|
||||||
#define DEF_PT_TIMEOUT 60 /* 60 seconds */
|
|
||||||
|
|
||||||
#define GET_CONFIG_CMD 0x46
|
|
||||||
#define GET_CONFIG_CMD_LEN 10
|
|
||||||
#define GET_PERFORMANCE_CMD 0xac
|
|
||||||
#define GET_PERFORMANCE_CMD_LEN 12
|
|
||||||
#define SET_CD_SPEED_CMD 0xbb
|
|
||||||
#define SET_CD_SPEED_CMDLEN 12
|
|
||||||
#define SET_STREAMING_CMD 0xb6
|
|
||||||
#define SET_STREAMING_CMDLEN 12
|
|
||||||
|
|
||||||
#if defined(__GNUC__) || defined(__clang__)
|
|
||||||
static int pr2ws(const char * fmt, ...)
|
|
||||||
__attribute__ ((format (printf, 1, 2)));
|
|
||||||
#else
|
|
||||||
static int pr2ws(const char * fmt, ...);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
static int
|
|
||||||
pr2ws(const char * fmt, ...)
|
|
||||||
{
|
|
||||||
va_list args;
|
|
||||||
int n;
|
|
||||||
|
|
||||||
va_start(args, fmt);
|
|
||||||
n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args);
|
|
||||||
va_end(args);
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct sg_pt_base *
|
|
||||||
create_pt_obj(const char * cname)
|
|
||||||
{
|
|
||||||
struct sg_pt_base * ptvp = construct_scsi_pt_obj();
|
|
||||||
if (NULL == ptvp)
|
|
||||||
pr2ws("%s: out of memory\n", cname);
|
|
||||||
return ptvp;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Invokes a SCSI SET CD SPEED command (MMC).
|
|
||||||
* Return of 0 -> success, SG_LIB_CAT_INVALID_OP -> command not supported,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
|
|
||||||
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* -1 -> other failure */
|
|
||||||
int
|
|
||||||
sg_ll_set_cd_speed(int sg_fd, int rot_control, int drv_read_speed,
|
|
||||||
int drv_write_speed, bool noisy, int verbose)
|
|
||||||
{
|
|
||||||
static const char * const cdb_name_s = "set cd speed";
|
|
||||||
int res, ret, k, sense_cat;
|
|
||||||
unsigned char scsCmdBlk[SET_CD_SPEED_CMDLEN] = {SET_CD_SPEED_CMD, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0 ,0};
|
|
||||||
unsigned char sense_b[SENSE_BUFF_LEN];
|
|
||||||
struct sg_pt_base * ptvp;
|
|
||||||
|
|
||||||
scsCmdBlk[1] |= (rot_control & 0x3);
|
|
||||||
sg_put_unaligned_be16((uint16_t)drv_read_speed, scsCmdBlk + 2);
|
|
||||||
sg_put_unaligned_be16((uint16_t)drv_write_speed, scsCmdBlk + 4);
|
|
||||||
|
|
||||||
if (verbose) {
|
|
||||||
pr2ws(" %s cdb: ", cdb_name_s);
|
|
||||||
for (k = 0; k < SET_CD_SPEED_CMDLEN; ++k)
|
|
||||||
pr2ws("%02x ", scsCmdBlk[k]);
|
|
||||||
pr2ws("\n");
|
|
||||||
}
|
|
||||||
if (NULL == ((ptvp = create_pt_obj(cdb_name_s))))
|
|
||||||
return -1;
|
|
||||||
set_scsi_pt_cdb(ptvp, scsCmdBlk, sizeof(scsCmdBlk));
|
|
||||||
set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
|
|
||||||
res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
|
|
||||||
ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, SG_NO_DATA_IN, sense_b,
|
|
||||||
noisy, verbose, &sense_cat);
|
|
||||||
if (-1 == ret) {
|
|
||||||
int os_err = get_scsi_pt_os_err(ptvp);
|
|
||||||
|
|
||||||
if ((os_err > 0) && (os_err < 47))
|
|
||||||
ret = SG_LIB_OS_BASE_ERR + os_err;
|
|
||||||
} else if (-2 == ret) {
|
|
||||||
switch (sense_cat) {
|
|
||||||
case SG_LIB_CAT_NOT_READY:
|
|
||||||
case SG_LIB_CAT_UNIT_ATTENTION:
|
|
||||||
case SG_LIB_CAT_INVALID_OP:
|
|
||||||
case SG_LIB_CAT_ILLEGAL_REQ:
|
|
||||||
case SG_LIB_CAT_ABORTED_COMMAND:
|
|
||||||
ret = sense_cat;
|
|
||||||
break;
|
|
||||||
case SG_LIB_CAT_RECOVERED:
|
|
||||||
case SG_LIB_CAT_NO_SENSE:
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ret = -1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
ret = 0;
|
|
||||||
|
|
||||||
destruct_scsi_pt_obj(ptvp);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Invokes a SCSI GET CONFIGURATION command (MMC-3,4,5).
|
|
||||||
* Returns 0 when successful, SG_LIB_CAT_INVALID_OP if command not
|
|
||||||
* supported, SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported,
|
|
||||||
* SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, else -1 */
|
|
||||||
int
|
|
||||||
sg_ll_get_config(int sg_fd, int rt, int starting, void * resp,
|
|
||||||
int mx_resp_len, bool noisy, int verbose)
|
|
||||||
{
|
|
||||||
static const char * const cdb_name_s = "get configuration";
|
|
||||||
int res, k, ret, sense_cat;
|
|
||||||
unsigned char gcCmdBlk[GET_CONFIG_CMD_LEN] = {GET_CONFIG_CMD, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0};
|
|
||||||
unsigned char sense_b[SENSE_BUFF_LEN];
|
|
||||||
struct sg_pt_base * ptvp;
|
|
||||||
|
|
||||||
if ((rt < 0) || (rt > 3)) {
|
|
||||||
pr2ws("Bad rt value: %d\n", rt);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
gcCmdBlk[1] = (rt & 0x3);
|
|
||||||
if ((starting < 0) || (starting > 0xffff)) {
|
|
||||||
pr2ws("Bad starting field number: 0x%x\n", starting);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
sg_put_unaligned_be16((uint16_t)starting, gcCmdBlk + 2);
|
|
||||||
if ((mx_resp_len < 0) || (mx_resp_len > 0xffff)) {
|
|
||||||
pr2ws("Bad mx_resp_len: 0x%x\n", starting);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
sg_put_unaligned_be16((uint16_t)mx_resp_len, gcCmdBlk + 7);
|
|
||||||
|
|
||||||
if (verbose) {
|
|
||||||
pr2ws(" %s cdb: ", cdb_name_s);
|
|
||||||
for (k = 0; k < GET_CONFIG_CMD_LEN; ++k)
|
|
||||||
pr2ws("%02x ", gcCmdBlk[k]);
|
|
||||||
pr2ws("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NULL == ((ptvp = create_pt_obj(cdb_name_s))))
|
|
||||||
return -1;
|
|
||||||
set_scsi_pt_cdb(ptvp, gcCmdBlk, sizeof(gcCmdBlk));
|
|
||||||
set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
|
|
||||||
set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
|
|
||||||
res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
|
|
||||||
ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, mx_resp_len,
|
|
||||||
sense_b, noisy, verbose, &sense_cat);
|
|
||||||
if (-1 == ret) {
|
|
||||||
int os_err = get_scsi_pt_os_err(ptvp);
|
|
||||||
|
|
||||||
if ((os_err > 0) && (os_err < 47))
|
|
||||||
ret = SG_LIB_OS_BASE_ERR + os_err;
|
|
||||||
} else if (-2 == ret) {
|
|
||||||
switch (sense_cat) {
|
|
||||||
case SG_LIB_CAT_INVALID_OP:
|
|
||||||
case SG_LIB_CAT_ILLEGAL_REQ:
|
|
||||||
case SG_LIB_CAT_UNIT_ATTENTION:
|
|
||||||
case SG_LIB_CAT_ABORTED_COMMAND:
|
|
||||||
ret = sense_cat;
|
|
||||||
break;
|
|
||||||
case SG_LIB_CAT_RECOVERED:
|
|
||||||
case SG_LIB_CAT_NO_SENSE:
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ret = -1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ((verbose > 2) && (ret > 3)) {
|
|
||||||
unsigned char * bp;
|
|
||||||
int len;
|
|
||||||
|
|
||||||
bp = (unsigned char *)resp;
|
|
||||||
len = sg_get_unaligned_be32(bp + 0);
|
|
||||||
if (len < 0)
|
|
||||||
len = 0;
|
|
||||||
len = (ret < len) ? ret : len;
|
|
||||||
pr2ws(" %s: response:\n", cdb_name_s);
|
|
||||||
if (3 == verbose) {
|
|
||||||
pr2ws("%s:\n", (len > 256 ? ", first 256 bytes" : ""));
|
|
||||||
hex2stderr((const uint8_t *)resp, (len > 256 ? 256 : len),
|
|
||||||
-1);
|
|
||||||
} else {
|
|
||||||
pr2ws(":\n");
|
|
||||||
hex2stderr((const uint8_t *)resp, len, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ret = 0;
|
|
||||||
}
|
|
||||||
destruct_scsi_pt_obj(ptvp);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Invokes a SCSI GET PERFORMANCE command (MMC-3...6).
|
|
||||||
* Returns 0 when successful, SG_LIB_CAT_INVALID_OP if command not
|
|
||||||
* supported, SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported,
|
|
||||||
* SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, else -1 */
|
|
||||||
int
|
|
||||||
sg_ll_get_performance(int sg_fd, int data_type, unsigned int starting_lba,
|
|
||||||
int max_num_desc, int ttype, void * resp,
|
|
||||||
int mx_resp_len, bool noisy, int verbose)
|
|
||||||
{
|
|
||||||
static const char * const cdb_name_s = "get performance";
|
|
||||||
int res, k, ret, sense_cat;
|
|
||||||
unsigned char gpCmdBlk[GET_PERFORMANCE_CMD_LEN] = {GET_PERFORMANCE_CMD, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
||||||
unsigned char sense_b[SENSE_BUFF_LEN];
|
|
||||||
struct sg_pt_base * ptvp;
|
|
||||||
|
|
||||||
if ((data_type < 0) || (data_type > 0x1f)) {
|
|
||||||
pr2ws("Bad data_type value: %d\n", data_type);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
gpCmdBlk[1] = (data_type & 0x1f);
|
|
||||||
sg_put_unaligned_be32((uint32_t)starting_lba, gpCmdBlk + 2);
|
|
||||||
if ((max_num_desc < 0) || (max_num_desc > 0xffff)) {
|
|
||||||
pr2ws("Bad max_num_desc: 0x%x\n", max_num_desc);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
sg_put_unaligned_be16((uint16_t)max_num_desc, gpCmdBlk + 8);
|
|
||||||
if ((ttype < 0) || (ttype > 0xff)) {
|
|
||||||
pr2ws("Bad type: 0x%x\n", ttype);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
gpCmdBlk[10] = (unsigned char)ttype;
|
|
||||||
|
|
||||||
if (verbose) {
|
|
||||||
pr2ws(" %s cdb: ", cdb_name_s);
|
|
||||||
for (k = 0; k < GET_PERFORMANCE_CMD_LEN; ++k)
|
|
||||||
pr2ws("%02x ", gpCmdBlk[k]);
|
|
||||||
pr2ws("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NULL == ((ptvp = create_pt_obj(cdb_name_s))))
|
|
||||||
return -1;
|
|
||||||
set_scsi_pt_cdb(ptvp, gpCmdBlk, sizeof(gpCmdBlk));
|
|
||||||
set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
|
|
||||||
set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
|
|
||||||
res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
|
|
||||||
ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, mx_resp_len, sense_b,
|
|
||||||
noisy, verbose, &sense_cat);
|
|
||||||
if (-1 == ret) {
|
|
||||||
int os_err = get_scsi_pt_os_err(ptvp);
|
|
||||||
|
|
||||||
if ((os_err > 0) && (os_err < 47))
|
|
||||||
ret = SG_LIB_OS_BASE_ERR + os_err;
|
|
||||||
} else if (-2 == ret) {
|
|
||||||
switch (sense_cat) {
|
|
||||||
case SG_LIB_CAT_INVALID_OP:
|
|
||||||
case SG_LIB_CAT_ILLEGAL_REQ:
|
|
||||||
case SG_LIB_CAT_UNIT_ATTENTION:
|
|
||||||
case SG_LIB_CAT_ABORTED_COMMAND:
|
|
||||||
ret = sense_cat;
|
|
||||||
break;
|
|
||||||
case SG_LIB_CAT_RECOVERED:
|
|
||||||
case SG_LIB_CAT_NO_SENSE:
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ret = -1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ((verbose > 2) && (ret > 3)) {
|
|
||||||
unsigned char * bp;
|
|
||||||
int len;
|
|
||||||
|
|
||||||
bp = (unsigned char *)resp;
|
|
||||||
len = sg_get_unaligned_be32(bp + 0);
|
|
||||||
if (len < 0)
|
|
||||||
len = 0;
|
|
||||||
len = (ret < len) ? ret : len;
|
|
||||||
pr2ws(" %s: response", cdb_name_s);
|
|
||||||
if (3 == verbose) {
|
|
||||||
pr2ws("%s:\n", (len > 256 ? ", first 256 bytes" : ""));
|
|
||||||
hex2stderr((const uint8_t *)resp, (len > 256 ? 256 : len),
|
|
||||||
-1);
|
|
||||||
} else {
|
|
||||||
pr2ws(":\n");
|
|
||||||
hex2stderr((const uint8_t *)resp, len, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ret = 0;
|
|
||||||
}
|
|
||||||
destruct_scsi_pt_obj(ptvp);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Invokes a SCSI SET STREAMING command (MMC). Return of 0 -> success,
|
|
||||||
* SG_LIB_CAT_INVALID_OP -> Set Streaming not supported,
|
|
||||||
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
|
|
||||||
* SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_NOT_READY -> device not ready,
|
|
||||||
* -1 -> other failure */
|
|
||||||
int
|
|
||||||
sg_ll_set_streaming(int sg_fd, int type, void * paramp, int param_len,
|
|
||||||
bool noisy, int verbose)
|
|
||||||
{
|
|
||||||
static const char * const cdb_name_s = "set streaming";
|
|
||||||
int k, res, ret, sense_cat;
|
|
||||||
unsigned char ssCmdBlk[SET_STREAMING_CMDLEN] =
|
|
||||||
{SET_STREAMING_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
||||||
unsigned char sense_b[SENSE_BUFF_LEN];
|
|
||||||
struct sg_pt_base * ptvp;
|
|
||||||
|
|
||||||
ssCmdBlk[8] = type;
|
|
||||||
sg_put_unaligned_be16((uint16_t)param_len, ssCmdBlk + 9);
|
|
||||||
if (verbose) {
|
|
||||||
pr2ws(" %s cdb: ", cdb_name_s);
|
|
||||||
for (k = 0; k < SET_STREAMING_CMDLEN; ++k)
|
|
||||||
pr2ws("%02x ", ssCmdBlk[k]);
|
|
||||||
pr2ws("\n");
|
|
||||||
if ((verbose > 1) && paramp && param_len) {
|
|
||||||
pr2ws(" %s parameter list:\n", cdb_name_s);
|
|
||||||
hex2stderr((const uint8_t *)paramp, param_len, -1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NULL == ((ptvp = create_pt_obj(cdb_name_s))))
|
|
||||||
return -1;
|
|
||||||
set_scsi_pt_cdb(ptvp, ssCmdBlk, sizeof(ssCmdBlk));
|
|
||||||
set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
|
|
||||||
set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len);
|
|
||||||
res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
|
|
||||||
ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, SG_NO_DATA_IN, sense_b,
|
|
||||||
noisy, verbose, &sense_cat);
|
|
||||||
if (-1 == ret) {
|
|
||||||
int os_err = get_scsi_pt_os_err(ptvp);
|
|
||||||
|
|
||||||
if ((os_err > 0) && (os_err < 47))
|
|
||||||
ret = SG_LIB_OS_BASE_ERR + os_err;
|
|
||||||
} else if (-2 == ret) {
|
|
||||||
switch (sense_cat) {
|
|
||||||
case SG_LIB_CAT_NOT_READY:
|
|
||||||
case SG_LIB_CAT_INVALID_OP:
|
|
||||||
case SG_LIB_CAT_ILLEGAL_REQ:
|
|
||||||
case SG_LIB_CAT_UNIT_ATTENTION:
|
|
||||||
case SG_LIB_CAT_ABORTED_COMMAND:
|
|
||||||
ret = sense_cat;
|
|
||||||
break;
|
|
||||||
case SG_LIB_CAT_RECOVERED:
|
|
||||||
case SG_LIB_CAT_NO_SENSE:
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ret = -1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
ret = 0;
|
|
||||||
destruct_scsi_pt_obj(ptvp);
|
|
||||||
return ret;
|
|
||||||
}
|
|
@ -1,256 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 1999-2018 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#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"
|
|
||||||
|
|
||||||
|
|
||||||
/* Version 1.07 20160405 */
|
|
||||||
|
|
||||||
#if defined(__GNUC__) || defined(__clang__)
|
|
||||||
static int pr2ws(const char * fmt, ...)
|
|
||||||
__attribute__ ((format (printf, 1, 2)));
|
|
||||||
#else
|
|
||||||
static int pr2ws(const char * fmt, ...);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
static int
|
|
||||||
pr2ws(const char * fmt, ...)
|
|
||||||
{
|
|
||||||
va_list args;
|
|
||||||
int n;
|
|
||||||
|
|
||||||
va_start(args, fmt);
|
|
||||||
n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args);
|
|
||||||
va_end(args);
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
sg_print_masked_status(int masked_status)
|
|
||||||
{
|
|
||||||
int scsi_status = (masked_status << 1) & 0x7e;
|
|
||||||
|
|
||||||
sg_print_scsi_status(scsi_status);
|
|
||||||
}
|
|
||||||
|
|
||||||
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",
|
|
||||||
};
|
|
||||||
|
|
||||||
#define LINUX_HOST_BYTES_SZ \
|
|
||||||
(int)(sizeof(linux_host_bytes) / sizeof(linux_host_bytes[0]))
|
|
||||||
|
|
||||||
void
|
|
||||||
sg_print_host_status(int host_status)
|
|
||||||
{
|
|
||||||
pr2ws("Host_status=0x%02x ", host_status);
|
|
||||||
if ((host_status < 0) || (host_status >= LINUX_HOST_BYTES_SZ))
|
|
||||||
pr2ws("is invalid ");
|
|
||||||
else
|
|
||||||
pr2ws("[%s] ", linux_host_bytes[host_status]);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char * linux_driver_bytes[] = {
|
|
||||||
"DRIVER_OK", "DRIVER_BUSY", "DRIVER_SOFT", "DRIVER_MEDIA",
|
|
||||||
"DRIVER_ERROR", "DRIVER_INVALID", "DRIVER_TIMEOUT", "DRIVER_HARD",
|
|
||||||
"DRIVER_SENSE"
|
|
||||||
};
|
|
||||||
|
|
||||||
#define LINUX_DRIVER_BYTES_SZ \
|
|
||||||
(int)(sizeof(linux_driver_bytes) / sizeof(linux_driver_bytes[0]))
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
static const char * linux_driver_suggests[] = {
|
|
||||||
"SUGGEST_OK", "SUGGEST_RETRY", "SUGGEST_ABORT", "SUGGEST_REMAP",
|
|
||||||
"SUGGEST_DIE", "UNKNOWN","UNKNOWN","UNKNOWN",
|
|
||||||
"SUGGEST_SENSE"
|
|
||||||
};
|
|
||||||
|
|
||||||
#define LINUX_DRIVER_SUGGESTS_SZ \
|
|
||||||
(int)(sizeof(linux_driver_suggests) / sizeof(linux_driver_suggests[0]))
|
|
||||||
#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 < LINUX_DRIVER_BYTES_SZ)
|
|
||||||
driv_cp = linux_driver_bytes[driv];
|
|
||||||
#if 0
|
|
||||||
sugg = (driver_status & SG_LIB_SUGGEST_MASK) >> 4;
|
|
||||||
if (sugg < LINUX_DRIVER_SUGGESTS_SZ)
|
|
||||||
sugg_cp = linux_driver_suggests[sugg];
|
|
||||||
#endif
|
|
||||||
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') and returns 0. */
|
|
||||||
static int
|
|
||||||
sg_linux_sense_print(const char * leadin, int scsi_status, int host_status,
|
|
||||||
int driver_status, const unsigned char * 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 unsigned char * 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 unsigned char * 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 unsigned char * 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 */
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,141 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2009-2018 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#define __STDC_FORMAT_MACROS 1
|
|
||||||
#include <inttypes.h>
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "sg_lib.h"
|
|
||||||
#include "sg_pt.h"
|
|
||||||
#include "sg_pt_nvme.h"
|
|
||||||
|
|
||||||
|
|
||||||
static const char * scsi_pt_version_str = "3.03 20180115";
|
|
||||||
|
|
||||||
static const char * nvme_scsi_vendor_str = "NVMe ";
|
|
||||||
|
|
||||||
|
|
||||||
const char *
|
|
||||||
scsi_pt_version()
|
|
||||||
{
|
|
||||||
return scsi_pt_version_str;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Given the NVMe Identify controller response and optionally the NVMe
|
|
||||||
* Identify namespace response (NULL otherwise), generate the SCSI VPD
|
|
||||||
* page 0x83 (device identification) descriptor(s) in dop. Return the
|
|
||||||
* number of bytes written which will not exceed max_do_len. Probably use
|
|
||||||
* Peripheral Device Type (pdt) of 0 (disk) for don't know. Transport
|
|
||||||
* protocol (tproto) should be -1 if not known, else SCSI value.
|
|
||||||
* N.B. Does not write total VPD page length into dop[2:3] . */
|
|
||||||
int
|
|
||||||
sg_make_vpd_devid_for_nvme(const uint8_t * nvme_id_ctl_p,
|
|
||||||
const uint8_t * nvme_id_ns_p, int pdt,
|
|
||||||
int tproto, uint8_t * dop, int max_do_len)
|
|
||||||
{
|
|
||||||
bool have_nguid, have_eui64;
|
|
||||||
int k, n;
|
|
||||||
char b[4];
|
|
||||||
|
|
||||||
if ((NULL == nvme_id_ctl_p) || (NULL == dop) || (max_do_len < 56))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
memset(dop, 0, max_do_len);
|
|
||||||
dop[0] = 0x1f & pdt; /* (PQ=0)<<5 | (PDT=pdt); 0 or 0xd (SES) */
|
|
||||||
dop[1] = 0x83; /* Device Identification VPD page number */
|
|
||||||
/* Build a T10 Vendor ID based designator (desig_id=1) for controller */
|
|
||||||
if (tproto >= 0) {
|
|
||||||
dop[4] = ((0xf & tproto) << 4) | 0x2;
|
|
||||||
dop[5] = 0xa1; /* PIV=1, ASSOC=2 (target device), desig_id=1 */
|
|
||||||
} else {
|
|
||||||
dop[4] = 0x2; /* Prococol id=0, code_set=2 (ASCII) */
|
|
||||||
dop[5] = 0x21; /* PIV=0, ASSOC=2 (target device), desig_id=1 */
|
|
||||||
}
|
|
||||||
memcpy(dop + 8, nvme_scsi_vendor_str, 8); /* N.B. this is "NVMe " */
|
|
||||||
memcpy(dop + 16, nvme_id_ctl_p + 24, 40); /* MN */
|
|
||||||
for (k = 40; k > 0; --k) {
|
|
||||||
if (' ' == dop[15 + k])
|
|
||||||
dop[15 + k] = '_'; /* convert trailing spaces */
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (40 == k)
|
|
||||||
--k;
|
|
||||||
n = 16 + 1 + k;
|
|
||||||
if (max_do_len < (n + 20))
|
|
||||||
return 0;
|
|
||||||
memcpy(dop + n, nvme_id_ctl_p + 4, 20); /* SN */
|
|
||||||
for (k = 20; k > 0; --k) { /* trim trailing spaces */
|
|
||||||
if (' ' == dop[n + k - 1])
|
|
||||||
dop[n + k - 1] = '\0';
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
n += k;
|
|
||||||
if (0 != (n % 4))
|
|
||||||
n = ((n / 4) + 1) * 4; /* round up to next modulo 4 */
|
|
||||||
dop[7] = n - 8;
|
|
||||||
if (NULL == nvme_id_ns_p)
|
|
||||||
return n;
|
|
||||||
|
|
||||||
/* Look for NGUID (16 byte identifier) or EUI64 (8 byte) fields in
|
|
||||||
* NVME Identify for namespace. If found form a EUI and a SCSI string
|
|
||||||
* descriptor for non-zero NGUID or EUI64 (prefer NGUID if both). */
|
|
||||||
have_nguid = ! sg_all_zeros(nvme_id_ns_p + 104, 16);
|
|
||||||
have_eui64 = ! sg_all_zeros(nvme_id_ns_p + 120, 8);
|
|
||||||
if ((! have_nguid) && (! have_eui64))
|
|
||||||
return n;
|
|
||||||
if (have_nguid) {
|
|
||||||
if (max_do_len < (n + 20))
|
|
||||||
return n;
|
|
||||||
dop[n + 0] = 0x1; /* Prococol id=0, code_set=1 (binary) */
|
|
||||||
dop[n + 1] = 0x02; /* PIV=0, ASSOC=0 (lu), desig_id=2 (eui) */
|
|
||||||
dop[n + 3] = 16;
|
|
||||||
memcpy(dop + n + 4, nvme_id_ns_p + 104, 16);
|
|
||||||
n += 20;
|
|
||||||
if (max_do_len < (n + 40))
|
|
||||||
return n;
|
|
||||||
dop[n + 0] = 0x3; /* Prococol id=0, code_set=3 (utf8) */
|
|
||||||
dop[n + 1] = 0x08; /* PIV=0, ASSOC=0 (lu), desig_id=8 (scsi string) */
|
|
||||||
dop[n + 3] = 36;
|
|
||||||
memcpy(dop + n + 4, "eui.", 4);
|
|
||||||
for (k = 0; k < 16; ++k) {
|
|
||||||
snprintf(b, sizeof(b), "%02X", nvme_id_ns_p[104 + k]);
|
|
||||||
memcpy(dop + n + 8 + (2 * k), b, 2);
|
|
||||||
}
|
|
||||||
return n + 40;
|
|
||||||
} else { /* have_eui64 is true, 8 byte identifier */
|
|
||||||
if (max_do_len < (n + 12))
|
|
||||||
return n;
|
|
||||||
dop[n + 0] = 0x1; /* Prococol id=0, code_set=1 (binary) */
|
|
||||||
dop[n + 1] = 0x02; /* PIV=0, ASSOC=0 (lu), desig_id=2 (eui) */
|
|
||||||
dop[n + 3] = 8;
|
|
||||||
memcpy(dop + n + 4, nvme_id_ns_p + 120, 8);
|
|
||||||
n += 12;
|
|
||||||
if (max_do_len < (n + 24))
|
|
||||||
return n;
|
|
||||||
dop[n + 0] = 0x3; /* Prococol id=0, code_set=3 (utf8) */
|
|
||||||
dop[n + 1] = 0x08; /* PIV=0, ASSOC=0 (lu), desig_id=8 (scsi string) */
|
|
||||||
dop[n + 3] = 20;
|
|
||||||
memcpy(dop + n + 4, "eui.", 4);
|
|
||||||
for (k = 0; k < 8; ++k) {
|
|
||||||
snprintf(b, sizeof(b), "%02X", nvme_id_ns_p[120 + k]);
|
|
||||||
memcpy(dop + n + 8 + (2 * k), b, 2);
|
|
||||||
}
|
|
||||||
return n + 24;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,964 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2005-2018 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* sg_pt_linux version 1.37 20180126 */
|
|
||||||
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/sysmacros.h> /* to define 'major' */
|
|
||||||
#ifndef major
|
|
||||||
#include <sys/types.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <linux/major.h>
|
|
||||||
|
|
||||||
#include "sg_pt.h"
|
|
||||||
#include "sg_lib.h"
|
|
||||||
#include "sg_linux_inc.h"
|
|
||||||
#include "sg_pt_linux.h"
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef major
|
|
||||||
#define SG_DEV_MAJOR major
|
|
||||||
#else
|
|
||||||
#ifdef HAVE_LINUX_KDEV_T_H
|
|
||||||
#include <linux/kdev_t.h>
|
|
||||||
#endif
|
|
||||||
#define SG_DEV_MAJOR MAJOR /* MAJOR() macro faulty if > 255 minors */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef BLOCK_EXT_MAJOR
|
|
||||||
#define BLOCK_EXT_MAJOR 259
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define DEF_TIMEOUT 60000 /* 60,000 millisecs (60 seconds) */
|
|
||||||
|
|
||||||
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" /* 0xd */,
|
|
||||||
"DID_TRANSPORT_DISRUPTED", "DID_TRANSPORT_FAILFAST",
|
|
||||||
"DID_TARGET_FAILURE" /* 0x10 */,
|
|
||||||
"DID_NEXUS_FAILURE (reservation conflict)",
|
|
||||||
"DID_ALLOC_FAILURE",
|
|
||||||
"DID_MEDIUM_ERROR",
|
|
||||||
};
|
|
||||||
|
|
||||||
#define LINUX_HOST_BYTES_SZ \
|
|
||||||
(int)(sizeof(linux_host_bytes) / sizeof(linux_host_bytes[0]))
|
|
||||||
|
|
||||||
static const char * linux_driver_bytes[] = {
|
|
||||||
"DRIVER_OK", "DRIVER_BUSY", "DRIVER_SOFT", "DRIVER_MEDIA",
|
|
||||||
"DRIVER_ERROR", "DRIVER_INVALID", "DRIVER_TIMEOUT", "DRIVER_HARD",
|
|
||||||
"DRIVER_SENSE"
|
|
||||||
};
|
|
||||||
|
|
||||||
#define LINUX_DRIVER_BYTES_SZ \
|
|
||||||
(int)(sizeof(linux_driver_bytes) / sizeof(linux_driver_bytes[0]))
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
static const char * linux_driver_suggests[] = {
|
|
||||||
"SUGGEST_OK", "SUGGEST_RETRY", "SUGGEST_ABORT", "SUGGEST_REMAP",
|
|
||||||
"SUGGEST_DIE", "UNKNOWN","UNKNOWN","UNKNOWN",
|
|
||||||
"SUGGEST_SENSE"
|
|
||||||
};
|
|
||||||
|
|
||||||
#define LINUX_DRIVER_SUGGESTS_SZ \
|
|
||||||
(int)(sizeof(linux_driver_suggests) / sizeof(linux_driver_suggests[0]))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* These defines are for constants that should be visible in the
|
|
||||||
* /usr/include/scsi directory (brought in by sg_linux_inc.h).
|
|
||||||
* Redefined and aliased here to decouple this code from
|
|
||||||
* sg_io_linux.h N.B. the SUGGEST_* constants are no longer used.
|
|
||||||
*/
|
|
||||||
#ifndef DRIVER_MASK
|
|
||||||
#define DRIVER_MASK 0x0f
|
|
||||||
#endif
|
|
||||||
#ifndef SUGGEST_MASK
|
|
||||||
#define SUGGEST_MASK 0xf0
|
|
||||||
#endif
|
|
||||||
#ifndef DRIVER_SENSE
|
|
||||||
#define DRIVER_SENSE 0x08
|
|
||||||
#endif
|
|
||||||
#define SG_LIB_DRIVER_MASK DRIVER_MASK
|
|
||||||
#define SG_LIB_SUGGEST_MASK SUGGEST_MASK
|
|
||||||
#define SG_LIB_DRIVER_SENSE DRIVER_SENSE
|
|
||||||
|
|
||||||
bool sg_bsg_nvme_char_major_checked = false;
|
|
||||||
int sg_bsg_major = 0;
|
|
||||||
volatile int sg_nvme_char_major = 0;
|
|
||||||
|
|
||||||
long sg_lin_page_size = 4096; /* default, overridden with correct value */
|
|
||||||
|
|
||||||
|
|
||||||
#if defined(__GNUC__) || defined(__clang__)
|
|
||||||
static int pr2ws(const char * fmt, ...)
|
|
||||||
__attribute__ ((format (printf, 1, 2)));
|
|
||||||
#else
|
|
||||||
static int pr2ws(const char * fmt, ...);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
static int
|
|
||||||
pr2ws(const char * fmt, ...)
|
|
||||||
{
|
|
||||||
va_list args;
|
|
||||||
int n;
|
|
||||||
|
|
||||||
va_start(args, fmt);
|
|
||||||
n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args);
|
|
||||||
va_end(args);
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This function only needs to be called once (unless a NVMe controller
|
|
||||||
* can be hot-plugged into system in which case it should be called
|
|
||||||
* (again) after that event). */
|
|
||||||
void
|
|
||||||
sg_find_bsg_nvme_char_major(int verbose)
|
|
||||||
{
|
|
||||||
bool got_one = false;
|
|
||||||
int n;
|
|
||||||
const char * proc_devices = "/proc/devices";
|
|
||||||
char * cp;
|
|
||||||
FILE *fp;
|
|
||||||
char a[128];
|
|
||||||
char b[128];
|
|
||||||
|
|
||||||
sg_lin_page_size = sysconf(_SC_PAGESIZE);
|
|
||||||
if (NULL == (fp = fopen(proc_devices, "r"))) {
|
|
||||||
if (verbose)
|
|
||||||
pr2ws("fopen %s failed: %s\n", proc_devices, strerror(errno));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
while ((cp = fgets(b, sizeof(b), fp))) {
|
|
||||||
if ((1 == sscanf(b, "%126s", a)) &&
|
|
||||||
(0 == memcmp(a, "Character", 9)))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
while (cp && (cp = fgets(b, sizeof(b), fp))) {
|
|
||||||
if (2 == sscanf(b, "%d %126s", &n, a)) {
|
|
||||||
if (0 == strcmp("bsg", a)) {
|
|
||||||
sg_bsg_major = n;
|
|
||||||
if (got_one)
|
|
||||||
break;
|
|
||||||
got_one = true;
|
|
||||||
} else if (0 == strcmp("nvme", a)) {
|
|
||||||
sg_nvme_char_major = n;
|
|
||||||
if (got_one)
|
|
||||||
break;
|
|
||||||
got_one = true;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (verbose > 3) {
|
|
||||||
if (cp) {
|
|
||||||
if (sg_bsg_major > 0)
|
|
||||||
pr2ws("found sg_bsg_major=%d\n", sg_bsg_major);
|
|
||||||
if (sg_nvme_char_major > 0)
|
|
||||||
pr2ws("found sg_nvme_char_major=%d\n", sg_nvme_char_major);
|
|
||||||
} else
|
|
||||||
pr2ws("found no bsg not nvme char device in %s\n", proc_devices);
|
|
||||||
}
|
|
||||||
fclose(fp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Assumes that sg_find_bsg_nvme_char_major() has already been called. Returns
|
|
||||||
* true if dev_fd is a scsi generic pass-through device. If yields
|
|
||||||
* *is_nvme_p = true with *nsid_p = 0 then dev_fd is a NVMe char device.
|
|
||||||
* If yields *nsid_p > 0 then dev_fd is a NVMe block device. */
|
|
||||||
static bool
|
|
||||||
check_file_type(int dev_fd, struct stat * dev_statp, bool * is_bsg_p,
|
|
||||||
bool * is_nvme_p, uint32_t * nsid_p, int * os_err_p,
|
|
||||||
int verbose)
|
|
||||||
{
|
|
||||||
bool is_nvme = false;
|
|
||||||
bool is_sg = false;
|
|
||||||
bool is_bsg = false;
|
|
||||||
bool is_block = false;
|
|
||||||
int os_err = 0;
|
|
||||||
int major_num;
|
|
||||||
uint32_t nsid = 0; /* invalid NSID */
|
|
||||||
|
|
||||||
if (dev_fd >= 0) {
|
|
||||||
if (fstat(dev_fd, dev_statp) < 0) {
|
|
||||||
os_err = errno;
|
|
||||||
if (verbose)
|
|
||||||
pr2ws("%s: fstat() failed: %s (errno=%d)\n", __func__,
|
|
||||||
safe_strerror(os_err), os_err);
|
|
||||||
goto skip_out;
|
|
||||||
}
|
|
||||||
major_num = (int)SG_DEV_MAJOR(dev_statp->st_rdev);
|
|
||||||
if (S_ISCHR(dev_statp->st_mode)) {
|
|
||||||
if (SCSI_GENERIC_MAJOR == major_num)
|
|
||||||
is_sg = true;
|
|
||||||
else if (sg_bsg_major == major_num)
|
|
||||||
is_bsg = true;
|
|
||||||
else if (sg_nvme_char_major == major_num)
|
|
||||||
is_nvme = true;
|
|
||||||
} else if (S_ISBLK(dev_statp->st_mode)) {
|
|
||||||
is_block = true;
|
|
||||||
if (BLOCK_EXT_MAJOR == major_num) {
|
|
||||||
is_nvme = true;
|
|
||||||
nsid = ioctl(dev_fd, NVME_IOCTL_ID, NULL);
|
|
||||||
if (SG_NVME_BROADCAST_NSID == nsid) { /* means ioctl error */
|
|
||||||
os_err = errno;
|
|
||||||
if (verbose)
|
|
||||||
pr2ws("%s: ioctl(NVME_IOCTL_ID) failed: %s "
|
|
||||||
"(errno=%d)\n", __func__, safe_strerror(os_err),
|
|
||||||
os_err);
|
|
||||||
} else
|
|
||||||
os_err = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
os_err = EBADF;
|
|
||||||
if (verbose)
|
|
||||||
pr2ws("%s: invalid file descriptor (%d)\n", __func__, dev_fd);
|
|
||||||
}
|
|
||||||
skip_out:
|
|
||||||
if (verbose > 3) {
|
|
||||||
pr2ws("%s: file descriptor is ", __func__);
|
|
||||||
if (is_sg)
|
|
||||||
pr2ws("sg device\n");
|
|
||||||
else if (is_bsg)
|
|
||||||
pr2ws("bsg device\n");
|
|
||||||
else if (is_nvme && (0 == nsid))
|
|
||||||
pr2ws("NVMe char device\n");
|
|
||||||
else if (is_nvme)
|
|
||||||
pr2ws("NVMe block device, nsid=%lld\n",
|
|
||||||
((uint32_t)-1 == nsid) ? -1LL : (long long)nsid);
|
|
||||||
else if (is_block)
|
|
||||||
pr2ws("block device\n");
|
|
||||||
else
|
|
||||||
pr2ws("undetermined device, could be regular file\n");
|
|
||||||
}
|
|
||||||
if (is_bsg_p)
|
|
||||||
*is_bsg_p = is_bsg;
|
|
||||||
if (is_nvme_p)
|
|
||||||
*is_nvme_p = is_nvme;
|
|
||||||
if (nsid_p)
|
|
||||||
*nsid_p = nsid;
|
|
||||||
if (os_err_p)
|
|
||||||
*os_err_p = os_err;
|
|
||||||
return is_sg;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Assumes dev_fd is an "open" file handle associated with device_name. If
|
|
||||||
* the implementation (possibly for one OS) cannot determine from dev_fd if
|
|
||||||
* a SCSI or NVMe pass-through is referenced, then it might guess based on
|
|
||||||
* device_name. Returns 1 if SCSI generic pass-though device, returns 2 if
|
|
||||||
* secondary SCSI pass-through device (in Linux a bsg device); returns 3 is
|
|
||||||
* char NVMe device (i.e. no NSID); returns 4 if block NVMe device (includes
|
|
||||||
* NSID), or 0 if something else (e.g. ATA block device) or dev_fd < 0.
|
|
||||||
* If error, returns negated errno (operating system) value. */
|
|
||||||
int
|
|
||||||
check_pt_file_handle(int dev_fd, const char * device_name, int verbose)
|
|
||||||
{
|
|
||||||
if (verbose > 4)
|
|
||||||
pr2ws("%s: dev_fd=%d, device_name: %s\n", __func__, dev_fd,
|
|
||||||
device_name);
|
|
||||||
/* Linux doesn't need device_name to determine which pass-through */
|
|
||||||
if (! sg_bsg_nvme_char_major_checked) {
|
|
||||||
sg_bsg_nvme_char_major_checked = true;
|
|
||||||
sg_find_bsg_nvme_char_major(verbose);
|
|
||||||
}
|
|
||||||
if (dev_fd >= 0) {
|
|
||||||
bool is_sg, is_bsg, is_nvme;
|
|
||||||
int err;
|
|
||||||
uint32_t nsid;
|
|
||||||
struct stat a_stat;
|
|
||||||
|
|
||||||
is_sg = check_file_type(dev_fd, &a_stat, &is_bsg, &is_nvme, &nsid,
|
|
||||||
&err, verbose);
|
|
||||||
if (err)
|
|
||||||
return -err;
|
|
||||||
else if (is_sg)
|
|
||||||
return 1;
|
|
||||||
else if (is_bsg)
|
|
||||||
return 2;
|
|
||||||
else if (is_nvme && (0 == nsid))
|
|
||||||
return 3;
|
|
||||||
else if (is_nvme)
|
|
||||||
return 4;
|
|
||||||
else
|
|
||||||
return 0;
|
|
||||||
} else
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We make a runtime decision whether to use the sg v3 interface or the sg
|
|
||||||
* v4 interface (currently exclusively used by the bsg driver). If all the
|
|
||||||
* following are true we use sg v4 which is only currently supported on bsg
|
|
||||||
* device nodes:
|
|
||||||
* a) there is a bsg entry in the /proc/devices file
|
|
||||||
* b) the device node given to scsi_pt_open() is a char device
|
|
||||||
* c) the char major number of the device node given to scsi_pt_open()
|
|
||||||
* matches the char major number of the bsg entry in /proc/devices
|
|
||||||
* Otherwise the sg v3 interface is used.
|
|
||||||
*
|
|
||||||
* Note that in either case we prepare the data in a sg v4 structure. If
|
|
||||||
* the runtime tests indicate that the v3 interface is needed then
|
|
||||||
* do_scsi_pt_v3() transfers the input data into a v3 structure and
|
|
||||||
* then the output data is transferred back into a sg v4 structure.
|
|
||||||
* That implementation detail could change in the future.
|
|
||||||
*
|
|
||||||
* [20120806] Only use MAJOR() macro in kdev_t.h if that header file is
|
|
||||||
* available and major() macro [N.B. lower case] is not available.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef major
|
|
||||||
#define SG_DEV_MAJOR major
|
|
||||||
#else
|
|
||||||
#ifdef HAVE_LINUX_KDEV_T_H
|
|
||||||
#include <linux/kdev_t.h>
|
|
||||||
#endif
|
|
||||||
#define SG_DEV_MAJOR MAJOR /* MAJOR() macro faulty if > 255 minors */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/* Returns >= 0 if successful. If error in Unix returns negated errno. */
|
|
||||||
int
|
|
||||||
scsi_pt_open_device(const char * device_name, bool read_only, int verbose)
|
|
||||||
{
|
|
||||||
int oflags = O_NONBLOCK;
|
|
||||||
|
|
||||||
oflags |= (read_only ? O_RDONLY : O_RDWR);
|
|
||||||
return scsi_pt_open_flags(device_name, oflags, verbose);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Similar to scsi_pt_open_device() but takes Unix style open flags OR-ed */
|
|
||||||
/* together. The 'flags' argument is advisory and may be ignored. */
|
|
||||||
/* Returns >= 0 if successful, otherwise returns negated errno. */
|
|
||||||
int
|
|
||||||
scsi_pt_open_flags(const char * device_name, int flags, int verbose)
|
|
||||||
{
|
|
||||||
int fd;
|
|
||||||
|
|
||||||
if (! sg_bsg_nvme_char_major_checked) {
|
|
||||||
sg_bsg_nvme_char_major_checked = true;
|
|
||||||
sg_find_bsg_nvme_char_major(verbose);
|
|
||||||
}
|
|
||||||
if (verbose > 1) {
|
|
||||||
pr2ws("open %s with flags=0x%x\n", device_name, flags);
|
|
||||||
}
|
|
||||||
fd = open(device_name, flags);
|
|
||||||
if (fd < 0) {
|
|
||||||
fd = -errno;
|
|
||||||
if (verbose > 1)
|
|
||||||
pr2ws("%s: open(%s, 0x%x) failed: %s\n", __func__, device_name,
|
|
||||||
flags, safe_strerror(-fd));
|
|
||||||
}
|
|
||||||
return fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns 0 if successful. If error in Unix returns negated errno. */
|
|
||||||
int
|
|
||||||
scsi_pt_close_device(int device_fd)
|
|
||||||
{
|
|
||||||
int res;
|
|
||||||
|
|
||||||
res = close(device_fd);
|
|
||||||
if (res < 0)
|
|
||||||
res = -errno;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Caller should additionally call get_scsi_pt_os_err() after this call */
|
|
||||||
struct sg_pt_base *
|
|
||||||
construct_scsi_pt_obj_with_fd(int dev_fd, int verbose)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
struct sg_pt_linux_scsi * ptp;
|
|
||||||
|
|
||||||
/* The following 2 lines are temporary. It is to avoid a NULL pointer
|
|
||||||
* crash when an old utility is used with a newer library built after
|
|
||||||
* the sg_warnings_strm cleanup */
|
|
||||||
if (NULL == sg_warnings_strm)
|
|
||||||
sg_warnings_strm = stderr;
|
|
||||||
|
|
||||||
ptp = (struct sg_pt_linux_scsi *)
|
|
||||||
calloc(1, sizeof(struct sg_pt_linux_scsi));
|
|
||||||
if (ptp) {
|
|
||||||
err = set_pt_file_handle((struct sg_pt_base *)ptp, dev_fd, verbose);
|
|
||||||
if ((0 == err) && (! ptp->is_nvme)) {
|
|
||||||
ptp->io_hdr.guard = 'Q';
|
|
||||||
#ifdef BSG_PROTOCOL_SCSI
|
|
||||||
ptp->io_hdr.protocol = BSG_PROTOCOL_SCSI;
|
|
||||||
#endif
|
|
||||||
#ifdef BSG_SUB_PROTOCOL_SCSI_CMD
|
|
||||||
ptp->io_hdr.subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
} else if (verbose)
|
|
||||||
pr2ws("%s: calloc() failed, out of memory?\n", __func__);
|
|
||||||
|
|
||||||
return (struct sg_pt_base *)ptp;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sg_pt_base *
|
|
||||||
construct_scsi_pt_obj()
|
|
||||||
{
|
|
||||||
return construct_scsi_pt_obj_with_fd(-1 /* dev_fd */, 0 /* verbose */);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
destruct_scsi_pt_obj(struct sg_pt_base * vp)
|
|
||||||
{
|
|
||||||
struct sg_pt_linux_scsi * ptp = &vp->impl;
|
|
||||||
|
|
||||||
if (ptp->free_nvme_id_ctlp) {
|
|
||||||
free(ptp->free_nvme_id_ctlp);
|
|
||||||
ptp->free_nvme_id_ctlp = NULL;
|
|
||||||
ptp->nvme_id_ctlp = NULL;
|
|
||||||
}
|
|
||||||
if (ptp)
|
|
||||||
free(ptp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remembers previous device file descriptor */
|
|
||||||
void
|
|
||||||
clear_scsi_pt_obj(struct sg_pt_base * vp)
|
|
||||||
{
|
|
||||||
bool is_sg, is_bsg, is_nvme;
|
|
||||||
int fd;
|
|
||||||
uint32_t nvme_nsid;
|
|
||||||
struct sg_pt_linux_scsi * ptp = &vp->impl;
|
|
||||||
|
|
||||||
if (ptp) {
|
|
||||||
fd = ptp->dev_fd;
|
|
||||||
is_sg = ptp->is_sg;
|
|
||||||
is_bsg = ptp->is_bsg;
|
|
||||||
is_nvme = ptp->is_nvme;
|
|
||||||
nvme_nsid = ptp->nvme_nsid;
|
|
||||||
memset(ptp, 0, sizeof(struct sg_pt_linux_scsi));
|
|
||||||
ptp->io_hdr.guard = 'Q';
|
|
||||||
#ifdef BSG_PROTOCOL_SCSI
|
|
||||||
ptp->io_hdr.protocol = BSG_PROTOCOL_SCSI;
|
|
||||||
#endif
|
|
||||||
#ifdef BSG_SUB_PROTOCOL_SCSI_CMD
|
|
||||||
ptp->io_hdr.subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD;
|
|
||||||
#endif
|
|
||||||
ptp->dev_fd = fd;
|
|
||||||
ptp->is_sg = is_sg;
|
|
||||||
ptp->is_bsg = is_bsg;
|
|
||||||
ptp->is_nvme = is_nvme;
|
|
||||||
ptp->nvme_direct = false;
|
|
||||||
ptp->nvme_nsid = nvme_nsid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Forget any previous dev_fd and install the one given. May attempt to
|
|
||||||
* find file type (e.g. if pass-though) from OS so there could be an error.
|
|
||||||
* Returns 0 for success or the same value as get_scsi_pt_os_err()
|
|
||||||
* will return. dev_fd should be >= 0 for a valid file handle or -1 . */
|
|
||||||
int
|
|
||||||
set_pt_file_handle(struct sg_pt_base * vp, int dev_fd, int verbose)
|
|
||||||
{
|
|
||||||
struct sg_pt_linux_scsi * ptp = &vp->impl;
|
|
||||||
struct stat a_stat;
|
|
||||||
|
|
||||||
if (! sg_bsg_nvme_char_major_checked) {
|
|
||||||
sg_bsg_nvme_char_major_checked = true;
|
|
||||||
sg_find_bsg_nvme_char_major(verbose);
|
|
||||||
}
|
|
||||||
ptp->dev_fd = dev_fd;
|
|
||||||
if (dev_fd >= 0)
|
|
||||||
ptp->is_sg = check_file_type(dev_fd, &a_stat, &ptp->is_bsg,
|
|
||||||
&ptp->is_nvme, &ptp->nvme_nsid,
|
|
||||||
&ptp->os_err, verbose);
|
|
||||||
else {
|
|
||||||
ptp->is_sg = false;
|
|
||||||
ptp->is_bsg = false;
|
|
||||||
ptp->is_nvme = false;
|
|
||||||
ptp->nvme_direct = false;
|
|
||||||
ptp->nvme_nsid = 0;
|
|
||||||
ptp->os_err = 0;
|
|
||||||
}
|
|
||||||
return ptp->os_err;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Valid file handles (which is the return value) are >= 0 . Returns -1
|
|
||||||
* if there is no valid file handle. */
|
|
||||||
int
|
|
||||||
get_pt_file_handle(const struct sg_pt_base * vp)
|
|
||||||
{
|
|
||||||
const struct sg_pt_linux_scsi * ptp = &vp->impl;
|
|
||||||
|
|
||||||
return ptp->dev_fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
set_scsi_pt_cdb(struct sg_pt_base * vp, const unsigned char * cdb,
|
|
||||||
int cdb_len)
|
|
||||||
{
|
|
||||||
struct sg_pt_linux_scsi * ptp = &vp->impl;
|
|
||||||
|
|
||||||
if (ptp->io_hdr.request)
|
|
||||||
++ptp->in_err;
|
|
||||||
ptp->io_hdr.request = (__u64)(sg_uintptr_t)cdb;
|
|
||||||
ptp->io_hdr.request_len = cdb_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
set_scsi_pt_sense(struct sg_pt_base * vp, unsigned char * sense,
|
|
||||||
int max_sense_len)
|
|
||||||
{
|
|
||||||
struct sg_pt_linux_scsi * ptp = &vp->impl;
|
|
||||||
|
|
||||||
if (ptp->io_hdr.response)
|
|
||||||
++ptp->in_err;
|
|
||||||
memset(sense, 0, max_sense_len);
|
|
||||||
ptp->io_hdr.response = (__u64)(sg_uintptr_t)sense;
|
|
||||||
ptp->io_hdr.max_response_len = max_sense_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Setup for data transfer from device */
|
|
||||||
void
|
|
||||||
set_scsi_pt_data_in(struct sg_pt_base * vp, unsigned char * dxferp,
|
|
||||||
int dxfer_ilen)
|
|
||||||
{
|
|
||||||
struct sg_pt_linux_scsi * ptp = &vp->impl;
|
|
||||||
|
|
||||||
if (ptp->io_hdr.din_xferp)
|
|
||||||
++ptp->in_err;
|
|
||||||
if (dxfer_ilen > 0) {
|
|
||||||
ptp->io_hdr.din_xferp = (__u64)(sg_uintptr_t)dxferp;
|
|
||||||
ptp->io_hdr.din_xfer_len = dxfer_ilen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Setup for data transfer toward device */
|
|
||||||
void
|
|
||||||
set_scsi_pt_data_out(struct sg_pt_base * vp, const unsigned char * dxferp,
|
|
||||||
int dxfer_olen)
|
|
||||||
{
|
|
||||||
struct sg_pt_linux_scsi * ptp = &vp->impl;
|
|
||||||
|
|
||||||
if (ptp->io_hdr.dout_xferp)
|
|
||||||
++ptp->in_err;
|
|
||||||
if (dxfer_olen > 0) {
|
|
||||||
ptp->io_hdr.dout_xferp = (__u64)(sg_uintptr_t)dxferp;
|
|
||||||
ptp->io_hdr.dout_xfer_len = dxfer_olen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
set_pt_metadata_xfer(struct sg_pt_base * vp, unsigned char * dxferp,
|
|
||||||
uint32_t dxfer_len, bool out_true)
|
|
||||||
{
|
|
||||||
struct sg_pt_linux_scsi * ptp = &vp->impl;
|
|
||||||
|
|
||||||
if (dxfer_len > 0) {
|
|
||||||
ptp->mdxferp = dxferp;
|
|
||||||
ptp->mdxfer_len = dxfer_len;
|
|
||||||
ptp->mdxfer_out = out_true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
set_scsi_pt_packet_id(struct sg_pt_base * vp, int pack_id)
|
|
||||||
{
|
|
||||||
struct sg_pt_linux_scsi * ptp = &vp->impl;
|
|
||||||
|
|
||||||
ptp->io_hdr.spare_in = pack_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
set_scsi_pt_tag(struct sg_pt_base * vp, uint64_t tag)
|
|
||||||
{
|
|
||||||
struct sg_pt_linux_scsi * ptp = &vp->impl;
|
|
||||||
|
|
||||||
ptp->io_hdr.request_tag = tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Note that task management function codes are transport specific */
|
|
||||||
void
|
|
||||||
set_scsi_pt_task_management(struct sg_pt_base * vp, int tmf_code)
|
|
||||||
{
|
|
||||||
struct sg_pt_linux_scsi * ptp = &vp->impl;
|
|
||||||
|
|
||||||
ptp->io_hdr.subprotocol = 1; /* SCSI task management function */
|
|
||||||
ptp->tmf_request[0] = (unsigned char)tmf_code; /* assume it fits */
|
|
||||||
ptp->io_hdr.request = (__u64)(sg_uintptr_t)(&(ptp->tmf_request[0]));
|
|
||||||
ptp->io_hdr.request_len = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
set_scsi_pt_task_attr(struct sg_pt_base * vp, int attribute, int priority)
|
|
||||||
{
|
|
||||||
struct sg_pt_linux_scsi * ptp = &vp->impl;
|
|
||||||
|
|
||||||
ptp->io_hdr.request_attr = attribute;
|
|
||||||
ptp->io_hdr.request_priority = priority;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef BSG_FLAG_Q_AT_TAIL
|
|
||||||
#define BSG_FLAG_Q_AT_TAIL 0x10
|
|
||||||
#endif
|
|
||||||
#ifndef BSG_FLAG_Q_AT_HEAD
|
|
||||||
#define BSG_FLAG_Q_AT_HEAD 0x20
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Need this later if translated to v3 interface */
|
|
||||||
#ifndef SG_FLAG_Q_AT_TAIL
|
|
||||||
#define SG_FLAG_Q_AT_TAIL 0x10
|
|
||||||
#endif
|
|
||||||
#ifndef SG_FLAG_Q_AT_HEAD
|
|
||||||
#define SG_FLAG_Q_AT_HEAD 0x20
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void
|
|
||||||
set_scsi_pt_flags(struct sg_pt_base * vp, int flags)
|
|
||||||
{
|
|
||||||
struct sg_pt_linux_scsi * ptp = &vp->impl;
|
|
||||||
|
|
||||||
/* default action of bsg driver (sg v4) is QUEUE_AT_HEAD */
|
|
||||||
/* default action of block layer SG_IO ioctl is QUEUE_AT_TAIL */
|
|
||||||
if (SCSI_PT_FLAGS_QUEUE_AT_HEAD & flags) { /* favour AT_HEAD */
|
|
||||||
ptp->io_hdr.flags |= BSG_FLAG_Q_AT_HEAD;
|
|
||||||
ptp->io_hdr.flags &= ~BSG_FLAG_Q_AT_TAIL;
|
|
||||||
} else if (SCSI_PT_FLAGS_QUEUE_AT_TAIL & flags) {
|
|
||||||
ptp->io_hdr.flags |= BSG_FLAG_Q_AT_TAIL;
|
|
||||||
ptp->io_hdr.flags &= ~BSG_FLAG_Q_AT_HEAD;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* N.B. Returns din_resid and ignores dout_resid */
|
|
||||||
int
|
|
||||||
get_scsi_pt_resid(const struct sg_pt_base * vp)
|
|
||||||
{
|
|
||||||
const struct sg_pt_linux_scsi * ptp = &vp->impl;
|
|
||||||
|
|
||||||
if (NULL == ptp)
|
|
||||||
return 0;
|
|
||||||
return ptp->nvme_direct ? 0 : ptp->io_hdr.din_resid;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
get_scsi_pt_status_response(const struct sg_pt_base * vp)
|
|
||||||
{
|
|
||||||
const struct sg_pt_linux_scsi * ptp = &vp->impl;
|
|
||||||
|
|
||||||
if (NULL == ptp)
|
|
||||||
return 0;
|
|
||||||
return (int)(ptp->nvme_direct ? ptp->nvme_status :
|
|
||||||
ptp->io_hdr.device_status);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t
|
|
||||||
get_pt_result(const struct sg_pt_base * vp)
|
|
||||||
{
|
|
||||||
const struct sg_pt_linux_scsi * ptp = &vp->impl;
|
|
||||||
|
|
||||||
if (NULL == ptp)
|
|
||||||
return 0;
|
|
||||||
return ptp->nvme_direct ? ptp->nvme_result :
|
|
||||||
ptp->io_hdr.device_status;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
get_scsi_pt_sense_len(const struct sg_pt_base * vp)
|
|
||||||
{
|
|
||||||
const struct sg_pt_linux_scsi * ptp = &vp->impl;
|
|
||||||
|
|
||||||
return ptp->io_hdr.response_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
get_scsi_pt_duration_ms(const struct sg_pt_base * vp)
|
|
||||||
{
|
|
||||||
const struct sg_pt_linux_scsi * ptp = &vp->impl;
|
|
||||||
|
|
||||||
return ptp->io_hdr.duration;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
get_scsi_pt_transport_err(const struct sg_pt_base * vp)
|
|
||||||
{
|
|
||||||
const struct sg_pt_linux_scsi * ptp = &vp->impl;
|
|
||||||
|
|
||||||
return ptp->io_hdr.transport_status;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
set_scsi_pt_transport_err(struct sg_pt_base * vp, int err)
|
|
||||||
{
|
|
||||||
struct sg_pt_linux_scsi * ptp = &vp->impl;
|
|
||||||
|
|
||||||
ptp->io_hdr.transport_status = err;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns b which will contain a null char terminated string (if
|
|
||||||
* max_b_len > 0). Combined driver and transport (called "host" in Linux
|
|
||||||
* kernel) statuses */
|
|
||||||
char *
|
|
||||||
get_scsi_pt_transport_err_str(const struct sg_pt_base * vp, int max_b_len,
|
|
||||||
char * b)
|
|
||||||
{
|
|
||||||
const struct sg_pt_linux_scsi * ptp = &vp->impl;
|
|
||||||
int ds = ptp->io_hdr.driver_status;
|
|
||||||
int hs = ptp->io_hdr.transport_status;
|
|
||||||
int n, m;
|
|
||||||
char * cp = b;
|
|
||||||
int driv;
|
|
||||||
const char * driv_cp = "invalid";
|
|
||||||
|
|
||||||
if (max_b_len < 1)
|
|
||||||
return b;
|
|
||||||
m = max_b_len;
|
|
||||||
n = 0;
|
|
||||||
if (hs) {
|
|
||||||
if ((hs < 0) || (hs >= LINUX_HOST_BYTES_SZ))
|
|
||||||
n = snprintf(cp, m, "Host_status=0x%02x is invalid\n", hs);
|
|
||||||
else
|
|
||||||
n = snprintf(cp, m, "Host_status=0x%02x [%s]\n", hs,
|
|
||||||
linux_host_bytes[hs]);
|
|
||||||
}
|
|
||||||
m -= n;
|
|
||||||
if (m < 1) {
|
|
||||||
b[max_b_len - 1] = '\0';
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
cp += n;
|
|
||||||
driv = ds & SG_LIB_DRIVER_MASK;
|
|
||||||
if (driv < LINUX_DRIVER_BYTES_SZ)
|
|
||||||
driv_cp = linux_driver_bytes[driv];
|
|
||||||
#if 0
|
|
||||||
sugg = (ds & SG_LIB_SUGGEST_MASK) >> 4;
|
|
||||||
if (sugg < LINUX_DRIVER_SUGGESTS_SZ)
|
|
||||||
sugg_cp = linux_driver_suggests[sugg];
|
|
||||||
#endif
|
|
||||||
n = snprintf(cp, m, "Driver_status=0x%02x [%s]\n", ds, driv_cp);
|
|
||||||
m -= n;
|
|
||||||
if (m < 1)
|
|
||||||
b[max_b_len - 1] = '\0';
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
get_scsi_pt_result_category(const struct sg_pt_base * vp)
|
|
||||||
{
|
|
||||||
const struct sg_pt_linux_scsi * ptp = &vp->impl;
|
|
||||||
int dr_st = ptp->io_hdr.driver_status & SG_LIB_DRIVER_MASK;
|
|
||||||
int scsi_st = ptp->io_hdr.device_status & 0x7e;
|
|
||||||
|
|
||||||
if (ptp->os_err)
|
|
||||||
return SCSI_PT_RESULT_OS_ERR;
|
|
||||||
else if (ptp->io_hdr.transport_status)
|
|
||||||
return SCSI_PT_RESULT_TRANSPORT_ERR;
|
|
||||||
else if (dr_st && (SG_LIB_DRIVER_SENSE != dr_st))
|
|
||||||
return SCSI_PT_RESULT_TRANSPORT_ERR;
|
|
||||||
else if ((SG_LIB_DRIVER_SENSE == dr_st) ||
|
|
||||||
(SAM_STAT_CHECK_CONDITION == scsi_st) ||
|
|
||||||
(SAM_STAT_COMMAND_TERMINATED == scsi_st))
|
|
||||||
return SCSI_PT_RESULT_SENSE;
|
|
||||||
else if (scsi_st)
|
|
||||||
return SCSI_PT_RESULT_STATUS;
|
|
||||||
else
|
|
||||||
return SCSI_PT_RESULT_GOOD;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
get_scsi_pt_os_err(const struct sg_pt_base * vp)
|
|
||||||
{
|
|
||||||
const struct sg_pt_linux_scsi * ptp = &vp->impl;
|
|
||||||
|
|
||||||
return ptp->os_err;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *
|
|
||||||
get_scsi_pt_os_err_str(const struct sg_pt_base * vp, int max_b_len, char * b)
|
|
||||||
{
|
|
||||||
const struct sg_pt_linux_scsi * ptp = &vp->impl;
|
|
||||||
const char * cp;
|
|
||||||
|
|
||||||
cp = safe_strerror(ptp->os_err);
|
|
||||||
strncpy(b, cp, max_b_len);
|
|
||||||
if ((int)strlen(cp) >= max_b_len)
|
|
||||||
b[max_b_len - 1] = '\0';
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
pt_device_is_nvme(const struct sg_pt_base * vp)
|
|
||||||
{
|
|
||||||
const struct sg_pt_linux_scsi * ptp = &vp->impl;
|
|
||||||
|
|
||||||
return ptp->is_nvme;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If a NVMe block device (which includes the NSID) handle is associated
|
|
||||||
* with 'vp', then its NSID is returned (values range from 0x1 to
|
|
||||||
* 0xffffffe). Otherwise 0 is returned. */
|
|
||||||
uint32_t
|
|
||||||
get_pt_nvme_nsid(const struct sg_pt_base * vp)
|
|
||||||
{
|
|
||||||
const struct sg_pt_linux_scsi * ptp = &vp->impl;
|
|
||||||
|
|
||||||
return ptp->nvme_nsid;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Executes SCSI command using sg v3 interface */
|
|
||||||
static int
|
|
||||||
do_scsi_pt_v3(struct sg_pt_linux_scsi * ptp, int fd, int time_secs,
|
|
||||||
int verbose)
|
|
||||||
{
|
|
||||||
struct sg_io_hdr v3_hdr;
|
|
||||||
|
|
||||||
memset(&v3_hdr, 0, sizeof(v3_hdr));
|
|
||||||
/* convert v4 to v3 header */
|
|
||||||
v3_hdr.interface_id = 'S';
|
|
||||||
v3_hdr.dxfer_direction = SG_DXFER_NONE;
|
|
||||||
v3_hdr.cmdp = (unsigned char *)(long)ptp->io_hdr.request;
|
|
||||||
v3_hdr.cmd_len = (unsigned char)ptp->io_hdr.request_len;
|
|
||||||
if (ptp->io_hdr.din_xfer_len > 0) {
|
|
||||||
if (ptp->io_hdr.dout_xfer_len > 0) {
|
|
||||||
if (verbose)
|
|
||||||
pr2ws("sgv3 doesn't support bidi\n");
|
|
||||||
return SCSI_PT_DO_BAD_PARAMS;
|
|
||||||
}
|
|
||||||
v3_hdr.dxferp = (void *)(long)ptp->io_hdr.din_xferp;
|
|
||||||
v3_hdr.dxfer_len = (unsigned int)ptp->io_hdr.din_xfer_len;
|
|
||||||
v3_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
|
|
||||||
} else if (ptp->io_hdr.dout_xfer_len > 0) {
|
|
||||||
v3_hdr.dxferp = (void *)(long)ptp->io_hdr.dout_xferp;
|
|
||||||
v3_hdr.dxfer_len = (unsigned int)ptp->io_hdr.dout_xfer_len;
|
|
||||||
v3_hdr.dxfer_direction = SG_DXFER_TO_DEV;
|
|
||||||
}
|
|
||||||
if (ptp->io_hdr.response && (ptp->io_hdr.max_response_len > 0)) {
|
|
||||||
v3_hdr.sbp = (unsigned char *)(long)ptp->io_hdr.response;
|
|
||||||
v3_hdr.mx_sb_len = (unsigned char)ptp->io_hdr.max_response_len;
|
|
||||||
}
|
|
||||||
v3_hdr.pack_id = (int)ptp->io_hdr.spare_in;
|
|
||||||
if (BSG_FLAG_Q_AT_HEAD & ptp->io_hdr.flags)
|
|
||||||
v3_hdr.flags |= SG_FLAG_Q_AT_HEAD; /* favour AT_HEAD */
|
|
||||||
else if (BSG_FLAG_Q_AT_TAIL & ptp->io_hdr.flags)
|
|
||||||
v3_hdr.flags |= SG_FLAG_Q_AT_TAIL;
|
|
||||||
|
|
||||||
if (NULL == v3_hdr.cmdp) {
|
|
||||||
if (verbose)
|
|
||||||
pr2ws("No SCSI command (cdb) given\n");
|
|
||||||
return SCSI_PT_DO_BAD_PARAMS;
|
|
||||||
}
|
|
||||||
/* io_hdr.timeout is in milliseconds, if greater than zero */
|
|
||||||
v3_hdr.timeout = ((time_secs > 0) ? (time_secs * 1000) : DEF_TIMEOUT);
|
|
||||||
/* Finally do the v3 SG_IO ioctl */
|
|
||||||
if (ioctl(fd, SG_IO, &v3_hdr) < 0) {
|
|
||||||
ptp->os_err = errno;
|
|
||||||
if (verbose > 1)
|
|
||||||
pr2ws("ioctl(SG_IO v3) failed: %s (errno=%d)\n",
|
|
||||||
safe_strerror(ptp->os_err), ptp->os_err);
|
|
||||||
return -ptp->os_err;
|
|
||||||
}
|
|
||||||
ptp->io_hdr.device_status = (__u32)v3_hdr.status;
|
|
||||||
ptp->io_hdr.driver_status = (__u32)v3_hdr.driver_status;
|
|
||||||
ptp->io_hdr.transport_status = (__u32)v3_hdr.host_status;
|
|
||||||
ptp->io_hdr.response_len = (__u32)v3_hdr.sb_len_wr;
|
|
||||||
ptp->io_hdr.duration = (__u32)v3_hdr.duration;
|
|
||||||
ptp->io_hdr.din_resid = (__s32)v3_hdr.resid;
|
|
||||||
/* v3_hdr.info not passed back since no mapping defined (yet) */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Executes SCSI command (or at least forwards it to lower layers).
|
|
||||||
* Returns 0 for success, negative numbers are negated 'errno' values from
|
|
||||||
* OS system calls. Positive return values are errors from this package. */
|
|
||||||
int
|
|
||||||
do_scsi_pt(struct sg_pt_base * vp, int fd, int time_secs, int verbose)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
struct sg_pt_linux_scsi * ptp = &vp->impl;
|
|
||||||
bool have_checked_for_type = (ptp->dev_fd >= 0);
|
|
||||||
|
|
||||||
if (! sg_bsg_nvme_char_major_checked) {
|
|
||||||
sg_bsg_nvme_char_major_checked = true;
|
|
||||||
sg_find_bsg_nvme_char_major(verbose);
|
|
||||||
}
|
|
||||||
if (ptp->in_err) {
|
|
||||||
if (verbose)
|
|
||||||
pr2ws("Replicated or unused set_scsi_pt... functions\n");
|
|
||||||
return SCSI_PT_DO_BAD_PARAMS;
|
|
||||||
}
|
|
||||||
if (fd >= 0) {
|
|
||||||
if ((ptp->dev_fd >= 0) && (fd != ptp->dev_fd)) {
|
|
||||||
if (verbose)
|
|
||||||
pr2ws("%s: file descriptor given to create() and here "
|
|
||||||
"differ\n", __func__);
|
|
||||||
return SCSI_PT_DO_BAD_PARAMS;
|
|
||||||
}
|
|
||||||
ptp->dev_fd = fd;
|
|
||||||
} else if (ptp->dev_fd < 0) {
|
|
||||||
if (verbose)
|
|
||||||
pr2ws("%s: invalid file descriptors\n", __func__);
|
|
||||||
return SCSI_PT_DO_BAD_PARAMS;
|
|
||||||
} else
|
|
||||||
fd = ptp->dev_fd;
|
|
||||||
if (! have_checked_for_type) {
|
|
||||||
err = set_pt_file_handle(vp, ptp->dev_fd, verbose);
|
|
||||||
if (err)
|
|
||||||
return -ptp->os_err;
|
|
||||||
}
|
|
||||||
if (ptp->os_err)
|
|
||||||
return -ptp->os_err;
|
|
||||||
if (ptp->is_nvme)
|
|
||||||
return sg_do_nvme_pt(vp, -1, time_secs, verbose);
|
|
||||||
else if (sg_bsg_major <= 0)
|
|
||||||
return do_scsi_pt_v3(ptp, fd, time_secs, verbose);
|
|
||||||
else if (ptp->is_bsg)
|
|
||||||
; /* drop through to sg v4 implementation */
|
|
||||||
else
|
|
||||||
return do_scsi_pt_v3(ptp, fd, time_secs, verbose);
|
|
||||||
|
|
||||||
if (! ptp->io_hdr.request) {
|
|
||||||
if (verbose)
|
|
||||||
pr2ws("No SCSI command (cdb) given (v4)\n");
|
|
||||||
return SCSI_PT_DO_BAD_PARAMS;
|
|
||||||
}
|
|
||||||
/* io_hdr.timeout is in milliseconds */
|
|
||||||
ptp->io_hdr.timeout = ((time_secs > 0) ? (time_secs * 1000) :
|
|
||||||
DEF_TIMEOUT);
|
|
||||||
#if 0
|
|
||||||
/* sense buffer already zeroed */
|
|
||||||
if (ptp->io_hdr.response && (ptp->io_hdr.max_response_len > 0)) {
|
|
||||||
void * p;
|
|
||||||
|
|
||||||
p = (void *)(long)ptp->io_hdr.response;
|
|
||||||
memset(p, 0, ptp->io_hdr.max_response_len);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (ioctl(fd, SG_IO, &ptp->io_hdr) < 0) {
|
|
||||||
ptp->os_err = errno;
|
|
||||||
if (verbose > 1)
|
|
||||||
pr2ws("ioctl(SG_IO v4) failed: %s (errno=%d)\n",
|
|
||||||
safe_strerror(ptp->os_err), ptp->os_err);
|
|
||||||
return -ptp->os_err;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,516 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2006-2018 Luben Tuikov and 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <getopt.h>
|
|
||||||
#define __STDC_FORMAT_MACROS 1
|
|
||||||
#include <inttypes.h>
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
#include "sg_lib.h"
|
|
||||||
#include "sg_cmds_basic.h"
|
|
||||||
#include "sg_cmds_extra.h"
|
|
||||||
#include "sg_unaligned.h"
|
|
||||||
#include "sg_pr2serr.h"
|
|
||||||
|
|
||||||
#ifdef SG_LIB_WIN32
|
|
||||||
#ifdef SG_LIB_WIN32_DIRECT
|
|
||||||
#include "sg_pt.h" /* needed for scsi_pt_win32_direct() */
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This utility issues the SCSI WRITE BUFFER command to the given device.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static const char * version_str = "1.24 20180111"; /* spc5r18 */
|
|
||||||
|
|
||||||
#define ME "sg_write_buffer: "
|
|
||||||
#define DEF_XFER_LEN (8 * 1024 * 1024)
|
|
||||||
#define EBUFF_SZ 256
|
|
||||||
|
|
||||||
#define WRITE_BUFFER_CMD 0x3b
|
|
||||||
#define WRITE_BUFFER_CMDLEN 10
|
|
||||||
#define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */
|
|
||||||
#define DEF_PT_TIMEOUT 300 /* 300 seconds, 5 minutes */
|
|
||||||
|
|
||||||
static struct option long_options[] = {
|
|
||||||
{"bpw", required_argument, 0, 'b'},
|
|
||||||
{"dry-run", no_argument, 0, 'd'},
|
|
||||||
{"dry_run", no_argument, 0, 'd'},
|
|
||||||
{"help", no_argument, 0, 'h'},
|
|
||||||
{"id", required_argument, 0, 'i'},
|
|
||||||
{"in", required_argument, 0, 'I'},
|
|
||||||
{"length", required_argument, 0, 'l'},
|
|
||||||
{"mode", required_argument, 0, 'm'},
|
|
||||||
{"offset", required_argument, 0, 'o'},
|
|
||||||
{"read-stdin", no_argument, 0, 'r'},
|
|
||||||
{"read_stdin", no_argument, 0, 'r'},
|
|
||||||
{"raw", no_argument, 0, 'r'},
|
|
||||||
{"skip", required_argument, 0, 's'},
|
|
||||||
{"specific", required_argument, 0, 'S'},
|
|
||||||
{"timeout", required_argument, 0, 't' },
|
|
||||||
{"verbose", no_argument, 0, 'v'},
|
|
||||||
{"version", no_argument, 0, 'V'},
|
|
||||||
{0, 0, 0, 0},
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
usage()
|
|
||||||
{
|
|
||||||
pr2serr("Usage: "
|
|
||||||
"sg_write_buffer [--bpw=CS] [--dry-run] [--help] [--id=ID] "
|
|
||||||
"[--in=FILE]\n"
|
|
||||||
" [--length=LEN] [--mode=MO] "
|
|
||||||
"[--offset=OFF]\n"
|
|
||||||
" [--read-stdin] [--skip=SKIP] "
|
|
||||||
"[--specific=MS]\n"
|
|
||||||
" [--timeout=TO] [--verbose] [--version] "
|
|
||||||
"DEVICE\n"
|
|
||||||
" where:\n"
|
|
||||||
" --bpw=CS|-b CS CS is chunk size: bytes per write "
|
|
||||||
"buffer\n"
|
|
||||||
" command (def: 0 -> as many as "
|
|
||||||
"possible)\n"
|
|
||||||
" --dry-run|-d skip WRITE BUFFER commands, do "
|
|
||||||
"everything else\n"
|
|
||||||
" --help|-h print out usage message then exit\n"
|
|
||||||
" --id=ID|-i ID buffer identifier (0 (default) to "
|
|
||||||
"255)\n"
|
|
||||||
" --in=FILE|-I FILE read from FILE ('-I -' read "
|
|
||||||
"from stdin)\n"
|
|
||||||
" --length=LEN|-l LEN length in bytes to write; may be "
|
|
||||||
"deduced from\n"
|
|
||||||
" FILE\n"
|
|
||||||
" --mode=MO|-m MO write buffer mode, MO is number or "
|
|
||||||
"acronym\n"
|
|
||||||
" (def: 0 -> 'combined header and "
|
|
||||||
"data' (obs))\n"
|
|
||||||
" --offset=OFF|-o OFF buffer offset (unit: bytes, def: 0)\n"
|
|
||||||
" --read-stdin|-r read from stdin (same as '-I -')\n"
|
|
||||||
" --skip=SKIP|-s SKIP bytes in file FILE to skip before "
|
|
||||||
"reading\n"
|
|
||||||
" --specific=MS|-S MS mode specific value; 3 bit field "
|
|
||||||
"(0 to 7)\n"
|
|
||||||
" --timeout=TO|-t TO command timeout in seconds (def: "
|
|
||||||
"300)\n"
|
|
||||||
" --verbose|-v increase verbosity\n"
|
|
||||||
" --version|-V print version string and exit\n\n"
|
|
||||||
"Performs one or more SCSI WRITE BUFFER commands. Use '-m xxx' "
|
|
||||||
"to list\navailable modes. A chunk size of 4 KB ('--bpw=4k') "
|
|
||||||
"seems to work well.\nExample: sg_write_buffer -b 4k -I xxx.lod "
|
|
||||||
"-m 7 /dev/sg3\n"
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#define MODE_HEADER_DATA 0
|
|
||||||
#define MODE_VENDOR 1
|
|
||||||
#define MODE_DATA 2
|
|
||||||
#define MODE_DNLD_MC 4
|
|
||||||
#define MODE_DNLD_MC_SAVE 5
|
|
||||||
#define MODE_DNLD_MC_OFFS 6
|
|
||||||
#define MODE_DNLD_MC_OFFS_SAVE 7
|
|
||||||
#define MODE_ECHO_BUFFER 0x0A
|
|
||||||
#define MODE_DNLD_MC_EV_OFFS_DEFER 0x0D
|
|
||||||
#define MODE_DNLD_MC_OFFS_DEFER 0x0E
|
|
||||||
#define MODE_ACTIVATE_MC 0x0F
|
|
||||||
#define MODE_EN_EX_ECHO 0x1A
|
|
||||||
#define MODE_DIS_EX 0x1B
|
|
||||||
#define MODE_DNLD_ERR_HISTORY 0x1C
|
|
||||||
|
|
||||||
|
|
||||||
struct mode_s {
|
|
||||||
const char *mode_string;
|
|
||||||
int mode;
|
|
||||||
const char *comment;
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct mode_s mode_arr[] = {
|
|
||||||
{"hd", MODE_HEADER_DATA, "combined header and data "
|
|
||||||
"(obsolete)"},
|
|
||||||
{"vendor", MODE_VENDOR, "vendor specific"},
|
|
||||||
{"data", MODE_DATA, "data"},
|
|
||||||
{"dmc", MODE_DNLD_MC, "download microcode and activate"},
|
|
||||||
{"dmc_save", MODE_DNLD_MC_SAVE, "download microcode, save and "
|
|
||||||
"activate"},
|
|
||||||
{"dmc_offs", MODE_DNLD_MC_OFFS, "download microcode with offsets "
|
|
||||||
"and activate"},
|
|
||||||
{"dmc_offs_save", MODE_DNLD_MC_OFFS_SAVE, "download microcode with "
|
|
||||||
"offsets, save and\n\t\t\t\tactivate"},
|
|
||||||
{"echo", MODE_ECHO_BUFFER, "write data to echo buffer"},
|
|
||||||
{"dmc_offs_ev_defer", MODE_DNLD_MC_EV_OFFS_DEFER, "download "
|
|
||||||
"microcode with offsets, select\n\t\t\t\tactivation event, "
|
|
||||||
"save and defer activation"},
|
|
||||||
{"dmc_offs_defer", MODE_DNLD_MC_OFFS_DEFER, "download microcode "
|
|
||||||
"with offsets, save and\n\t\t\t\tdefer activation"},
|
|
||||||
{"activate_mc", MODE_ACTIVATE_MC, "activate deferred microcode"},
|
|
||||||
{"en_ex", MODE_EN_EX_ECHO, "enable expander communications "
|
|
||||||
"protocol and\n\t\t\t\techo buffer (obsolete)"},
|
|
||||||
{"dis_ex", MODE_DIS_EX, "disable expander communications "
|
|
||||||
"protocol\n\t\t\t\t(obsolete)"},
|
|
||||||
{"deh", MODE_DNLD_ERR_HISTORY, "download application client "
|
|
||||||
"error history "},
|
|
||||||
{NULL, 0, NULL},
|
|
||||||
};
|
|
||||||
|
|
||||||
static void
|
|
||||||
print_modes(void)
|
|
||||||
{
|
|
||||||
const struct mode_s * mp;
|
|
||||||
|
|
||||||
pr2serr("The modes parameter argument can be numeric (hex or decimal)\n"
|
|
||||||
"or symbolic:\n");
|
|
||||||
for (mp = mode_arr; mp->mode_string; ++mp) {
|
|
||||||
pr2serr(" %2d (0x%02x) %-18s%s\n", mp->mode, mp->mode,
|
|
||||||
mp->mode_string, mp->comment);
|
|
||||||
}
|
|
||||||
pr2serr("\nAdditionally '--bpw=<val>,act' does a activate deferred "
|
|
||||||
"microcode after\nsuccessful dmc_offs_defer and "
|
|
||||||
"dmc_offs_ev_defer mode downloads.\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int
|
|
||||||
main(int argc, char * argv[])
|
|
||||||
{
|
|
||||||
bool bpw_then_activate = false;
|
|
||||||
bool dry_run = false;
|
|
||||||
bool got_stdin = false;
|
|
||||||
bool wb_len_given = false;
|
|
||||||
int sg_fd, infd, res, c, len, k, n;
|
|
||||||
int bpw = 0;
|
|
||||||
int do_help = 0;
|
|
||||||
int ret = 0;
|
|
||||||
int verbose = 0;
|
|
||||||
int wb_id = 0;
|
|
||||||
int wb_len = 0;
|
|
||||||
int wb_mode = 0;
|
|
||||||
int wb_offset = 0;
|
|
||||||
int wb_skip = 0;
|
|
||||||
int wb_timeout = DEF_PT_TIMEOUT;
|
|
||||||
int wb_mspec = 0;
|
|
||||||
const char * device_name = NULL;
|
|
||||||
const char * file_name = NULL;
|
|
||||||
unsigned char * dop = NULL;
|
|
||||||
char * cp;
|
|
||||||
const struct mode_s * mp;
|
|
||||||
char ebuff[EBUFF_SZ];
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
int option_index = 0;
|
|
||||||
|
|
||||||
c = getopt_long(argc, argv, "b:dhi:I:l:m:o:rs:S:t:vV", long_options,
|
|
||||||
&option_index);
|
|
||||||
if (c == -1)
|
|
||||||
break;
|
|
||||||
|
|
||||||
switch (c) {
|
|
||||||
case 'b':
|
|
||||||
bpw = sg_get_num(optarg);
|
|
||||||
if (bpw < 0) {
|
|
||||||
pr2serr("argument to '--bpw' should be in a positive "
|
|
||||||
"number\n");
|
|
||||||
return SG_LIB_SYNTAX_ERROR;
|
|
||||||
}
|
|
||||||
if ((cp = strchr(optarg, ','))) {
|
|
||||||
if (0 == strncmp("act", cp + 1, 3))
|
|
||||||
bpw_then_activate = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'd':
|
|
||||||
dry_run = true;
|
|
||||||
break;
|
|
||||||
case 'h':
|
|
||||||
case '?':
|
|
||||||
++do_help;
|
|
||||||
break;
|
|
||||||
case 'i':
|
|
||||||
wb_id = sg_get_num(optarg);
|
|
||||||
if ((wb_id < 0) || (wb_id > 255)) {
|
|
||||||
pr2serr("argument to '--id' should be in the range 0 to "
|
|
||||||
"255\n");
|
|
||||||
return SG_LIB_SYNTAX_ERROR;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'I':
|
|
||||||
file_name = optarg;
|
|
||||||
break;
|
|
||||||
case 'l':
|
|
||||||
wb_len = sg_get_num(optarg);
|
|
||||||
if (wb_len < 0) {
|
|
||||||
pr2serr("bad argument to '--length'\n");
|
|
||||||
return SG_LIB_SYNTAX_ERROR;
|
|
||||||
}
|
|
||||||
wb_len_given = true;
|
|
||||||
break;
|
|
||||||
case 'm':
|
|
||||||
if (isdigit(*optarg)) {
|
|
||||||
wb_mode = sg_get_num(optarg);
|
|
||||||
if ((wb_mode < 0) || (wb_mode > 31)) {
|
|
||||||
pr2serr("argument to '--mode' should be in the range 0 "
|
|
||||||
"to 31\n");
|
|
||||||
return SG_LIB_SYNTAX_ERROR;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
len = strlen(optarg);
|
|
||||||
for (mp = mode_arr; mp->mode_string; ++mp) {
|
|
||||||
if (0 == strncmp(mp->mode_string, optarg, len)) {
|
|
||||||
wb_mode = mp->mode;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (! mp->mode_string) {
|
|
||||||
print_modes();
|
|
||||||
return SG_LIB_SYNTAX_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'o':
|
|
||||||
wb_offset = sg_get_num(optarg);
|
|
||||||
if (wb_offset < 0) {
|
|
||||||
pr2serr("bad argument to '--offset'\n");
|
|
||||||
return SG_LIB_SYNTAX_ERROR;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'r': /* --read-stdin and --raw (previous name) */
|
|
||||||
file_name = "-";
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
wb_skip = sg_get_num(optarg);
|
|
||||||
if (wb_skip < 0) {
|
|
||||||
pr2serr("bad argument to '--skip'\n");
|
|
||||||
return SG_LIB_SYNTAX_ERROR;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'S':
|
|
||||||
wb_mspec = sg_get_num(optarg);
|
|
||||||
if ((wb_mspec < 0) || (wb_mspec > 7)) {
|
|
||||||
pr2serr("expected argument to '--specific' to be 0 to 7\n");
|
|
||||||
return SG_LIB_SYNTAX_ERROR;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 't':
|
|
||||||
wb_timeout = sg_get_num(optarg);
|
|
||||||
if (wb_timeout < 0) {
|
|
||||||
pr2serr("Invalid argument to '--timeout'\n");
|
|
||||||
return SG_LIB_SYNTAX_ERROR;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'v':
|
|
||||||
++verbose;
|
|
||||||
break;
|
|
||||||
case 'V':
|
|
||||||
pr2serr(ME "version: %s\n", version_str);
|
|
||||||
return 0;
|
|
||||||
default:
|
|
||||||
pr2serr("unrecognised option code 0x%x ??\n", c);
|
|
||||||
usage();
|
|
||||||
return SG_LIB_SYNTAX_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (do_help) {
|
|
||||||
if (do_help > 1) {
|
|
||||||
usage();
|
|
||||||
pr2serr("\n");
|
|
||||||
print_modes();
|
|
||||||
} else
|
|
||||||
usage();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (optind < argc) {
|
|
||||||
if (NULL == device_name) {
|
|
||||||
device_name = argv[optind];
|
|
||||||
++optind;
|
|
||||||
}
|
|
||||||
if (optind < argc) {
|
|
||||||
for (; optind < argc; ++optind)
|
|
||||||
pr2serr("Unexpected extra argument: %s\n", argv[optind]);
|
|
||||||
usage();
|
|
||||||
return SG_LIB_SYNTAX_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NULL == device_name) {
|
|
||||||
pr2serr("missing device name!\n");
|
|
||||||
usage();
|
|
||||||
return SG_LIB_SYNTAX_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((wb_len > 0) && (bpw > wb_len)) {
|
|
||||||
pr2serr("trim chunk size (CS) to be the same as LEN\n");
|
|
||||||
bpw = wb_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef SG_LIB_WIN32
|
|
||||||
#ifdef SG_LIB_WIN32_DIRECT
|
|
||||||
if (verbose > 4)
|
|
||||||
pr2serr("Initial win32 SPT interface state: %s\n",
|
|
||||||
scsi_pt_win32_spt_state() ? "direct" : "indirect");
|
|
||||||
scsi_pt_win32_direct(SG_LIB_WIN32_DIRECT /* SPT pt interface */);
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
sg_fd = sg_cmds_open_device(device_name, false /* rw */, verbose);
|
|
||||||
if (sg_fd < 0) {
|
|
||||||
pr2serr(ME "open error: %s: %s\n", device_name,
|
|
||||||
safe_strerror(-sg_fd));
|
|
||||||
return SG_LIB_FILE_ERROR;
|
|
||||||
}
|
|
||||||
if (file_name || (wb_len > 0)) {
|
|
||||||
if (0 == wb_len)
|
|
||||||
wb_len = DEF_XFER_LEN;
|
|
||||||
if (NULL == (dop = (unsigned char *)malloc(wb_len))) {
|
|
||||||
pr2serr(ME "out of memory\n");
|
|
||||||
ret = SG_LIB_SYNTAX_ERROR;
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
memset(dop, 0xff, wb_len);
|
|
||||||
if (file_name) {
|
|
||||||
got_stdin = (0 == strcmp(file_name, "-"));
|
|
||||||
if (got_stdin) {
|
|
||||||
if (wb_skip > 0) {
|
|
||||||
pr2serr("Can't skip on stdin\n");
|
|
||||||
ret = SG_LIB_FILE_ERROR;
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
infd = STDIN_FILENO;
|
|
||||||
} else {
|
|
||||||
if ((infd = open(file_name, O_RDONLY)) < 0) {
|
|
||||||
snprintf(ebuff, EBUFF_SZ,
|
|
||||||
ME "could not open %s for reading", file_name);
|
|
||||||
perror(ebuff);
|
|
||||||
ret = SG_LIB_FILE_ERROR;
|
|
||||||
goto err_out;
|
|
||||||
} else if (sg_set_binary_mode(infd) < 0)
|
|
||||||
perror("sg_set_binary_mode");
|
|
||||||
if (wb_skip > 0) {
|
|
||||||
if (lseek(infd, wb_skip, SEEK_SET) < 0) {
|
|
||||||
snprintf(ebuff, EBUFF_SZ, ME "couldn't skip to "
|
|
||||||
"required position on %s", file_name);
|
|
||||||
perror(ebuff);
|
|
||||||
close(infd);
|
|
||||||
ret = SG_LIB_FILE_ERROR;
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
res = read(infd, dop, wb_len);
|
|
||||||
if (res < 0) {
|
|
||||||
snprintf(ebuff, EBUFF_SZ, ME "couldn't read from %s",
|
|
||||||
file_name);
|
|
||||||
perror(ebuff);
|
|
||||||
if (! got_stdin)
|
|
||||||
close(infd);
|
|
||||||
ret = SG_LIB_FILE_ERROR;
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
if (res < wb_len) {
|
|
||||||
if (wb_len_given) {
|
|
||||||
pr2serr("tried to read %d bytes from %s, got %d bytes\n",
|
|
||||||
wb_len, file_name, res);
|
|
||||||
pr2serr("pad with 0xff bytes and continue\n");
|
|
||||||
} else {
|
|
||||||
if (verbose) {
|
|
||||||
pr2serr("tried to read %d bytes from %s, got %d "
|
|
||||||
"bytes\n", wb_len, file_name, res);
|
|
||||||
pr2serr("will write %d bytes", res);
|
|
||||||
if ((bpw > 0) && (bpw < wb_len))
|
|
||||||
pr2serr(", %d bytes per WRITE BUFFER command\n",
|
|
||||||
bpw);
|
|
||||||
else
|
|
||||||
pr2serr("\n");
|
|
||||||
}
|
|
||||||
wb_len = res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (! got_stdin)
|
|
||||||
close(infd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
res = 0;
|
|
||||||
if (bpw > 0) {
|
|
||||||
for (k = 0; k < wb_len; k += n) {
|
|
||||||
n = wb_len - k;
|
|
||||||
if (n > bpw)
|
|
||||||
n = bpw;
|
|
||||||
if (verbose)
|
|
||||||
pr2serr("sending write buffer, mode=0x%x, mspec=%d, id=%d, "
|
|
||||||
" offset=%d, len=%d\n", wb_mode, wb_mspec, wb_id,
|
|
||||||
wb_offset + k, n);
|
|
||||||
if (dry_run) {
|
|
||||||
if (verbose)
|
|
||||||
pr2serr("skipping WRITE BUFFER command due to "
|
|
||||||
"--dry-run\n");
|
|
||||||
res = 0;
|
|
||||||
} else
|
|
||||||
res = sg_ll_write_buffer_v2(sg_fd, wb_mode, wb_mspec, wb_id,
|
|
||||||
wb_offset + k, dop + k, n,
|
|
||||||
wb_timeout, true, verbose);
|
|
||||||
if (res)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (bpw_then_activate) {
|
|
||||||
if (verbose)
|
|
||||||
pr2serr("sending Activate deferred microcode [0xf]\n");
|
|
||||||
if (dry_run) {
|
|
||||||
if (verbose)
|
|
||||||
pr2serr("skipping WRITE BUFFER(ACTIVATE) command due to "
|
|
||||||
"--dry-run\n");
|
|
||||||
res = 0;
|
|
||||||
} else
|
|
||||||
res = sg_ll_write_buffer_v2(sg_fd, MODE_ACTIVATE_MC,
|
|
||||||
0 /* buffer_id */,
|
|
||||||
0 /* buffer_offset */, 0,
|
|
||||||
NULL, 0, wb_timeout, true,
|
|
||||||
verbose);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (verbose)
|
|
||||||
pr2serr("sending single write buffer, mode=0x%x, mpsec=%d, "
|
|
||||||
"id=%d, offset=%d, len=%d\n", wb_mode, wb_mspec, wb_id,
|
|
||||||
wb_offset, wb_len);
|
|
||||||
if (dry_run) {
|
|
||||||
if (verbose)
|
|
||||||
pr2serr("skipping WRITE BUFFER(all in one) command due to "
|
|
||||||
"--dry-run\n");
|
|
||||||
res = 0;
|
|
||||||
} else
|
|
||||||
res = sg_ll_write_buffer_v2(sg_fd, wb_mode, wb_mspec, wb_id,
|
|
||||||
wb_offset, dop, wb_len, wb_timeout,
|
|
||||||
true, verbose);
|
|
||||||
}
|
|
||||||
if (0 != res) {
|
|
||||||
char b[80];
|
|
||||||
|
|
||||||
ret = res;
|
|
||||||
sg_get_category_sense_str(res, sizeof(b), b, verbose);
|
|
||||||
pr2serr("Write buffer failed: %s\n", b);
|
|
||||||
}
|
|
||||||
|
|
||||||
err_out:
|
|
||||||
if (dop)
|
|
||||||
free(dop);
|
|
||||||
res = sg_cmds_close_device(sg_fd);
|
|
||||||
if (res < 0) {
|
|
||||||
pr2serr("close error: %s\n", safe_strerror(-res));
|
|
||||||
if (0 == ret)
|
|
||||||
return SG_LIB_FILE_ERROR;
|
|
||||||
}
|
|
||||||
return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user