mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-tools.git
synced 2024-11-24 02:03:37 +08:00
tools: sg_write_buffer: add sg_write_buffer for FFU
sg_write_buffer sources are added for FFU. Signed-off-by: Hyojun Kim <hyojun@google.com> Signed-off-by: Jaegeuk Kim <jaegeuk@google.com>
This commit is contained in:
parent
254cdad2ee
commit
b39f62dce4
@ -196,6 +196,7 @@ AC_CONFIG_FILES([
|
||||
mkfs/Makefile
|
||||
fsck/Makefile
|
||||
tools/Makefile
|
||||
tools/sg_write_buffer/Makefile
|
||||
])
|
||||
|
||||
# export library version info for mkfs/libf2fs_format_la
|
||||
|
@ -13,3 +13,5 @@ f2fscrypt_SOURCES = f2fscrypt.c sha512.c
|
||||
f2fscrypt_LDFLAGS = -luuid
|
||||
dist_man_MANS = f2fscrypt.8
|
||||
endif
|
||||
|
||||
SUBDIRS = sg_write_buffer
|
||||
|
27
tools/sg_write_buffer/Android.bp
Normal file
27
tools/sg_write_buffer/Android.bp
Normal file
@ -0,0 +1,27 @@
|
||||
cc_defaults {
|
||||
name: "sg3-utils-defaults",
|
||||
cflags: [
|
||||
"-Wno-unused-function"
|
||||
],
|
||||
local_include_dirs: [
|
||||
"include",
|
||||
],
|
||||
}
|
||||
|
||||
cc_binary {
|
||||
name: "sg_write_buffer",
|
||||
defaults: [ "sg3-utils-defaults" ],
|
||||
srcs: [
|
||||
"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",
|
||||
],
|
||||
}
|
18
tools/sg_write_buffer/Makefile.am
Normal file
18
tools/sg_write_buffer/Makefile.am
Normal file
@ -0,0 +1,18 @@
|
||||
## Makefile.am
|
||||
|
||||
if LINUX
|
||||
AM_CPPFLAGS = -I./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
|
156
tools/sg_write_buffer/include/freebsd_nvme_ioctl.h
Normal file
156
tools/sg_write_buffer/include/freebsd_nvme_ioctl.h
Normal file
@ -0,0 +1,156 @@
|
||||
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"
|
21
tools/sg_write_buffer/include/sg_cmds.h
Normal file
21
tools/sg_write_buffer/include/sg_cmds.h
Normal file
@ -0,0 +1,21 @@
|
||||
#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
|
310
tools/sg_write_buffer/include/sg_cmds_basic.h
Normal file
310
tools/sg_write_buffer/include/sg_cmds_basic.h
Normal file
@ -0,0 +1,310 @@
|
||||
#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
|
369
tools/sg_write_buffer/include/sg_cmds_extra.h
Normal file
369
tools/sg_write_buffer/include/sg_cmds_extra.h
Normal file
@ -0,0 +1,369 @@
|
||||
#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
|
52
tools/sg_write_buffer/include/sg_cmds_mmc.h
Normal file
52
tools/sg_write_buffer/include/sg_cmds_mmc.h
Normal file
@ -0,0 +1,52 @@
|
||||
#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
|
185
tools/sg_write_buffer/include/sg_io_linux.h
Normal file
185
tools/sg_write_buffer/include/sg_io_linux.h
Normal file
@ -0,0 +1,185 @@
|
||||
#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
|
602
tools/sg_write_buffer/include/sg_lib.h
Normal file
602
tools/sg_write_buffer/include/sg_lib.h
Normal file
@ -0,0 +1,602 @@
|
||||
#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 */
|
121
tools/sg_write_buffer/include/sg_lib_data.h
Normal file
121
tools/sg_write_buffer/include/sg_lib_data.h
Normal file
@ -0,0 +1,121 @@
|
||||
#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
|
56
tools/sg_write_buffer/include/sg_linux_inc.h
Normal file
56
tools/sg_write_buffer/include/sg_linux_inc.h
Normal file
@ -0,0 +1,56 @@
|
||||
#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
|
30
tools/sg_write_buffer/include/sg_pr2serr.h
Normal file
30
tools/sg_write_buffer/include/sg_pr2serr.h
Normal file
@ -0,0 +1,30 @@
|
||||
#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
|
215
tools/sg_write_buffer/include/sg_pt.h
Normal file
215
tools/sg_write_buffer/include/sg_pt.h
Normal file
@ -0,0 +1,215 @@
|
||||
#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
|
171
tools/sg_write_buffer/include/sg_pt_linux.h
Normal file
171
tools/sg_write_buffer/include/sg_pt_linux.h
Normal file
@ -0,0 +1,171 @@
|
||||
#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 */
|
172
tools/sg_write_buffer/include/sg_pt_nvme.h
Normal file
172
tools/sg_write_buffer/include/sg_pt_nvme.h
Normal file
@ -0,0 +1,172 @@
|
||||
#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 */
|
473
tools/sg_write_buffer/include/sg_pt_win32.h
Normal file
473
tools/sg_write_buffer/include/sg_pt_win32.h
Normal file
@ -0,0 +1,473 @@
|
||||
#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
|
325
tools/sg_write_buffer/include/sg_unaligned.h
Normal file
325
tools/sg_write_buffer/include/sg_unaligned.h
Normal file
@ -0,0 +1,325 @@
|
||||
#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 */
|
663
tools/sg_write_buffer/sg_cmds_basic.c
Normal file
663
tools/sg_write_buffer/sg_cmds_basic.c
Normal file
@ -0,0 +1,663 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
1069
tools/sg_write_buffer/sg_cmds_basic2.c
Normal file
1069
tools/sg_write_buffer/sg_cmds_basic2.c
Normal file
File diff suppressed because it is too large
Load Diff
2524
tools/sg_write_buffer/sg_cmds_extra.c
Normal file
2524
tools/sg_write_buffer/sg_cmds_extra.c
Normal file
File diff suppressed because it is too large
Load Diff
382
tools/sg_write_buffer/sg_cmds_mmc.c
Normal file
382
tools/sg_write_buffer/sg_cmds_mmc.c
Normal file
@ -0,0 +1,382 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
256
tools/sg_write_buffer/sg_io_linux.c
Normal file
256
tools/sg_write_buffer/sg_io_linux.c
Normal file
@ -0,0 +1,256 @@
|
||||
/*
|
||||
* 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 */
|
3494
tools/sg_write_buffer/sg_lib.c
Normal file
3494
tools/sg_write_buffer/sg_lib.c
Normal file
File diff suppressed because it is too large
Load Diff
1688
tools/sg_write_buffer/sg_lib_data.c
Normal file
1688
tools/sg_write_buffer/sg_lib_data.c
Normal file
File diff suppressed because it is too large
Load Diff
141
tools/sg_write_buffer/sg_pt_common.c
Normal file
141
tools/sg_write_buffer/sg_pt_common.c
Normal file
@ -0,0 +1,141 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
964
tools/sg_write_buffer/sg_pt_linux.c
Normal file
964
tools/sg_write_buffer/sg_pt_linux.c
Normal file
@ -0,0 +1,964 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
1185
tools/sg_write_buffer/sg_pt_linux_nvme.c
Normal file
1185
tools/sg_write_buffer/sg_pt_linux_nvme.c
Normal file
File diff suppressed because it is too large
Load Diff
516
tools/sg_write_buffer/sg_write_buffer.c
Normal file
516
tools/sg_write_buffer/sg_write_buffer.c
Normal file
@ -0,0 +1,516 @@
|
||||
/*
|
||||
* 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