Remove sg_write_buffer

Remove the sg_write_buffer source code and build rules now that the
sg3_utils project has been imported.

Signed-off-by: Bart Van Assche <bvanassche@google.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
This commit is contained in:
Bart Van Assche 2022-11-23 16:34:49 -08:00 committed by sunshenshen
parent c027dc1905
commit f3344cf6db
30 changed files with 1 additions and 16161 deletions

1
.gitignore vendored
View File

@ -51,7 +51,6 @@ stamp-h1
/tools/parse.f2fs
/tools/f2fscrypt
/tools/f2fs_io/f2fs_io
/tools/sg_write_buffer/sg_write_buffer
# cscope files
cscope.*

View File

@ -260,7 +260,6 @@ AC_CONFIG_FILES([
mkfs/Makefile
fsck/Makefile
tools/Makefile
tools/sg_write_buffer/Makefile
tools/f2fs_io/Makefile
])

View File

@ -17,4 +17,4 @@ f2fscrypt_LDFLAGS = ${libuuid_LIBS}
dist_man_MANS = f2fscrypt.8
endif
SUBDIRS = sg_write_buffer f2fs_io
SUBDIRS = f2fs_io

View File

@ -1,18 +0,0 @@
## Makefile.am
if LINUX
AM_CPPFLAGS = -I$(srcdir)/include
AM_CFLAGS = -Wall
sbin_PROGRAMS = sg_write_buffer
sg_write_buffer_SOURCES = sg_write_buffer.c \
sg_cmds_basic.c \
sg_cmds_basic2.c \
sg_cmds_extra.c \
sg_cmds_mmc.c \
sg_io_linux.c \
sg_lib.c \
sg_lib_data.c \
sg_pt_common.c \
sg_pt_linux.c \
sg_pt_linux_nvme.c
endif

View File

@ -1,156 +0,0 @@
PROPS-END
/*-
* Copyright (C) 2012-2013 Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <sys/param.h>
#define NVME_PASSTHROUGH_CMD _IOWR('n', 0, struct nvme_pt_command)
#if __FreeBSD_version < 1100110
struct nvme_command
{
/* dword 0 */
uint16_t opc : 8; /* opcode */
uint16_t fuse : 2; /* fused operation */
uint16_t rsvd1 : 6;
uint16_t cid; /* command identifier */
/* dword 1 */
uint32_t nsid; /* namespace identifier */
/* dword 2-3 */
uint32_t rsvd2;
uint32_t rsvd3;
/* dword 4-5 */
uint64_t mptr; /* metadata pointer */
/* dword 6-7 */
uint64_t prp1; /* prp entry 1 */
/* dword 8-9 */
uint64_t prp2; /* prp entry 2 */
/* dword 10-15 */
uint32_t cdw10; /* command-specific */
uint32_t cdw11; /* command-specific */
uint32_t cdw12; /* command-specific */
uint32_t cdw13; /* command-specific */
uint32_t cdw14; /* command-specific */
uint32_t cdw15; /* command-specific */
} __packed;
struct nvme_status {
uint16_t p : 1; /* phase tag */
uint16_t sc : 8; /* status code */
uint16_t sct : 3; /* status code type */
uint16_t rsvd2 : 2;
uint16_t m : 1; /* more */
uint16_t dnr : 1; /* do not retry */
} __packed;
struct nvme_completion {
/* dword 0 */
uint32_t cdw0; /* command-specific */
/* dword 1 */
uint32_t rsvd1;
/* dword 2 */
uint16_t sqhd; /* submission queue head pointer */
uint16_t sqid; /* submission queue identifier */
/* dword 3 */
uint16_t cid; /* command identifier */
struct nvme_status status;
} __packed;
struct nvme_pt_command {
/*
* cmd is used to specify a passthrough command to a controller or
* namespace.
*
* The following fields from cmd may be specified by the caller:
* * opc (opcode)
* * nsid (namespace id) - for admin commands only
* * cdw10-cdw15
*
* Remaining fields must be set to 0 by the caller.
*/
struct nvme_command cmd;
/*
* cpl returns completion status for the passthrough command
* specified by cmd.
*
* The following fields will be filled out by the driver, for
* consumption by the caller:
* * cdw0
* * status (except for phase)
*
* Remaining fields will be set to 0 by the driver.
*/
struct nvme_completion cpl;
/* buf is the data buffer associated with this passthrough command. */
void * buf;
/*
* len is the length of the data buffer associated with this
* passthrough command.
*/
uint32_t len;
/*
* is_read = 1 if the passthrough command will read data into the
* supplied buffer from the controller.
*
* is_read = 0 if the passthrough command will write data from the
* supplied buffer to the controller.
*/
uint32_t is_read;
/*
* driver_lock is used by the driver only. It must be set to 0
* by the caller.
*/
struct mtx * driver_lock;
};
#else
#include <dev/nvme/nvme.h>
#endif
#define nvme_completion_is_error(cpl) \
((cpl)->status.sc != 0 || (cpl)->status.sct != 0)
#define NVME_CTRLR_PREFIX "/dev/nvme"
#define NVME_NS_PREFIX "ns"

View File

@ -1,21 +0,0 @@
#ifndef SG_CMDS_H
#define SG_CMDS_H
/********************************************************************
* This header did contain wrapper declarations for many SCSI commands
* up until sg3_utils version 1.22 . In that version, the command
* wrappers were broken into two groups, the 'basic' ones found in the
* "sg_cmds_basic.h" header and the 'extra' ones found in the
* "sg_cmds_extra.h" header. This header now simply includes those two
* headers.
* In sg3_utils version 1.26 the sg_cmds_mmc.h header was added and
* contains some MMC specific commands.
* The corresponding function definitions are found in the sg_cmds_basic.c,
* sg_cmds_extra.c and sg_cmds_mmc.c files.
********************************************************************/
#include "sg_cmds_basic.h"
#include "sg_cmds_extra.h"
#include "sg_cmds_mmc.h"
#endif

View File

@ -1,310 +0,0 @@
#ifndef SG_CMDS_BASIC_H
#define SG_CMDS_BASIC_H
/*
* Copyright (c) 2004-2017 Douglas Gilbert.
* All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the BSD_LICENSE file.
*/
/*
* Error, warning and verbose output is sent to the file pointed to by
* sg_warnings_strm which is declared in sg_lib.h and can be set with
* the sg_set_warnings_strm() function. If not given sg_warnings_strm
* defaults to stderr.
* If 'noisy' is false and 'verbose' is zero then following functions should
* not output anything to sg_warnings_strm. If 'noisy' is true and
* 'verbose' is zero then Unit Attention, Recovered, Medium and Hardware
* errors (sense keys) send output to sg_warnings_strm. Increasing values
* of 'verbose' send increasing amounts of (debug) output to
* sg_warnings_strm.
*/
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Invokes a SCSI INQUIRY command and yields the response
* Returns 0 when successful, SG_LIB_CAT_INVALID_OP -> not supported,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
* SG_LIB_CAT_ABORTED_COMMAND, -1 -> other errors */
int sg_ll_inquiry(int sg_fd, bool cmddt, bool evpd, int pg_op, void * resp,
int mx_resp_len, bool noisy, int verbose);
/* Invokes a SCSI INQUIRY command and yields the response. Returns 0 when
* successful, various SG_LIB_CAT_* positive values or -1 -> other errors.
* The CMDDT field is obsolete in the INQUIRY cdb (since spc3r16 in 2003) so
* an argument to set it has been removed (use the REPORT SUPPORTED OPERATION
* CODES command instead). Adds the ability to set the command abort timeout
* and the ability to report the residual count. If timeout_secs is zero
* or less the default command abort timeout (60 seconds) is used.
* If residp is non-NULL then the residual value is written where residp
* points. A residual value of 0 implies mx_resp_len bytes have be written
* where resp points. If the residual value equals mx_resp_len then no
* bytes have been written. */
int
sg_ll_inquiry_v2(int sg_fd, bool evpd, int pg_op, void * resp,
int mx_resp_len, int timeout_secs, int * residp,
bool noisy, int verbose);
/* Invokes a SCSI LOG SELECT command. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> Log Select not supported,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
* SG_LIB_CAT_ABORTED_COMMAND, * SG_LIB_CAT_NOT_READY -> device not ready,
* -1 -> other failure */
int sg_ll_log_select(int sg_fd, bool pcr, bool sp, int pc, int pg_code,
int subpg_code, unsigned char * paramp, int param_len,
bool noisy, int verbose);
/* Invokes a SCSI LOG SENSE command. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> Log Sense not supported,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
* -1 -> other failure */
int sg_ll_log_sense(int sg_fd, bool ppc, bool sp, int pc, int pg_code,
int subpg_code, int paramp, unsigned char * resp,
int mx_resp_len, bool noisy, int verbose);
/* Same as sg_ll_log_sense() apart from timeout_secs and residp. See
* sg_ll_inquiry_v2() for their description */
int sg_ll_log_sense_v2(int sg_fd, bool ppc, bool sp, int pc, int pg_code,
int subpg_code, int paramp, unsigned char * resp,
int mx_resp_len, int timeout_secs, int * residp,
bool noisy, int verbose);
/* Invokes a SCSI MODE SELECT (6) command. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ ->
* bad field in cdb, * SG_LIB_CAT_NOT_READY -> device not ready,
* SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND,
* -1 -> other failure */
int sg_ll_mode_select6(int sg_fd, bool pf, bool sp, void * paramp,
int param_len, bool noisy, int verbose);
/* Invokes a SCSI MODE SELECT (10) command. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ ->
* bad field in cdb, * SG_LIB_CAT_NOT_READY -> device not ready,
* SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND,
* -1 -> other failure */
int sg_ll_mode_select10(int sg_fd, bool pf, bool sp, void * paramp,
int param_len, bool noisy, int verbose);
/* Invokes a SCSI MODE SENSE (6) command. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ ->
* bad field in cdb, * SG_LIB_CAT_NOT_READY -> device not ready,
* SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND,
* -1 -> other failure */
int sg_ll_mode_sense6(int sg_fd, bool dbd, int pc, int pg_code,
int sub_pg_code, void * resp, int mx_resp_len,
bool noisy, int verbose);
/* Invokes a SCSI MODE SENSE (10) command. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ ->
* bad field in cdb, * SG_LIB_CAT_NOT_READY -> device not ready,
* SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND,
* -1 -> other failure */
int sg_ll_mode_sense10(int sg_fd, bool llbaa, bool dbd, int pc, int pg_code,
int sub_pg_code, void * resp, int mx_resp_len,
bool noisy, int verbose);
/* Same as sg_ll_mode_sense10() apart from timeout_secs and residp. See
* sg_ll_inquiry_v2() for their description */
int sg_ll_mode_sense10_v2(int sg_fd, bool llbaa, bool dbd, int pc,
int pg_code, int sub_pg_code, void * resp,
int mx_resp_len, int timeout_secs, int * residp,
bool noisy, int verbose);
/* Invokes a SCSI PREVENT ALLOW MEDIUM REMOVAL command (SPC-3)
* prevent==0 allows removal, prevent==1 prevents removal ...
* Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> command not supported
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
* -1 -> other failure */
int sg_ll_prevent_allow(int sg_fd, int prevent, bool noisy, int verbose);
/* Invokes a SCSI READ CAPACITY (10) command. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_UNIT_ATTENTION
* -> perhaps media changed, SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
* -1 -> other failure */
int sg_ll_readcap_10(int sg_fd, bool pmi, unsigned int lba, void * resp,
int mx_resp_len, bool noisy, int verbose);
/* Invokes a SCSI READ CAPACITY (16) command. Returns 0 -> success,
* SG_LIB_CAT_UNIT_ATTENTION -> media changed??, SG_LIB_CAT_INVALID_OP
* -> cdb not supported, SG_LIB_CAT_IlLEGAL_REQ -> bad field in cdb
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
* -1 -> other failure */
int sg_ll_readcap_16(int sg_fd, bool pmi, uint64_t llba, void * resp,
int mx_resp_len, bool noisy, int verbose);
/* Invokes a SCSI REPORT LUNS command. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> Report Luns not supported,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
* SG_LIB_NOT_READY (shouldn't happen), -1 -> other failure */
int sg_ll_report_luns(int sg_fd, int select_report, void * resp,
int mx_resp_len, bool noisy, int verbose);
/* Invokes a SCSI REQUEST SENSE command. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> Request Sense not supported??,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
* -1 -> other failure */
int sg_ll_request_sense(int sg_fd, bool desc, void * resp, int mx_resp_len,
bool noisy, int verbose);
/* Invokes a SCSI START STOP UNIT command (SBC + MMC).
* Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> Start stop unit not supported,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
* -1 -> other failure
* SBC-3 and MMC partially overlap on the power_condition_modifier(sbc) and
* format_layer_number(mmc) fields. They also overlap on the noflush(sbc)
* and fl(mmc) one bit field. This is the cause of the awkardly named
* pc_mod__fl_num and noflush__fl arguments to this function. */
int sg_ll_start_stop_unit(int sg_fd, bool immed, int pc_mod__fl_num,
int power_cond, bool noflush__fl, bool loej,
bool start, bool noisy, int verbose);
/* Invokes a SCSI SYNCHRONIZE CACHE (10) command. Return of 0 -> success,
* SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND,
* SG_LIB_CAT_INVALID_OP -> cdb not supported,
* SG_LIB_CAT_IlLEGAL_REQ -> bad field in cdb
* SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
int sg_ll_sync_cache_10(int sg_fd, bool sync_nv, bool immed, int group,
unsigned int lba, unsigned int count, bool noisy,
int verbose);
/* Invokes a SCSI TEST UNIT READY command.
* 'pack_id' is just for diagnostics, safe to set to 0.
* Return of 0 -> success, SG_LIB_CAT_UNIT_ATTENTION,
* SG_LIB_CAT_NOT_READY -> device not ready,
* SG_LIB_CAT_ABORTED_COMMAND, -1 -> other failure */
int sg_ll_test_unit_ready(int sg_fd, int pack_id, bool noisy, int verbose);
/* Invokes a SCSI TEST UNIT READY command.
* 'pack_id' is just for diagnostics, safe to set to 0.
* Looks for progress indicator if 'progress' non-NULL;
* if found writes value [0..65535] else write -1.
* Return of 0 -> success, SG_LIB_CAT_UNIT_ATTENTION,
* SG_LIB_CAT_ABORTED_COMMAND, SG_LIB_CAT_NOT_READY ->
* device not ready, -1 -> other failure */
int sg_ll_test_unit_ready_progress(int sg_fd, int pack_id, int * progress,
bool noisy, int verbose);
struct sg_simple_inquiry_resp {
unsigned char peripheral_qualifier;
unsigned char peripheral_type;
unsigned char byte_1; /* was 'rmb' prior to version 1.39 */
/* now rmb == !!(0x80 & byte_1) */
unsigned char version; /* as per recent drafts: whole of byte 2 */
unsigned char byte_3;
unsigned char byte_5;
unsigned char byte_6;
unsigned char byte_7;
char vendor[9]; /* T10 field is 8 bytes, NUL char appended */
char product[17];
char revision[5];
};
/* Yields most of first 36 bytes of a standard INQUIRY (evpd==0) response.
* Returns 0 when successful, SG_LIB_CAT_INVALID_OP -> not supported,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
* -1 -> other errors */
int sg_simple_inquiry(int sg_fd, struct sg_simple_inquiry_resp * inq_data,
bool noisy, int verbose);
/* MODE SENSE commands yield a response that has header then zero or more
* block descriptors followed by mode pages. In most cases users are
* interested in the first mode page. This function returns the (byte)
* offset of the start of the first mode page. Set mode_sense_6 to true for
* MODE SENSE (6) and false for MODE SENSE (10). Returns >= 0 is successful
* or -1 if failure. If there is a failure a message is written to err_buff
* if it is non-NULL and err_buff_len > 0. */
int sg_mode_page_offset(const unsigned char * resp, int resp_len,
bool mode_sense_6, char * err_buff, int err_buff_len);
/* MODE SENSE commands yield a response that has header then zero or more
* block descriptors followed by mode pages. This functions returns the
* length (in bytes) of those three components. Note that the return value
* can exceed resp_len in which case the MODE SENSE command should be
* re-issued with a larger response buffer. If bd_lenp is non-NULL and if
* successful the block descriptor length (in bytes) is written to *bd_lenp.
* Set mode_sense_6 to true for MODE SENSE (6) and false for MODE SENSE (10)
* responses. Returns -1 if there is an error (e.g. response too short). */
int sg_msense_calc_length(const unsigned char * resp, int resp_len,
bool mode_sense_6, int * bd_lenp);
/* Fetches current, changeable, default and/or saveable modes pages as
* indicated by pcontrol_arr for given pg_code and sub_pg_code. If
* mode6==0 then use MODE SENSE (10) else use MODE SENSE (6). If
* flexible set and mode data length seems wrong then try and
* fix (compensating hack for bad device or driver). pcontrol_arr
* should have 4 elements for output of current, changeable, default
* and saved values respectively. Each element should be NULL or
* at least mx_mpage_len bytes long.
* Return of 0 -> overall success, SG_LIB_CAT_INVALID_OP -> invalid opcode,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
* SG_LIB_CAT_NOT_READY -> device not ready,
* SG_LIB_CAT_MALFORMED -> bad response, -1 -> other failure.
* If success_mask pointer is not NULL then first zeros it. Then set bits
* 0, 1, 2 and/or 3 if the current, changeable, default and saved values
* respectively have been fetched. If error on current page
* then stops and returns that error; otherwise continues if an error is
* detected but returns the first error encountered. */
int sg_get_mode_page_controls(int sg_fd, bool mode6, int pg_code,
int sub_pg_code, bool dbd, bool flexible,
int mx_mpage_len, int * success_mask,
void * pcontrol_arr[], int * reported_lenp,
int verbose);
/* Returns file descriptor >= 0 if successful. If error in Unix returns
negated errno. Implementation calls scsi_pt_open_device(). */
int sg_cmds_open_device(const char * device_name, bool read_only, int verbose);
/* Returns file descriptor >= 0 if successful. If error in Unix returns
negated errno. Implementation calls scsi_pt_open_flags(). */
int sg_cmds_open_flags(const char * device_name, int flags, int verbose);
/* Returns 0 if successful. If error in Unix returns negated errno.
Implementation calls scsi_pt_close_device(). */
int sg_cmds_close_device(int device_fd);
const char * sg_cmds_version();
#define SG_NO_DATA_IN 0
struct sg_pt_base;
/* This is a helper function used by sg_cmds_* implementations after the
* call to the pass-through. pt_res is returned from do_scsi_pt(). If valid
* sense data is found it is decoded and output to sg_warnings_strm (def:
* stderr); depending on the 'noisy' and 'verbose' settings. Returns -2 for
* sense data (may not be fatal), -1 for failed, 0, or a positive number. If
* 'mx_di_len > 0' then asks pass-through for resid and returns
* (mx_di_len - resid); otherwise returns 0. So for data-in it should return
* the actual number of bytes received. For data-out (to device) or no data
* call with 'mx_di_len' set to 0 or less. If -2 returned then sense category
* output via 'o_sense_cat' pointer (if not NULL). Note that several sense
* categories also have data in bytes received; -2 is still returned. */
int sg_cmds_process_resp(struct sg_pt_base * ptvp, const char * leadin,
int pt_res, int mx_di_len,
const unsigned char * sense_b, bool noisy,
int verbose, int * o_sense_cat);
/* NVMe devices use a different command set. This function will return true
* if the device associated with 'pvtp' is a NVME device, else it will
* return false (e.g. for SCSI devices). */
bool sg_cmds_is_nvme(const struct sg_pt_base * ptvp);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,369 +0,0 @@
#ifndef SG_CMDS_EXTRA_H
#define SG_CMDS_EXTRA_H
/*
* Copyright (c) 2004-2018 Douglas Gilbert.
* All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the BSD_LICENSE file.
*/
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Note: all functions that have an 'int timeout_secs' argument will use
* that value if it is > 0. Otherwise they will set an internal default
* which is currently 60 seconds. This timeout is typically applied in the
* SCSI stack above the initiator. If it goes off then the SCSI command is
* aborted and there can be other unwelcome side effects. Note that some
* commands (e.g. FORMAT UNIT and the Third Party copy commands) can take
* a lot longer than the default timeout. */
/* Invokes a ATA PASS-THROUGH (12, 16 or 32) SCSI command (SAT). This is
* selected by the cdb_len argument that can take values of 12, 16 or 32
* only (else -1 is returned). The byte at offset 0 (and bytes 0 to 9
* inclusive for ATA PT(32)) pointed to be cdbp are ignored and apart from
* the control byte, the rest is copied into an internal cdb which is then
* sent to the device. The control byte is byte 11 for ATA PT(12), byte 15
* for ATA PT(16) and byte 1 for ATA PT(32). If timeout_secs <= 0 then the
* timeout is set to 60 seconds. For data in or out transfers set dinp or
* doutp, and dlen to the number of bytes to transfer. If dlen is zero then
* no data transfer is assumed. If sense buffer obtained then it is written
* to sensep, else sensep[0] is set to 0x0. If ATA return descriptor is
* obtained then written to ata_return_dp, else ata_return_dp[0] is set to
* 0x0. Either sensep or ata_return_dp (or both) may be NULL pointers.
* Returns SCSI status value (>= 0) or -1 if other error. Users are
* expected to check the sense buffer themselves. If available the data in
* resid is written to residp. Note in SAT-2 and later, fixed format sense
* data may be placed in *sensep in which case sensep[0]==0x70, prior to
* SAT-2 descriptor sense format was required (i.e. sensep[0]==0x72).
*/
int sg_ll_ata_pt(int sg_fd, const unsigned char * cdbp, int cdb_len,
int timeout_secs, void * dinp, void * doutp, int dlen,
unsigned char * sensep, int max_sense_len,
unsigned char * ata_return_dp, int max_ata_return_len,
int * residp, int verbose);
/* Invokes a FORMAT UNIT (SBC-3) command. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> Format unit not supported,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
* -1 -> other failure. Note that sg_ll_format_unit2() and
* sg_ll_format_unit_v2() are the same, both add the ffmt argument. */
int sg_ll_format_unit(int sg_fd, int fmtpinfo, bool longlist, bool fmtdata,
bool cmplist, int dlist_format, int timeout_secs,
void * paramp, int param_len, bool noisy, int verbose);
int sg_ll_format_unit2(int sg_fd, int fmtpinfo, bool longlist, bool fmtdata,
bool cmplist, int dlist_format, int ffmt,
int timeout_secs, void * paramp, int param_len,
bool noisy, int verbose);
int sg_ll_format_unit_v2(int sg_fd, int fmtpinfo, bool longlist, bool fmtdata,
bool cmplist, int dlist_format, int ffmt,
int timeout_secs, void * paramp, int param_len,
bool noisy, int verbose);
/* Invokes a SCSI GET LBA STATUS(16) or GET LBA STATUS(32) command (SBC).
* Returns 0 -> success,
* SG_LIB_CAT_INVALID_OP -> GET LBA STATUS(16 or 32) not supported,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
* SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure.
* sg_ll_get_lba_status() calls the 16 byte variant with rt=0 . */
int sg_ll_get_lba_status(int sg_fd, uint64_t start_llba, void * resp,
int alloc_len, bool noisy, int verbose);
int sg_ll_get_lba_status16(int sg_fd, uint64_t start_llba, uint8_t rt,
void * resp, int alloc_len, bool noisy,
int verbose);
int sg_ll_get_lba_status32(int sg_fd, uint64_t start_llba, uint32_t scan_len,
uint32_t element_id, uint8_t rt,
void * resp, int alloc_len, bool noisy,
int verbose);
/* Invokes a SCSI PERSISTENT RESERVE IN command (SPC). Returns 0
* when successful, SG_LIB_CAT_INVALID_OP if command not supported,
* SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported,
* SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, else -1 */
int sg_ll_persistent_reserve_in(int sg_fd, int rq_servact, void * resp,
int mx_resp_len, bool noisy, int verbose);
/* Invokes a SCSI PERSISTENT RESERVE OUT command (SPC). Returns 0
* when successful, SG_LIB_CAT_INVALID_OP if command not supported,
* SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported,
* SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, else -1 */
int sg_ll_persistent_reserve_out(int sg_fd, int rq_servact, int rq_scope,
unsigned int rq_type, void * paramp,
int param_len, bool noisy, int verbose);
/* Invokes a SCSI READ BLOCK LIMITS command. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> READ BLOCK LIMITS not supported,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
* SG_LIB_NOT_READY (shouldn't happen), -1 -> other failure */
int sg_ll_read_block_limits(int sg_fd, void * resp, int mx_resp_len,
bool noisy, int verbose);
/* Invokes a SCSI READ BUFFER command (SPC). Return of 0 ->
* success, SG_LIB_CAT_INVALID_OP -> invalid opcode,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
* -1 -> other failure */
int sg_ll_read_buffer(int sg_fd, int mode, int buffer_id, int buffer_offset,
void * resp, int mx_resp_len, bool noisy, int verbose);
/* Invokes a SCSI READ DEFECT DATA (10) command (SBC). Return of 0 ->
* success, SG_LIB_CAT_INVALID_OP -> invalid opcode,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
* -1 -> other failure */
int sg_ll_read_defect10(int sg_fd, bool req_plist, bool req_glist,
int dl_format, void * resp, int mx_resp_len,
bool noisy, int verbose);
/* Invokes a SCSI READ LONG (10) command (SBC). Note that 'xfer_len'
* is in bytes. Returns 0 -> success,
* SG_LIB_CAT_INVALID_OP -> READ LONG(10) not supported,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
* SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO -> bad field in cdb, with info
* field written to 'offsetp', SG_LIB_CAT_UNIT_ATTENTION,
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
* -1 -> other failure */
int sg_ll_read_long10(int sg_fd, bool pblock, bool correct, unsigned int lba,
void * resp, int xfer_len, int * offsetp, bool noisy,
int verbose);
/* Invokes a SCSI READ LONG (16) command (SBC). Note that 'xfer_len'
* is in bytes. Returns 0 -> success,
* SG_LIB_CAT_INVALID_OP -> READ LONG(16) not supported,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
* SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO -> bad field in cdb, with info
* field written to 'offsetp', SG_LIB_CAT_UNIT_ATTENTION,
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
* -1 -> other failure */
int sg_ll_read_long16(int sg_fd, bool pblock, bool correct, uint64_t llba,
void * resp, int xfer_len, int * offsetp, bool noisy,
int verbose);
/* Invokes a SCSI READ MEDIA SERIAL NUMBER command. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> Read media serial number not supported,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
* -1 -> other failure */
int sg_ll_read_media_serial_num(int sg_fd, void * resp, int mx_resp_len,
bool noisy, int verbose);
/* Invokes a SCSI REASSIGN BLOCKS command. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_UNIT_ATTENTION,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
* SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
int sg_ll_reassign_blocks(int sg_fd, bool longlba, bool longlist,
void * paramp, int param_len, bool noisy,
int verbose);
/* Invokes a SCSI RECEIVE DIAGNOSTIC RESULTS command. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> Receive diagnostic results not supported,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
* -1 -> other failure */
int sg_ll_receive_diag(int sg_fd, bool pcv, int pg_code, void * resp,
int mx_resp_len, bool noisy, int verbose);
/* Same as sg_ll_receive_diag() but with added timeout_secs and residp
* arguments. Adds the ability to set the command abort timeout
* and the ability to report the residual count. If timeout_secs is zero
* or less the default command abort timeout (60 seconds) is used.
* If residp is non-NULL then the residual value is written where residp
* points. A residual value of 0 implies mx_resp_len bytes have be written
* where resp points. If the residual value equals mx_resp_len then no
* bytes have been written. */
int sg_ll_receive_diag_v2(int sg_fd, bool pcv, int pg_code, void * resp,
int mx_resp_len, int timeout_secs, int * residp,
bool noisy, int verbose);
/* Invokes a SCSI REPORT IDENTIFYING INFORMATION command. This command was
* called REPORT DEVICE IDENTIFIER prior to spc4r07. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> Report identifying information not supported,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
* -1 -> other failure */
int sg_ll_report_id_info(int sg_fd, int itype, void * resp, int max_resp_len,
bool noisy, int verbose);
/* Invokes a SCSI REPORT TARGET PORT GROUPS command. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> Report Target Port Groups not supported,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
* SG_LIB_CAT_UNIT_ATTENTION, -1 -> other failure */
int sg_ll_report_tgt_prt_grp(int sg_fd, void * resp, int mx_resp_len,
bool noisy, int verbose);
int sg_ll_report_tgt_prt_grp2(int sg_fd, void * resp, int mx_resp_len,
bool extended, bool noisy, int verbose);
/* Invokes a SCSI SET TARGET PORT GROUPS command. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> Report Target Port Groups not supported,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
* SG_LIB_CAT_UNIT_ATTENTION, -1 -> other failure */
int sg_ll_set_tgt_prt_grp(int sg_fd, void * paramp, int param_len, bool noisy,
int verbose);
/* Invokes a SCSI REPORT REFERRALS command. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> Report Referrals not supported,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
* SG_LIB_CAT_UNIT_ATTENTION, -1 -> other failure */
int sg_ll_report_referrals(int sg_fd, uint64_t start_llba, bool one_seg,
void * resp, int mx_resp_len, bool noisy,
int verbose);
/* Invokes a SCSI SEND DIAGNOSTIC command. Foreground, extended self tests can
* take a long time, if so set long_duration flag in which case the timeout
* is set to 7200 seconds; if the value of long_duration is > 7200 then that
* value is taken as the timeout value in seconds. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> Send diagnostic not supported,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
* -1 -> other failure */
int sg_ll_send_diag(int sg_fd, int st_code, bool pf_bit, bool st_bit,
bool devofl_bit, bool unitofl_bit, int long_duration,
void * paramp, int param_len, bool noisy, int verbose);
/* Invokes a SCSI SET IDENTIFYING INFORMATION command. This command was
* called SET DEVICE IDENTIFIER prior to spc4r07. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> Set identifying information not supported,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
* -1 -> other failure */
int sg_ll_set_id_info(int sg_fd, int itype, void * paramp, int param_len,
bool noisy, int verbose);
/* Invokes a SCSI UNMAP (SBC-3) command. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> command not supported,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
* SG_LIB_CAT_UNIT_ATTENTION, -1 -> other failure */
int sg_ll_unmap(int sg_fd, int group_num, int timeout_secs, void * paramp,
int param_len, bool noisy, int verbose);
/* Invokes a SCSI UNMAP (SBC-3) command. Version 2 adds anchor field
* (sbc3r22). Otherwise same as sg_ll_unmap() . */
int sg_ll_unmap_v2(int sg_fd, bool anchor, int group_num, int timeout_secs,
void * paramp, int param_len, bool noisy, int verbose);
/* Invokes a SCSI VERIFY (10) command (SBC and MMC).
* Note that 'veri_len' is in blocks while 'data_out_len' is in bytes.
* Returns of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> Verify(10) not supported,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
* SG_LIB_CAT_MEDIUM_HARD -> medium or hardware error, no valid info,
* SG_LIB_CAT_MEDIUM_HARD_WITH_INFO -> as previous, with valid info,
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
* SG_LIB_CAT_MISCOMPARE, -1 -> other failure */
int sg_ll_verify10(int sg_fd, int vrprotect, bool dpo, int bytechk,
unsigned int lba, int veri_len, void * data_out,
int data_out_len, unsigned int * infop, bool noisy,
int verbose);
/* Invokes a SCSI VERIFY (16) command (SBC).
* Note that 'veri_len' is in blocks while 'data_out_len' is in bytes.
* Returns of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> Verify(16) not supported,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
* SG_LIB_CAT_MEDIUM_HARD -> medium or hardware error, no valid info,
* SG_LIB_CAT_MEDIUM_HARD_WITH_INFO -> as previous, with valid info,
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
* SG_LIB_CAT_MISCOMPARE, -1 -> other failure */
int sg_ll_verify16(int sg_fd, int vrprotect, bool dpo, int bytechk,
uint64_t llba, int veri_len, int group_num,
void * data_out, int data_out_len, uint64_t * infop,
bool noisy, int verbose);
/* Invokes a SCSI WRITE BUFFER command (SPC). Return of 0 ->
* success, SG_LIB_CAT_INVALID_OP -> invalid opcode,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
* -1 -> other failure */
int sg_ll_write_buffer(int sg_fd, int mode, int buffer_id, int buffer_offset,
void * paramp, int param_len, bool noisy, int verbose);
/* Invokes a SCSI WRITE BUFFER command (SPC). Return of 0 ->
* success, SG_LIB_CAT_INVALID_OP -> invalid opcode,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
* -1 -> other failure. Adds mode specific field (spc4r32) and timeout
* to command abort to override default of 60 seconds. If timeout_secs is
* 0 or less then the default timeout is used instead. */
int
sg_ll_write_buffer_v2(int sg_fd, int mode, int m_specific, int buffer_id,
uint32_t buffer_offset, void * paramp,
uint32_t param_len, int timeout_secs, bool noisy,
int verbose);
/* Invokes a SCSI WRITE LONG (10) command (SBC). Note that 'xfer_len'
* is in bytes. Returns 0 -> success,
* SG_LIB_CAT_INVALID_OP -> WRITE LONG(10) not supported,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
* SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO -> bad field in cdb, with info
* field written to 'offsetp', SG_LIB_CAT_UNIT_ATTENTION,
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
* -1 -> other failure */
int sg_ll_write_long10(int sg_fd, bool cor_dis, bool wr_uncor, bool pblock,
unsigned int lba, void * data_out, int xfer_len,
int * offsetp, bool noisy, int verbose);
/* Invokes a SCSI WRITE LONG (16) command (SBC). Note that 'xfer_len'
* is in bytes. Returns 0 -> success,
* SG_LIB_CAT_INVALID_OP -> WRITE LONG(16) not supported,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
* SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO -> bad field in cdb, with info
* field written to 'offsetp', SG_LIB_CAT_UNIT_ATTENTION,
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
* -1 -> other failure */
int sg_ll_write_long16(int sg_fd, bool cor_dis, bool wr_uncor, bool pblock,
uint64_t llba, void * data_out, int xfer_len,
int * offsetp, bool noisy, int verbose);
/* Invokes a SPC-3 SCSI RECEIVE COPY RESULTS command. In SPC-4 this function
* supports all service action variants of the THIRD-PARTY COPY IN opcode.
* SG_LIB_CAT_INVALID_OP -> Receive copy results not supported,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
* -1 -> other failure */
int sg_ll_receive_copy_results(int sg_fd, int sa, int list_id, void * resp,
int mx_resp_len, bool noisy, int verbose);
/* Invokes a SCSI EXTENDED COPY(LID1) command. For EXTENDED COPY(LID4)
* including POPULATE TOKEN and WRITE USING TOKEN use
* sg_ll_3party_copy_out(). Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> Extended copy not supported,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
* -1 -> other failure */
int sg_ll_extended_copy(int sg_fd, void * paramp, int param_len, bool noisy,
int verbose);
/* Handles various service actions associated with opcode 0x83 which is
* called THIRD PARTY COPY OUT. These include the EXTENDED COPY(LID4),
* POPULATE TOKEN and WRITE USING TOKEN commands. Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> opcode 0x83 not supported,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
* -1 -> other failure */
int sg_ll_3party_copy_out(int sg_fd, int sa, unsigned int list_id,
int group_num, int timeout_secs, void * paramp,
int param_len, bool noisy, int verbose);
/* Invokes a SCSI PRE-FETCH(10), PRE-FETCH(16) or SEEK(10) command (SBC).
* Returns 0 -> success, 25 (SG_LIB_CAT_CONDITION_MET), various SG_LIB_CAT_*
* positive values or -1 -> other errors. Note that CONDITION MET status
* is returned when immed=true and num_blocks can fit in device's cache,
* somewaht strangely, GOOD status (return 0) is returned if num_blocks
* cannot fit in device's cache. If do_seek10==true then does a SEEK(10)
* command with given lba, if that LBA is < 2**32 . Unclear what SEEK(10)
* does, assume it is like PRE-FETCH. If timeout_secs is 0 (or less) then
* use DEF_PT_TIMEOUT (60 seconds) as command timeout. */
int sg_ll_pre_fetch_x(int sg_fd, bool do_seek10, bool cdb16, bool immed,
uint64_t lba, uint32_t num_blocks, int group_num,
int timeout_secs, bool noisy, int verbose);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,52 +0,0 @@
#ifndef SG_CMDS_MMC_H
#define SG_CMDS_MMC_H
/*
* Copyright (c) 2008-2017 Douglas Gilbert.
* All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the BSD_LICENSE file.
*/
#ifdef __cplusplus
extern "C" {
#endif
/* Invokes a SCSI GET CONFIGURATION command (MMC-3...6).
* Returns 0 when successful, SG_LIB_CAT_INVALID_OP if command not
* supported, SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported,
* SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, else -1 */
int sg_ll_get_config(int sg_fd, int rt, int starting, void * resp,
int mx_resp_len, bool noisy, int verbose);
/* Invokes a SCSI GET PERFORMANCE command (MMC-3...6).
* Returns 0 when successful, SG_LIB_CAT_INVALID_OP if command not
* supported, SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported,
* SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, else -1 */
int sg_ll_get_performance(int sg_fd, int data_type, unsigned int starting_lba,
int max_num_desc, int type, void * resp,
int mx_resp_len, bool noisy, int verbose);
/* Invokes a SCSI SET CD SPEED command (MMC).
* Return of 0 -> success, SG_LIB_CAT_INVALID_OP -> command not supported,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
* -1 -> other failure */
int sg_ll_set_cd_speed(int sg_fd, int rot_control, int drv_read_speed,
int drv_write_speed, bool noisy, int verbose);
/* Invokes a SCSI SET STREAMING command (MMC). Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> Set Streaming not supported,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
* SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_NOT_READY -> device not ready,
* -1 -> other failure */
int sg_ll_set_streaming(int sg_fd, int type, void * paramp, int param_len,
bool noisy, int verbose);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,185 +0,0 @@
#ifndef SG_IO_LINUX_H
#define SG_IO_LINUX_H
/*
* Copyright (c) 2004-2017 Douglas Gilbert.
* All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the BSD_LICENSE file.
*/
/*
* Version 1.05 [20171009]
*/
/*
* This header file contains linux specific information related to the SCSI
* command pass through in the SCSI generic (sg) driver and the linux
* block layer.
*/
#include "sg_lib.h"
#include "sg_linux_inc.h"
#ifdef __cplusplus
extern "C" {
#endif
/* The following are 'host_status' codes */
#ifndef DID_OK
#define DID_OK 0x00
#endif
#ifndef DID_NO_CONNECT
#define DID_NO_CONNECT 0x01 /* Unable to connect before timeout */
#define DID_BUS_BUSY 0x02 /* Bus remain busy until timeout */
#define DID_TIME_OUT 0x03 /* Timed out for some other reason */
#define DID_BAD_TARGET 0x04 /* Bad target (id?) */
#define DID_ABORT 0x05 /* Told to abort for some other reason */
#define DID_PARITY 0x06 /* Parity error (on SCSI bus) */
#define DID_ERROR 0x07 /* Internal error */
#define DID_RESET 0x08 /* Reset by somebody */
#define DID_BAD_INTR 0x09 /* Received an unexpected interrupt */
#define DID_PASSTHROUGH 0x0a /* Force command past mid-level */
#define DID_SOFT_ERROR 0x0b /* The low-level driver wants a retry */
#endif
#ifndef DID_IMM_RETRY
#define DID_IMM_RETRY 0x0c /* Retry without decrementing retry count */
#endif
#ifndef DID_REQUEUE
#define DID_REQUEUE 0x0d /* Requeue command (no immediate retry) also
* without decrementing the retry count */
#endif
#ifndef DID_TRANSPORT_DISRUPTED
#define DID_TRANSPORT_DISRUPTED 0xe
#endif
#ifndef DID_TRANSPORT_FAILFAST
#define DID_TRANSPORT_FAILFAST 0xf
#endif
#ifndef DID_TARGET_FAILURE
#define DID_TARGET_FAILURE 0x10
#endif
#ifndef DID_NEXUS_FAILURE
#define DID_NEXUS_FAILURE 0x11
#endif
/* These defines are to isolate applications from kernel define changes */
#define SG_LIB_DID_OK DID_OK
#define SG_LIB_DID_NO_CONNECT DID_NO_CONNECT
#define SG_LIB_DID_BUS_BUSY DID_BUS_BUSY
#define SG_LIB_DID_TIME_OUT DID_TIME_OUT
#define SG_LIB_DID_BAD_TARGET DID_BAD_TARGET
#define SG_LIB_DID_ABORT DID_ABORT
#define SG_LIB_DID_PARITY DID_PARITY
#define SG_LIB_DID_ERROR DID_ERROR
#define SG_LIB_DID_RESET DID_RESET
#define SG_LIB_DID_BAD_INTR DID_BAD_INTR
#define SG_LIB_DID_PASSTHROUGH DID_PASSTHROUGH
#define SG_LIB_DID_SOFT_ERROR DID_SOFT_ERROR
#define SG_LIB_DID_IMM_RETRY DID_IMM_RETRY
#define SG_LIB_DID_REQUEUE DID_REQUEUE
#define SG_LIB_TRANSPORT_DISRUPTED DID_TRANSPORT_DISRUPTED
#define SG_LIB_DID_TRANSPORT_FAILFAST DID_TRANSPORT_FAILFAST
#define SG_LIB_DID_TARGET_FAILURE DID_TARGET_FAILURE
#define SG_LIB_DID_NEXUS_FAILURE DID_NEXUS_FAILURE
/* The following are 'driver_status' codes */
#ifndef DRIVER_OK
#define DRIVER_OK 0x00
#endif
#ifndef DRIVER_BUSY
#define DRIVER_BUSY 0x01
#define DRIVER_SOFT 0x02
#define DRIVER_MEDIA 0x03
#define DRIVER_ERROR 0x04
#define DRIVER_INVALID 0x05
#define DRIVER_TIMEOUT 0x06
#define DRIVER_HARD 0x07
#define DRIVER_SENSE 0x08 /* Sense_buffer has been set */
/* N.B. the SUGGEST_* codes are no longer used in Linux and are only kept
* to stop compilation breakages.
* Following "suggests" are "or-ed" with one of previous 8 entries */
#define SUGGEST_RETRY 0x10
#define SUGGEST_ABORT 0x20
#define SUGGEST_REMAP 0x30
#define SUGGEST_DIE 0x40
#define SUGGEST_SENSE 0x80
#define SUGGEST_IS_OK 0xff
#endif
#ifndef DRIVER_MASK
#define DRIVER_MASK 0x0f
#endif
#ifndef SUGGEST_MASK
#define SUGGEST_MASK 0xf0
#endif
/* These defines are to isolate applications from kernel define changes */
#define SG_LIB_DRIVER_OK DRIVER_OK
#define SG_LIB_DRIVER_BUSY DRIVER_BUSY
#define SG_LIB_DRIVER_SOFT DRIVER_SOFT
#define SG_LIB_DRIVER_MEDIA DRIVER_MEDIA
#define SG_LIB_DRIVER_ERROR DRIVER_ERROR
#define SG_LIB_DRIVER_INVALID DRIVER_INVALID
#define SG_LIB_DRIVER_TIMEOUT DRIVER_TIMEOUT
#define SG_LIB_DRIVER_HARD DRIVER_HARD
#define SG_LIB_DRIVER_SENSE DRIVER_SENSE
/* N.B. the SUGGEST_* codes are no longer used in Linux and are only kept
* to stop compilation breakages. */
#define SG_LIB_SUGGEST_RETRY SUGGEST_RETRY
#define SG_LIB_SUGGEST_ABORT SUGGEST_ABORT
#define SG_LIB_SUGGEST_REMAP SUGGEST_REMAP
#define SG_LIB_SUGGEST_DIE SUGGEST_DIE
#define SG_LIB_SUGGEST_SENSE SUGGEST_SENSE
#define SG_LIB_SUGGEST_IS_OK SUGGEST_IS_OK
#define SG_LIB_DRIVER_MASK DRIVER_MASK
#define SG_LIB_SUGGEST_MASK SUGGEST_MASK
void sg_print_masked_status(int masked_status);
void sg_print_host_status(int host_status);
void sg_print_driver_status(int driver_status);
/* sg_chk_n_print() returns 1 quietly if there are no errors/warnings
else it prints errors/warnings (prefixed by 'leadin') to
'sg_warnings_fd' and returns 0. raw_sinfo indicates whether the
raw sense buffer (in ASCII hex) should be printed. */
int sg_chk_n_print(const char * leadin, int masked_status, int host_status,
int driver_status, const unsigned char * sense_buffer,
int sb_len, bool raw_sinfo);
/* The following function declaration is for the sg version 3 driver. */
struct sg_io_hdr;
/* sg_chk_n_print3() returns 1 quietly if there are no errors/warnings;
else it prints errors/warnings (prefixed by 'leadin') to
'sg_warnings_fd' and returns 0. */
int sg_chk_n_print3(const char * leadin, struct sg_io_hdr * hp,
bool raw_sinfo);
/* Calls sg_scsi_normalize_sense() after obtaining the sense buffer and
its length from the struct sg_io_hdr pointer. If these cannot be
obtained, false is returned. */
bool sg_normalize_sense(const struct sg_io_hdr * hp,
struct sg_scsi_sense_hdr * sshp);
int sg_err_category(int masked_status, int host_status, int driver_status,
const unsigned char * sense_buffer, int sb_len);
int sg_err_category_new(int scsi_status, int host_status, int driver_status,
const unsigned char * sense_buffer, int sb_len);
/* The following function declaration is for the sg version 3 driver. */
int sg_err_category3(struct sg_io_hdr * hp);
/* Note about SCSI status codes found in older versions of Linux.
Linux has traditionally used a 1 bit right shifted and masked
version of SCSI standard status codes. Now CHECK_CONDITION
and friends (in <scsi/scsi.h>) are deprecated. */
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,602 +0,0 @@
#ifndef SG_LIB_H
#define SG_LIB_H
/*
* Copyright (c) 2004-2018 Douglas Gilbert.
* All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the BSD_LICENSE file.
*/
/*
*
* On 5th October 2004 a FreeBSD license was added to this file.
* The intention is to keep this file and the related sg_lib.c file
* as open source and encourage their unencumbered use.
*
* Current version number is in the sg_lib.c file and can be accessed
* with the sg_lib_version() function.
*/
/*
* This header file contains defines and function declarations that may
* be useful to applications that communicate with devices that use a
* SCSI command set. These command sets have names like SPC-4, SBC-3,
* SSC-3, SES-2 and draft standards defining them can be found at
* http://www.t10.org . Virtually all devices in the Linux SCSI subsystem
* utilize SCSI command sets. Many devices in other Linux device subsystems
* utilize SCSI command sets either natively or via emulation (e.g. a
* parallel ATA disk in a USB enclosure).
*/
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/* SCSI Peripheral Device Types (PDT) [5 bit field] */
#define PDT_DISK 0x0 /* direct access block device (disk) */
#define PDT_TAPE 0x1 /* sequential access device (magnetic tape) */
#define PDT_PRINTER 0x2 /* printer device (see SSC-1) */
#define PDT_PROCESSOR 0x3 /* processor device (e.g. SAFTE device) */
#define PDT_WO 0x4 /* write once device (some optical disks) */
#define PDT_MMC 0x5 /* CD/DVD/BD (multi-media) */
#define PDT_SCANNER 0x6 /* obsolete */
#define PDT_OPTICAL 0x7 /* optical memory device (some optical disks) */
#define PDT_MCHANGER 0x8 /* media changer device (e.g. tape robot) */
#define PDT_COMMS 0x9 /* communications device (obsolete) */
#define PDT_SAC 0xc /* storage array controller device */
#define PDT_SES 0xd /* SCSI Enclosure Services (SES) device */
#define PDT_RBC 0xe /* Reduced Block Commands (simplified PDT_DISK) */
#define PDT_OCRW 0xf /* optical card read/write device */
#define PDT_BCC 0x10 /* bridge controller commands */
#define PDT_OSD 0x11 /* Object Storage Device (OSD) */
#define PDT_ADC 0x12 /* Automation/drive commands (ADC) */
#define PDT_SMD 0x13 /* Security Manager Device (SMD) */
#define PDT_ZBC 0x14 /* Zoned Block Commands (ZBC) */
#define PDT_WLUN 0x1e /* Well known logical unit (WLUN) */
#define PDT_UNKNOWN 0x1f /* Unknown or no device type */
#ifndef SAM_STAT_GOOD
/* The SCSI status codes as found in SAM-4 at www.t10.org */
#define SAM_STAT_GOOD 0x0
#define SAM_STAT_CHECK_CONDITION 0x2
#define SAM_STAT_CONDITION_MET 0x4
#define SAM_STAT_BUSY 0x8
#define SAM_STAT_INTERMEDIATE 0x10 /* obsolete in SAM-4 */
#define SAM_STAT_INTERMEDIATE_CONDITION_MET 0x14 /* obsolete in SAM-4 */
#define SAM_STAT_RESERVATION_CONFLICT 0x18
#define SAM_STAT_COMMAND_TERMINATED 0x22 /* obsolete in SAM-3 */
#define SAM_STAT_TASK_SET_FULL 0x28
#define SAM_STAT_ACA_ACTIVE 0x30
#define SAM_STAT_TASK_ABORTED 0x40
#endif
/* The SCSI sense key codes as found in SPC-4 at www.t10.org */
#define SPC_SK_NO_SENSE 0x0
#define SPC_SK_RECOVERED_ERROR 0x1
#define SPC_SK_NOT_READY 0x2
#define SPC_SK_MEDIUM_ERROR 0x3
#define SPC_SK_HARDWARE_ERROR 0x4
#define SPC_SK_ILLEGAL_REQUEST 0x5
#define SPC_SK_UNIT_ATTENTION 0x6
#define SPC_SK_DATA_PROTECT 0x7
#define SPC_SK_BLANK_CHECK 0x8
#define SPC_SK_VENDOR_SPECIFIC 0x9
#define SPC_SK_COPY_ABORTED 0xa
#define SPC_SK_ABORTED_COMMAND 0xb
#define SPC_SK_RESERVED 0xc
#define SPC_SK_VOLUME_OVERFLOW 0xd
#define SPC_SK_MISCOMPARE 0xe
#define SPC_SK_COMPLETED 0xf
/* Transport protocol identifiers or just Protocol identifiers */
#define TPROTO_FCP 0
#define TPROTO_SPI 1
#define TPROTO_SSA 2
#define TPROTO_1394 3
#define TPROTO_SRP 4 /* SCSI over RDMA */
#define TPROTO_ISCSI 5
#define TPROTO_SAS 6
#define TPROTO_ADT 7
#define TPROTO_ATA 8
#define TPROTO_UAS 9 /* USB attached SCSI */
#define TPROTO_SOP 0xa /* SCSI over PCIe */
#define TPROTO_PCIE 0xb /* includes NVMe */
#define TPROTO_NONE 0xf
/* SCSI Feature Sets (sfs) */
#define SCSI_FS_SPC_DISCOVERY_2016 0x1
#define SCSI_FS_SBC_BASE_2010 0x102
#define SCSI_FS_SBC_BASE_2016 0x101
#define SCSI_FS_SBC_BASIC_PROV_2016 0x103
#define SCSI_FS_SBC_DRIVE_MAINT_2016 0x104
/* Often SCSI responses use the highest integer that can fit in a field
* to indicate "unbounded" or limit does not apply. Sometimes represented
* in output as "-1" for brevity */
#define SG_LIB_UNBOUNDED_16BIT 0xffff
#define SG_LIB_UNBOUNDED_32BIT 0xffffffffU
#define SG_LIB_UNBOUNDED_64BIT 0xffffffffffffffffULL
#if (__STDC_VERSION__ >= 199901L) /* C99 or later */
typedef uintptr_t sg_uintptr_t;
#else
typedef unsigned long sg_uintptr_t;
#endif
/* The format of the version string is like this: "2.26 20170906" */
const char * sg_lib_version();
/* Returns length of SCSI command given the opcode (first byte).
* Yields the wrong answer for variable length commands (opcode=0x7f)
* and potentially some vendor specific commands. */
int sg_get_command_size(unsigned char cdb_byte0);
/* Command name given pointer to the cdb. Certain command names
* depend on peripheral type (give 0 or -1 if unknown). Places command
* name into buff and will write no more than buff_len bytes. */
void sg_get_command_name(const unsigned char * cdbp, int peri_type,
int buff_len, char * buff);
/* Command name given only the first byte (byte 0) of a cdb and
* peripheral type (give 0 or -1 if unknown). */
void sg_get_opcode_name(unsigned char cdb_byte0, int peri_type, int buff_len,
char * buff);
/* Command name given opcode (byte 0), service action and peripheral type.
* If no service action give 0, if unknown peripheral type give 0 or -1 . */
void sg_get_opcode_sa_name(unsigned char cdb_byte0, int service_action,
int peri_type, int buff_len, char * buff);
/* Fetch scsi status string. */
void sg_get_scsi_status_str(int scsi_status, int buff_len, char * buff);
/* This is a slightly stretched SCSI sense "descriptor" format header.
* The addition is to allow the 0x70 and 0x71 response codes. The idea
* is to place the salient data of both "fixed" and "descriptor" sense
* format into one structure to ease application processing.
* The original sense buffer should be kept around for those cases
* in which more information is required (e.g. the LBA of a MEDIUM ERROR). */
struct sg_scsi_sense_hdr {
unsigned char response_code; /* permit: 0x0, 0x70, 0x71, 0x72, 0x73 */
unsigned char sense_key;
unsigned char asc;
unsigned char ascq;
unsigned char byte4;
unsigned char byte5;
unsigned char byte6;
unsigned char additional_length;
};
/* Maps the salient data from a sense buffer which is in either fixed or
* descriptor format into a structure mimicking a descriptor format
* header (i.e. the first 8 bytes of sense descriptor format).
* If zero response code returns false. Otherwise returns true and if 'sshp'
* is non-NULL then zero all fields and then set the appropriate fields in
* that structure. sshp::additional_length is always 0 for response
* codes 0x70 and 0x71 (fixed format). */
bool sg_scsi_normalize_sense(const unsigned char * sensep, int sense_len,
struct sg_scsi_sense_hdr * sshp);
/* Attempt to find the first SCSI sense data descriptor that matches the
* given 'desc_type'. If found return pointer to start of sense data
* descriptor; otherwise (including fixed format sense data) returns NULL. */
const unsigned char * sg_scsi_sense_desc_find(const unsigned char * sensep,
int sense_len, int desc_type);
/* Get sense key from sense buffer. If successful returns a sense key value
* between 0 and 15. If sense buffer cannot be decode, returns -1 . */
int sg_get_sense_key(const unsigned char * sensep, int sense_len);
/* Yield string associated with sense_key value. Returns 'buff'. */
char * sg_get_sense_key_str(int sense_key, int buff_len, char * buff);
/* Yield string associated with ASC/ASCQ values. Returns 'buff'. */
char * sg_get_asc_ascq_str(int asc, int ascq, int buff_len, char * buff);
/* Returns true if valid bit set, false if valid bit clear. Irrespective the
* information field is written out via 'info_outp' (except when it is
* NULL). Handles both fixed and descriptor sense formats. */
bool sg_get_sense_info_fld(const unsigned char * sensep, int sb_len,
uint64_t * info_outp);
/* Returns true if fixed format or command specific information descriptor
* is found in the descriptor sense; else false. If available the command
* specific information field (4 byte integer in fixed format, 8 byte
* integer in descriptor format) is written out via 'cmd_spec_outp'.
* Handles both fixed and descriptor sense formats. */
bool sg_get_sense_cmd_spec_fld(const unsigned char * sensep, int sb_len,
uint64_t * cmd_spec_outp);
/* Returns true if any of the 3 bits (i.e. FILEMARK, EOM or ILI) are set.
* In descriptor format if the stream commands descriptor not found
* then returns false. Writes true or false corresponding to these bits to
* the last three arguments if they are non-NULL. */
bool sg_get_sense_filemark_eom_ili(const unsigned char * sensep, int sb_len,
bool * filemark_p, bool * eom_p,
bool * ili_p);
/* Returns true if SKSV is set and sense key is NO_SENSE or NOT_READY. Also
* returns true if progress indication sense data descriptor found. Places
* progress field from sense data where progress_outp points. If progress
* field is not available returns false. Handles both fixed and descriptor
* sense formats. N.B. App should multiply by 100 and divide by 65536
* to get percentage completion from given value. */
bool sg_get_sense_progress_fld(const unsigned char * sensep, int sb_len,
int * progress_outp);
/* Closely related to sg_print_sense(). Puts decoded sense data in 'buff'.
* Usually multiline with multiple '\n' including one trailing. If
* 'raw_sinfo' set appends sense buffer in hex. 'leadin' is string prepended
* to each line written to 'buff', NULL treated as "". Returns the number of
* bytes written to 'buff' excluding the trailing '\0'.
* N.B. prior to sg3_utils v 1.42 'leadin' was only prepended to the first
* line output. Also this function returned type void. */
int sg_get_sense_str(const char * leadin, const unsigned char * sense_buffer,
int sb_len, bool raw_sinfo, int buff_len, char * buff);
/* Decode descriptor format sense descriptors (assumes sense buffer is
* in descriptor format). 'leadin' is string prepended to each line written
* to 'b', NULL treated as "". Returns the number of bytes written to 'b'
* excluding the trailing '\0'. */
int sg_get_sense_descriptors_str(const char * leadin,
const unsigned char * sense_buffer,
int sb_len, int blen, char * b);
/* Decodes a designation descriptor (e.g. as found in the Device
* Identification VPD page (0x83)) into string 'b' whose maximum length is
* blen. 'leadin' is string prepended to each line written to 'b', NULL
* treated as "". Returns the number of bytes written to 'b' excluding the
* trailing '\0'. */
int sg_get_designation_descriptor_str(const char * leadin,
const unsigned char * ddp, int dd_len,
bool print_assoc, bool do_long,
int blen, char * b);
/* Yield string associated with peripheral device type (pdt). Returns
* 'buff'. If 'pdt' out of range yields "bad pdt" string. */
char * sg_get_pdt_str(int pdt, int buff_len, char * buff);
/* Some lesser used PDTs share a lot in common with a more used PDT.
* Examples are PDT_ADC decaying to PDT_TAPE and PDT_ZBC to PDT_DISK.
* If such a lesser used 'pdt' is given to this function, then it will
* return the more used PDT (i.e. "decays to"); otherwise 'pdt' is returned.
* Valid for 'pdt' 0 to 31, for other values returns 0. */
int sg_lib_pdt_decay(int pdt);
/* Yield string associated with transport protocol identifier (tpi). Returns
* 'buff'. If 'tpi' out of range yields "bad tpi" string. */
char * sg_get_trans_proto_str(int tpi, int buff_len, char * buff);
/* Decode TransportID pointed to by 'bp' of length 'bplen'. Place decoded
* string output in 'buff' which is also the return value. Each new line
* is prefixed by 'leadin'. If leadin NULL treat as "". */
char * sg_decode_transportid_str(const char * leadin, unsigned char * bp,
int bplen, bool only_one, int buff_len,
char * buff);
/* Returns a designator's type string given 'val' (0 to 15 inclusive),
* otherwise returns NULL. */
const char * sg_get_desig_type_str(int val);
/* Returns a designator's code_set string given 'val' (0 to 15 inclusive),
* otherwise returns NULL. */
const char * sg_get_desig_code_set_str(int val);
/* Returns a designator's association string given 'val' (0 to 3 inclusive),
* otherwise returns NULL. */
const char * sg_get_desig_assoc_str(int val);
/* Yield SCSI Feature Set (sfs) string. When 'peri_type' is < -1 (or > 31)
* returns pointer to string (same as 'buff') associated with 'sfs_code'.
* When 'peri_type' is between -1 (for SPC) and 31 (inclusive) then a match
* on both 'sfs_code' and 'peri_type' is required. If 'foundp' is not NULL
* then where it points is set to true if a match is found else it is set to
* false. If 'buff' is not NULL then in the case of a match a descriptive
* string is written to 'buff' while if there is not a not then a string
* ending in "Reserved" is written (and may be prefixed with SPC, SBC, SSC
* or ZBC). Returns 'buff' (i.e. a pointer value) even if it is NULL.
* Example:
* char b[64];
* ...
* printf("%s\n", sg_get_sfs_str(sfs_code, -2, sizeof(b), b, NULL, 0));
*/
const char * sg_get_sfs_str(uint16_t sfs_code, int peri_type, int buff_len,
char * buff, bool * foundp, int verbose);
/* This is a heuristic that takes into account the command bytes and length
* to decide whether the presented unstructured sequence of bytes could be
* a SCSI command. If so it returns true otherwise false. Vendor specific
* SCSI commands (i.e. opcodes from 0xc0 to 0xff), if presented, are assumed
* to follow SCSI conventions (i.e. length of 6, 10, 12 or 16 bytes). The
* only SCSI commands considered above 16 bytes of length are the Variable
* Length Commands (opcode 0x7f) and the XCDB wrapped commands (opcode 0x7e).
* Both have an inbuilt length field which can be cross checked with clen.
* No NVMe commands (64 bytes long plus some extra added by some OSes) have
* opcodes 0x7e or 0x7f yet. ATA is register based but SATA has FIS
* structures that are sent across the wire. The 'FIS register' structure is
* used to move a command from a SATA host to device, but the ATA 'command'
* is not the first byte. So it is harder to say what will happen if a
* FIS structure is presented as a SCSI command, hopfully there is a low
* probability this function will yield true in that case. */
bool sg_is_scsi_cdb(const uint8_t * cdbp, int clen);
/* Yield string associated with NVMe command status value in sct_sc. It
* expects to decode DW3 bits 27:17 from the completion queue. Bits 27:25
* are the Status Code Type (SCT) and bits 24:17 are the Status Code (SC).
* Bit 17 in DW3 should be bit 0 in sct_sc. If no status string is found
* a string of the form "Reserved [0x<sct_sc_in_hex>]" is generated.
* Returns 'buff'. Does nothing if buff_len<=0 or if buff is NULL.*/
char * sg_get_nvme_cmd_status_str(uint16_t sct_sc, int buff_len, char * buff);
/* Attempts to map NVMe status value ((SCT << 8) | SC) n sct_sc to a SCSI
* status, sense_key, asc and ascq tuple. If successful returns true and
* writes to non-NULL pointer arguments; otherwise returns false. */
bool sg_nvme_status2scsi(uint16_t sct_sc, uint8_t * status_p, uint8_t * sk_p,
uint8_t * asc_p, uint8_t * ascq_p);
extern FILE * sg_warnings_strm;
void sg_set_warnings_strm(FILE * warnings_strm);
/* The following "print" functions send ACSII to 'sg_warnings_strm' file
* descriptor (default value is stderr). 'leadin' is string prepended to
* each line printed out, NULL treated as "". */
void sg_print_command(const unsigned char * command);
void sg_print_scsi_status(int scsi_status);
/* 'leadin' is string prepended to each line printed out, NULL treated as
* "". N.B. prior to sg3_utils v 1.42 'leadin' was only prepended to the
* first line printed. */
void sg_print_sense(const char * leadin, const unsigned char * sense_buffer,
int sb_len, bool raw_info);
/* Following examines exit_status and outputs a clear error message to
* warnings_strm (usually stderr) if one is known and returns true.
* Otherwise it doesn't print anything and returns false. Note that if
* exit_status==0 then returns true but prints nothing and if
* exit_status<0 ("some error occurred") false is returned. If leadin is
* non-NULL is will be printed before error message. */
bool sg_if_can2stderr(const char * leadin, int exit_status);
/* Utilities can use these exit status values for syntax errors and
* file (device node) problems (e.g. not found or permissions). */
#define SG_LIB_SYNTAX_ERROR 1 /* command line syntax problem */
#define SG_LIB_FILE_ERROR 15 /* device or other file problem */
/* The sg_err_category_sense() function returns one of the following.
* These may be used as exit status values (from a process). Notice that
* some of the lower values correspond to SCSI sense key values. */
#define SG_LIB_CAT_CLEAN 0 /* No errors or other information */
/* Value 1 left unused for utilities to use SG_LIB_SYNTAX_ERROR */
#define SG_LIB_CAT_NOT_READY 2 /* sense key, unit stopped? */
/* [sk,asc,ascq: 0x2,*,*] */
#define SG_LIB_CAT_MEDIUM_HARD 3 /* medium or hardware error, blank check */
/* [sk,asc,ascq: 0x3/0x4/0x8,*,*] */
#define SG_LIB_CAT_ILLEGAL_REQ 5 /* Illegal request (other than invalid */
/* opcode): [sk,asc,ascq: 0x5,*,*] */
#define SG_LIB_CAT_UNIT_ATTENTION 6 /* sense key, device state changed */
/* [sk,asc,ascq: 0x6,*,*] */
/* was SG_LIB_CAT_MEDIA_CHANGED earlier [sk,asc,ascq: 0x6,0x28,*] */
#define SG_LIB_CAT_DATA_PROTECT 7 /* sense key, media write protected? */
/* [sk,asc,ascq: 0x7,*,*] */
#define SG_LIB_CAT_INVALID_OP 9 /* (Illegal request,) Invalid opcode: */
/* [sk,asc,ascq: 0x5,0x20,0x0] */
#define SG_LIB_CAT_COPY_ABORTED 10 /* sense key, some data transferred */
/* [sk,asc,ascq: 0xa,*,*] */
#define SG_LIB_CAT_ABORTED_COMMAND 11 /* interpreted from sense buffer */
/* [sk,asc,ascq: 0xb,! 0x10,*] */
#define SG_LIB_CAT_MISCOMPARE 14 /* sense key, probably verify */
/* [sk,asc,ascq: 0xe,*,*] */
#define SG_LIB_CAT_NO_SENSE 20 /* sense data with key of "no sense" */
/* [sk,asc,ascq: 0x0,*,*] */
#define SG_LIB_CAT_RECOVERED 21 /* Successful command after recovered err */
/* [sk,asc,ascq: 0x1,*,*] */
#define SG_LIB_CAT_RES_CONFLICT SAM_STAT_RESERVATION_CONFLICT
/* 24: this is a SCSI status, not sense. */
/* It indicates reservation by another */
/* machine blocks this command */
#define SG_LIB_CAT_CONDITION_MET 25 /* SCSI status, not sense key. */
/* Only from PRE-FETCH (SBC-4) */
#define SG_LIB_CAT_BUSY 26 /* SCSI status, not sense. Invites retry */
#define SG_LIB_CAT_TS_FULL 27 /* SCSI status, not sense. Wait then retry */
#define SG_LIB_CAT_ACA_ACTIVE 28 /* SCSI status; ACA seldom used */
#define SG_LIB_CAT_TASK_ABORTED 29 /* SCSI status, this command aborted by? */
#define SG_LIB_CAT_PROTECTION 40 /* subset of aborted command (for PI, DIF) */
/* [sk,asc,ascq: 0xb,0x10,*] */
#define SG_LIB_NVME_STATUS 48 /* NVMe Status Field (SF) other than 0 */
#define SG_LIB_WILD_RESID 49 /* Residual value for data-in transfer of a */
/* SCSI command is nonsensical */
#define SG_LIB_OS_BASE_ERR 50 /* in Linux: values found in: */
/* include/uapi/asm-generic/errno-base.h */
/* Example: ENOMEM reported as 62 (=50+12) */
#define SG_LIB_CAT_MALFORMED 97 /* Response to SCSI command malformed */
#define SG_LIB_CAT_SENSE 98 /* Something else is in the sense buffer */
#define SG_LIB_CAT_OTHER 99 /* Some other error/warning has occurred */
/* (e.g. a transport or driver error) */
/* Returns a SG_LIB_CAT_* value. If cannot decode sense_buffer or a less
* common sense key then return SG_LIB_CAT_SENSE .*/
int sg_err_category_sense(const unsigned char * sense_buffer, int sb_len);
/* Here are some additional sense data categories that are not returned
* by sg_err_category_sense() but are returned by some related functions. */
#define SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO 17 /* Illegal request (other than */
/* invalid opcode) plus 'info' field: */
/* [sk,asc,ascq: 0x5,*,*] */
#define SG_LIB_CAT_MEDIUM_HARD_WITH_INFO 18 /* medium or hardware error */
/* sense key plus 'info' field: */
/* [sk,asc,ascq: 0x3/0x4,*,*] */
#define SG_LIB_CAT_PROTECTION_WITH_INFO 41 /* aborted command sense key, */
/* protection plus 'info' field: */
/* [sk,asc,ascq: 0xb,0x10,*] */
#define SG_LIB_CAT_TIMEOUT 33
/* Yield string associated with sense category. Returns 'buff' (or pointer
* to "Bad sense category" if 'buff' is NULL). If sense_cat unknown then
* yield "Sense category: <sense_cat>" string. */
const char * sg_get_category_sense_str(int sense_cat, int buff_len,
char * buff, int verbose);
/* Iterates to next designation descriptor in the device identification
* VPD page. The 'initial_desig_desc' should point to start of first
* descriptor with 'page_len' being the number of valid bytes in that
* and following descriptors. To start, 'off' should point to a negative
* value, thereafter it should point to the value yielded by the previous
* call. If 0 returned then 'initial_desig_desc + *off' should be a valid
* descriptor; returns -1 if normal end condition and -2 for an abnormal
* termination. Matches association, designator_type and/or code_set when
* any of those values are greater than or equal to zero. */
int sg_vpd_dev_id_iter(const unsigned char * initial_desig_desc, int page_len,
int * off, int m_assoc, int m_desig_type,
int m_code_set);
/* <<< General purpose (i.e. not SCSI specific) utility functions >>> */
/* Always returns valid string even if errnum is wild (or library problem).
* If errnum is negative, flip its sign. */
char * safe_strerror(int errnum);
/* Print (to stdout) 'str' of bytes in hex, 16 bytes per line optionally
* followed at the right hand side of the line with an ASCII interpretation.
* Each line is prefixed with an address, starting at 0 for str[0]..str[15].
* All output numbers are in hex. 'no_ascii' allows for 3 output types:
* > 0 each line has address then up to 16 ASCII-hex bytes
* = 0 in addition, the bytes are listed in ASCII to the right
* < 0 only the ASCII-hex bytes are listed (i.e. without address)
*/
void dStrHex(const char * str, int len, int no_ascii);
/* Print (to sg_warnings_strm (stderr)) 'str' of bytes in hex, 16 bytes per
* line optionally followed at right by its ASCII interpretation. Same
* logic as dStrHex() with different output stream (i.e. stderr). */
void dStrHexErr(const char * str, int len, int no_ascii);
/* Read 'len' bytes from 'str' and output as ASCII-Hex bytes (space
* separated) to 'b' not to exceed 'b_len' characters. Each line
* starts with 'leadin' (NULL for no leadin) and there are 16 bytes
* per line with an extra space between the 8th and 9th bytes. 'format'
* is 0 for repeat in printable ASCII ('.' for non printable chars) to
* right of each line; 1 don't (so just output ASCII hex). Returns
* number of bytes written to 'b' excluding the trailing '\0'. */
int dStrHexStr(const char * str, int len, const char * leadin, int format,
int cb_len, char * cbp);
/* The following 3 functions are equivalent to dStrHex(), dStrHexErr() and
* dStrHexStr() respectively. The difference is the type of the first of
* argument: uint8_t instead of char. The name of the argument is changed
* to b_str to stress it is a pointer to the start of a binary string. */
void hex2stdout(const uint8_t * b_str, int len, int no_ascii);
void hex2stderr(const uint8_t * b_str, int len, int no_ascii);
int hex2str(const uint8_t * b_str, int len, const char * leadin, int format,
int cb_len, char * cbp);
/* Returns true when executed on big endian machine; else returns false.
* Useful for displaying ATA identify words (which need swapping on a
* big endian machine). */
bool sg_is_big_endian();
/* Returns true if byte sequence starting at bp with a length of b_len is
* all zeros (for sg_all_zeros()) or all 0xff_s (for sg_all_ffs());
* otherwise returns false. If bp is NULL ir b_len <= 0 returns false. */
bool sg_all_zeros(const uint8_t * bp, int b_len);
bool sg_all_ffs(const uint8_t * bp, int b_len);
/* Extract character sequence from ATA words as in the model string
* in a IDENTIFY DEVICE response. Returns number of characters
* written to 'ochars' before 0 character is found or 'num' words
* are processed. */
int sg_ata_get_chars(const uint16_t * word_arr, int start_word,
int num_words, bool is_big_endian, char * ochars);
/* Print (to stdout) 16 bit 'words' in hex, 8 words per line optionally
* followed at the right hand side of the line with an ASCII interpretation
* (pairs of ASCII characters in big endian order (upper first)).
* Each line is prefixed with an address, starting at 0.
* All output numbers are in hex. 'no_ascii' allows for 3 output types:
* > 0 each line has address then up to 8 ASCII-hex words
* = 0 in addition, the words are listed in ASCII pairs to the right
* = -1 only the ASCII-hex words are listed (i.e. without address)
* = -2 only the ASCII-hex words, formatted for "hdparm --Istdin"
* < -2 same as -1
* If 'swapb' is true then bytes in each word swapped. Needs to be set
* for ATA IDENTIFY DEVICE response on big-endian machines.
*/
void dWordHex(const uint16_t * words, int num, int no_ascii, bool swapb);
/* If the number in 'buf' can not be decoded or the multiplier is unknown
* then -1 is returned. Accepts a hex prefix (0x or 0X) or a 'h' (or 'H')
* suffix. Otherwise a decimal multiplier suffix may be given. Recognised
* multipliers: c C *1; w W *2; b B *512; k K KiB *1,024;
* KB *1,000; m M MiB *1,048,576; MB *1,000,000; g G GiB *1,073,741,824;
* GB *1,000,000,000 and <n>x<m> which multiplies <n> by <m> . Ignore leading
* spaces and tabs; accept comma, hyphen, space, tab and hash as terminator.
*/
int sg_get_num(const char * buf);
/* If the number in 'buf' can not be decoded then -1 is returned. Accepts a
* hex prefix (0x or 0X) or a 'h' (or 'H') suffix; otherwise decimal is
* assumed. Does not accept multipliers. Accept a comma (","), hyphen ("-"),
* a whitespace or newline as terminator. Only decimal numbers can represent
* negative numbers and '-1' must be treated separately. */
int sg_get_num_nomult(const char * buf);
/* If the number in 'buf' can not be decoded or the multiplier is unknown
* then -1LL is returned. Accepts a hex prefix (0x or 0X) or a 'h' (or 'H')
* suffix. Otherwise a decimal multiplier suffix may be given. In addition
* to supporting the multipliers of sg_get_num(), this function supports:
* t T TiB *(2**40); TB *(10**12); p P PiB *(2**50); PB *(10**15) .
* Ignore leading spaces and tabs; accept comma, hyphen, space, tab and hash
* as terminator. */
int64_t sg_get_llnum(const char * buf);
/* If the number in 'buf' can not be decoded then -1 is returned. Accepts a
* hex prefix (0x or 0X) or a 'h' (or 'H') suffix; otherwise decimal is
* assumed. Does not accept multipliers. Accept a comma (","), hyphen ("-"),
* a whitespace or newline as terminator. Only decimal numbers can represent
* negative numbers and '-1' must be treated separately. */
int64_t sg_get_llnum_nomult(const char * buf);
/* Returns pointer to heap (or NULL) that is aligned to a align_to byte
* boundary. Sends back *buff_to_free pointer in third argument that may be
* different from the return value. If it is different then the *buff_to_free
* pointer should be freed (rather than the returned value) when the heap is
* no longer needed. If align_to is 0 then aligns to OS's page size. Sets all
* returned heap to zeros. If num_bytes is 0 then set to page size. */
uint8_t * sg_memalign(uint32_t num_bytes, uint32_t align_to,
uint8_t ** buff_to_free, bool vb);
/* Returns OS page size in bytes. If uncertain returns 4096. */
uint32_t sg_get_page_size(void);
/* If os_err_num is within bounds then the returned value is 'os_err_num +
* SG_LIB_OS_BASE_ERR' otherwise -1 is returned. If os_err_num is 0 then 0
* is returned. */
int sg_convert_errno(int os_err_num);
/* <<< Architectural support functions [is there a better place?] >>> */
/* Non Unix OSes distinguish between text and binary files.
* Set text mode on fd. Does nothing in Unix. Returns negative number on
* failure. */
int sg_set_text_mode(int fd);
/* Set binary mode on fd. Does nothing in Unix. Returns negative number on
* failure. */
int sg_set_binary_mode(int fd);
#ifdef __cplusplus
}
#endif
#endif /* SG_LIB_H */

View File

@ -1,121 +0,0 @@
#ifndef SG_LIB_DATA_H
#define SG_LIB_DATA_H
/*
* Copyright (c) 2007-2018 Douglas Gilbert.
* All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the BSD_LICENSE file.
*/
/*
* This header file contains some structure declarations and array name
* declarations which are defined in the sg_lib_data.c .
* Typically this header does not need to be exposed to users of the
* sg_lib interface declared in sg_libs.h .
*/
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Operation codes with associated service actions that change or qualify
* the command name */
#define SG_EXTENDED_COPY 0x83 /* since spc4r34 became next entry */
#define SG_3PARTY_COPY_OUT 0x83 /* new in spc4r34: Third party copy out */
#define SG_RECEIVE_COPY 0x84 /* since spc4r34 became next entry */
#define SG_3PARTY_COPY_IN 0x84 /* new in spc4r34: Third party copy in */
#define SG_MAINTENANCE_IN 0xa3
#define SG_MAINTENANCE_OUT 0xa4
#define SG_PERSISTENT_RESERVE_IN 0x5e
#define SG_PERSISTENT_RESERVE_OUT 0x5f
#define SG_READ_ATTRIBUTE 0x8c
#define SG_READ_BUFFER 0x3c /* now READ BUFFER(10) */
#define SG_READ_BUFFER_16 0x9b
#define SG_READ_POSITION 0x34 /* SSC command with service actions */
#define SG_SANITIZE 0x48
#define SG_SERVICE_ACTION_BIDI 0x9d
#define SG_SERVICE_ACTION_IN_12 0xab
#define SG_SERVICE_ACTION_IN_16 0x9e
#define SG_SERVICE_ACTION_OUT_12 0xa9
#define SG_SERVICE_ACTION_OUT_16 0x9f
#define SG_VARIABLE_LENGTH_CMD 0x7f
#define SG_WRITE_BUFFER 0x3b
#define SG_ZONING_OUT 0x94
#define SG_ZONING_IN 0x95
struct sg_lib_simple_value_name_t {
int value;
const char * name;
};
struct sg_lib_value_name_t {
int value;
int peri_dev_type; /* 0 -> SPC and/or PDT_DISK, >0 -> PDT */
const char * name;
};
struct sg_lib_asc_ascq_t {
unsigned char asc; /* additional sense code */
unsigned char ascq; /* additional sense code qualifier */
const char * text;
};
struct sg_lib_asc_ascq_range_t {
unsigned char asc; /* additional sense code (ASC) */
unsigned char ascq_min; /* ASCQ minimum in range */
unsigned char ascq_max; /* ASCQ maximum in range */
const char * text;
};
/* First use: SCSI status, sense_key, asc, ascq tuple */
struct sg_lib_4tuple_u8 {
uint8_t t1;
uint8_t t2;
uint8_t t3;
uint8_t t4;
};
extern const char * sg_lib_version_str;
extern struct sg_lib_value_name_t sg_lib_normal_opcodes[];
extern struct sg_lib_value_name_t sg_lib_read_buff_arr[];
extern struct sg_lib_value_name_t sg_lib_write_buff_arr[];
extern struct sg_lib_value_name_t sg_lib_maint_in_arr[];
extern struct sg_lib_value_name_t sg_lib_maint_out_arr[];
extern struct sg_lib_value_name_t sg_lib_pr_in_arr[];
extern struct sg_lib_value_name_t sg_lib_pr_out_arr[];
extern struct sg_lib_value_name_t sg_lib_sanitize_sa_arr[];
extern struct sg_lib_value_name_t sg_lib_serv_in12_arr[];
extern struct sg_lib_value_name_t sg_lib_serv_out12_arr[];
extern struct sg_lib_value_name_t sg_lib_serv_in16_arr[];
extern struct sg_lib_value_name_t sg_lib_serv_out16_arr[];
extern struct sg_lib_value_name_t sg_lib_serv_bidi_arr[];
extern struct sg_lib_value_name_t sg_lib_xcopy_sa_arr[];
extern struct sg_lib_value_name_t sg_lib_rec_copy_sa_arr[];
extern struct sg_lib_value_name_t sg_lib_variable_length_arr[];
extern struct sg_lib_value_name_t sg_lib_zoning_out_arr[];
extern struct sg_lib_value_name_t sg_lib_zoning_in_arr[];
extern struct sg_lib_value_name_t sg_lib_read_attr_arr[];
extern struct sg_lib_value_name_t sg_lib_read_pos_arr[];
extern struct sg_lib_asc_ascq_range_t sg_lib_asc_ascq_range[];
extern struct sg_lib_asc_ascq_t sg_lib_asc_ascq[];
extern struct sg_lib_value_name_t sg_lib_scsi_feature_sets[];
extern const char * sg_lib_sense_key_desc[];
extern const char * sg_lib_pdt_strs[];
extern const char * sg_lib_transport_proto_strs[];
extern int sg_lib_pdt_decay_arr[];
extern struct sg_lib_value_name_t sg_lib_nvme_cmd_status_arr[];
extern struct sg_lib_4tuple_u8 sg_lib_scsi_status_sense_arr[];
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,56 +0,0 @@
#ifndef SG_LINUX_INC_H
#define SG_LINUX_INC_H
#ifdef SG_KERNEL_INCLUDES
#define __user
typedef unsigned char u8;
#include "/usr/src/linux/include/scsi/sg.h"
#include "/usr/src/linux/include/scsi/scsi.h"
#else
#ifdef SG_TRICK_GNU_INCLUDES
#include <linux/../scsi/sg.h>
#include <linux/../scsi/scsi.h>
#else
#include <scsi/sg.h>
#include <scsi/scsi.h>
#endif
#endif
#ifdef BLKGETSIZE64
#ifndef u64
#include <stdint.h> /* C99 header for exact integer types */
typedef uint64_t u64; /* problems with BLKGETSIZE64 ioctl in lk 2.4 */
#endif
#endif
/*
Getting the correct include files for the sg interface can be an ordeal.
In a perfect world, one would just write:
#include <scsi/sg.h>
#include <scsi/scsi.h>
This would include the files found in the /usr/include/scsi directory.
Those files are maintained with the GNU library which may or may not
agree with the kernel and version of sg driver that is running. Any
many cases this will not matter. However in some it might, for example
glibc 2.1's include files match the sg driver found in the lk 2.2
series. Hence if glibc 2.1 is used with lk 2.4 then the additional
sg v3 interface will not be visible.
If this is a problem then defining SG_KERNEL_INCLUDES will access the
kernel supplied header files (assuming they are in the normal place).
The GNU library maintainers and various kernel people don't like
this approach (but it does work).
The technique selected by defining SG_TRICK_GNU_INCLUDES worked (and
was used) prior to glibc 2.2 . Prior to that version /usr/include/linux
was a symbolic link to /usr/src/linux/include/linux .
There are other approaches if this include "mixup" causes pain. These
would involve include files being copied or symbolic links being
introduced.
Sorry about the inconvenience. Typically neither SG_KERNEL_INCLUDES
nor SG_TRICK_GNU_INCLUDES is defined.
dpg 20010415, 20030522
*/
#endif

View File

@ -1,30 +0,0 @@
#ifndef SG_PR2SERR_H
#define SG_PR2SERR_H
/*
* Copyright (c) 2004-2018 Douglas Gilbert.
* All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the BSD_LICENSE file.
*/
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
#if defined(__GNUC__) || defined(__clang__)
int pr2serr(const char * fmt, ...)
__attribute__ ((format (printf, 1, 2)));
#else
int pr2serr(const char * fmt, ...);
#endif
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,215 +0,0 @@
#ifndef SG_PT_H
#define SG_PT_H
/*
* Copyright (c) 2005-2018 Douglas Gilbert.
* All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the BSD_LICENSE file.
*/
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/* This declaration hides the fact that each implementation has its own
* structure "derived" (using a C++ term) from this one. It compiles
* because 'struct sg_pt_base' is only referenced (by pointer: 'objp')
* in this interface. An instance of this structure represents the
* context of one SCSI command. */
struct sg_pt_base;
/* The format of the version string is like this: "2.01 20090201".
* The leading digit will be incremented if this interface changes
* in a way that may impact backward compatibility. */
const char * scsi_pt_version();
/* Returns >= 0 if successful. If error in Unix returns negated errno. */
int scsi_pt_open_device(const char * device_name, bool read_only, int verbose);
/* Similar to scsi_pt_open_device() but takes Unix style open flags OR-ed
* together. Returns valid file descriptor( >= 0 ) if successful, otherwise
* returns -1 or a negated errno.
* In Win32 O_EXCL translated to equivalent. */
int scsi_pt_open_flags(const char * device_name, int flags, int verbose);
/* Returns 0 if successful. If error in Unix returns negated errno. */
int scsi_pt_close_device(int device_fd);
/* Assumes dev_fd is an "open" file handle associated with device_name. If
* the implementation (possibly for one OS) cannot determine from dev_fd if
* a SCSI or NVMe pass-through is referenced, then it might guess based on
* device_name. Returns 1 if SCSI generic pass-though device, returns 2 if
* secondary SCSI pass-through device (in Linux a bsg device); returns 3 is
* char NVMe device (i.e. no NSID); returns 4 if block NVMe device (includes
* NSID), or 0 if something else (e.g. ATA block device) or dev_fd < 0.
* If error, returns negated errno (operating system) value. */
int check_pt_file_handle(int dev_fd, const char * device_name, int verbose);
/* Creates an object that can be used to issue one or more SCSI commands
* (or task management functions). Returns NULL if problem.
* Once this object has been created it should be destroyed with
* destruct_scsi_pt_obj() when it is no longer needed. */
struct sg_pt_base * construct_scsi_pt_obj(void);
/* An alternate way to create an object that can be used to issue one or
* more SCSI commands (or task management functions). This variant
* associate a device file descriptor (handle) with the object and a
* verbose argument that causes error messages if errors occur. The
* reason for this is to optionally allow the detection of NVMe devices
* that will cause pt_device_is_nvme() to return true. Set dev_fd to
* -1 if no open device file descriptor is available. Caller should
* additionally call get_scsi_pt_os_err() after this call. */
struct sg_pt_base *
construct_scsi_pt_obj_with_fd(int dev_fd, int verbose);
/* Forget any previous dev_fd and install the one given. May attempt to
* find file type (e.g. if pass-though) from OS so there could be an error.
* Returns 0 for success or the same value as get_scsi_pt_os_err()
* will return. dev_fd should be >= 0 for a valid file handle or -1 . */
int set_pt_file_handle(struct sg_pt_base * objp, int dev_fd, int verbose);
/* Valid file handles (which is the return value) are >= 0 . Returns -1
* if there is no valid file handle. */
int get_pt_file_handle(const struct sg_pt_base * objp);
/* Clear state information held in *objp . This allows this object to be
* used to issue more than one SCSI command. The dev_fd is remembered.
* Use set_pt_file_handle() to change dev_fd. */
void clear_scsi_pt_obj(struct sg_pt_base * objp);
/* Set the CDB (command descriptor block) */
void set_scsi_pt_cdb(struct sg_pt_base * objp, const unsigned char * cdb,
int cdb_len);
/* Set the sense buffer and the maximum length that it can handle */
void set_scsi_pt_sense(struct sg_pt_base * objp, unsigned char * sense,
int max_sense_len);
/* Set a pointer and length to be used for data transferred from device */
void set_scsi_pt_data_in(struct sg_pt_base * objp, /* from device */
unsigned char * dxferp, int dxfer_ilen);
/* Set a pointer and length to be used for data transferred to device */
void set_scsi_pt_data_out(struct sg_pt_base * objp, /* to device */
const unsigned char * dxferp, int dxfer_olen);
/* Set a pointer and length to be used for metadata transferred to
* (out_true=true) or from (out_true-false) device */
void set_pt_metadata_xfer(struct sg_pt_base * objp, unsigned char * mdxferp,
uint32_t mdxfer_len, bool out_true);
/* The following "set_"s implementations may be dummies */
void set_scsi_pt_packet_id(struct sg_pt_base * objp, int pack_id);
void set_scsi_pt_tag(struct sg_pt_base * objp, uint64_t tag);
void set_scsi_pt_task_management(struct sg_pt_base * objp, int tmf_code);
void set_scsi_pt_task_attr(struct sg_pt_base * objp, int attribute,
int priority);
/* Following is a guard which is defined when set_scsi_pt_flags() is
* present. Older versions of this library may not have this function. */
#define SCSI_PT_FLAGS_FUNCTION 1
/* If neither QUEUE_AT_HEAD nor QUEUE_AT_TAIL are given, or both
* are given, use the pass-through default. */
#define SCSI_PT_FLAGS_QUEUE_AT_TAIL 0x10
#define SCSI_PT_FLAGS_QUEUE_AT_HEAD 0x20
/* Set (potentially OS dependent) flags for pass-through mechanism.
* Apart from contradictions, flags can be OR-ed together. */
void set_scsi_pt_flags(struct sg_pt_base * objp, int flags);
#define SCSI_PT_DO_START_OK 0
#define SCSI_PT_DO_BAD_PARAMS 1
#define SCSI_PT_DO_TIMEOUT 2
#define SCSI_PT_DO_NVME_STATUS 48 /* == SG_LIB_NVME_STATUS */
/* If OS error prior to or during command submission then returns negated
* error value (e.g. Unix '-errno'). This includes interrupted system calls
* (e.g. by a signal) in which case -EINTR would be returned. Note that
* system call errors also can be fetched with get_scsi_pt_os_err().
* Return 0 if okay (i.e. at the very least: command sent). Positive
* return values are errors (see SCSI_PT_DO_* defines). If a file descriptor
* has already been provided by construct_scsi_pt_obj_with_fd() then the
* given 'fd' can be -1 or the same value as given to the constructor. */
int do_scsi_pt(struct sg_pt_base * objp, int fd, int timeout_secs,
int verbose);
#define SCSI_PT_RESULT_GOOD 0
#define SCSI_PT_RESULT_STATUS 1 /* other than GOOD and CHECK CONDITION */
#define SCSI_PT_RESULT_SENSE 2
#define SCSI_PT_RESULT_TRANSPORT_ERR 3
#define SCSI_PT_RESULT_OS_ERR 4
/* highest numbered applicable category returned */
int get_scsi_pt_result_category(const struct sg_pt_base * objp);
/* If not available return 0 which implies there is no residual
* value. If supported the number of bytes actually sent back by
* the device is 'dxfer_ilen - get_scsi_pt_len()' bytes. */
int get_scsi_pt_resid(const struct sg_pt_base * objp);
/* Returns SCSI status value (from device that received the command). If an
* NVMe command was issued directly (i.e. through do_scsi_pt() then return
* NVMe status (i.e. ((SCT << 8) | SC)) */
int get_scsi_pt_status_response(const struct sg_pt_base * objp);
/* Returns SCSI status value or, if NVMe command given to do_scsi_pt(),
* then returns NVMe result (i.e. DWord(0) from completion queue). If
* 'objp' is NULL then returns 0xffffffff. */
uint32_t get_pt_result(const struct sg_pt_base * objp);
/* Actual sense length returned. If sense data is present but
actual sense length is not known, return 'max_sense_len' */
int get_scsi_pt_sense_len(const struct sg_pt_base * objp);
/* If not available return 0 (for success). */
int get_scsi_pt_os_err(const struct sg_pt_base * objp);
char * get_scsi_pt_os_err_str(const struct sg_pt_base * objp, int max_b_len,
char * b);
/* If not available return 0 (for success) */
int get_scsi_pt_transport_err(const struct sg_pt_base * objp);
void set_scsi_pt_transport_err(struct sg_pt_base * objp, int err);
char * get_scsi_pt_transport_err_str(const struct sg_pt_base * objp,
int max_b_len, char * b);
/* If not available return -1 */
int get_scsi_pt_duration_ms(const struct sg_pt_base * objp);
/* Return true if device associated with 'objp' uses NVMe command set. To
* be useful (in modifying the type of command sent (SCSI or NVMe) then
* construct_scsi_pt_obj_with_fd() should be used followed by an invocation
* of this function. */
bool pt_device_is_nvme(const struct sg_pt_base * objp);
/* If a NVMe block device (which includes the NSID) handle is associated
* with 'objp', then its NSID is returned (values range from 0x1 to
* 0xffffffe). Otherwise 0 is returned. */
uint32_t get_pt_nvme_nsid(const struct sg_pt_base * objp);
/* Should be invoked once per objp after other processing is complete in
* order to clean up resources. For ever successful construct_scsi_pt_obj()
* call there should be one destruct_scsi_pt_obj(). If the
* construct_scsi_pt_obj_with_fd() function was used to create this object
* then the dev_fd provided to that constructor is not altered by this
* destructor. So the user should still close dev_fd (perhaps with
* scsi_pt_close_device() ). */
void destruct_scsi_pt_obj(struct sg_pt_base * objp);
#ifdef SG_LIB_WIN32
#define SG_LIB_WIN32_DIRECT 1
/* Request SPT direct interface when state_direct is 1, state_direct set
* to 0 for the SPT indirect interface. Default setting selected by build
* (i.e. library compile time) and is usually indirect. */
void scsi_pt_win32_direct(int state_direct);
/* Returns current SPT interface state, 1 for direct, 0 for indirect */
int scsi_pt_win32_spt_state(void);
#endif
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,171 +0,0 @@
#ifndef SG_PT_LINUX_H
#define SG_PT_LINUX_H
/*
* Copyright (c) 2017 Douglas Gilbert.
* All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the BSD_LICENSE file.
*/
#include <stdint.h>
#include <stdbool.h>
#include <linux/types.h>
#include "sg_pt_nvme.h"
/* This header is for internal use by the sg3_utils library (libsgutils)
* and is Linux specific. Best not to include it directly in code that
* is meant to be OS independent. */
#ifdef __cplusplus
extern "C" {
#endif
#ifndef HAVE_LINUX_BSG_H
#define BSG_PROTOCOL_SCSI 0
#define BSG_SUB_PROTOCOL_SCSI_CMD 0
#define BSG_SUB_PROTOCOL_SCSI_TMF 1
#define BSG_SUB_PROTOCOL_SCSI_TRANSPORT 2
/*
* For flag constants below:
* sg.h sg_io_hdr also has bits defined for it's flags member. These
* two flag values (0x10 and 0x20) have the same meaning in sg.h . For
* bsg the BSG_FLAG_Q_AT_HEAD flag is ignored since it is the default.
*/
#define BSG_FLAG_Q_AT_TAIL 0x10 /* default is Q_AT_HEAD */
#define BSG_FLAG_Q_AT_HEAD 0x20
struct sg_io_v4 {
__s32 guard; /* [i] 'Q' to differentiate from v3 */
__u32 protocol; /* [i] 0 -> SCSI , .... */
__u32 subprotocol; /* [i] 0 -> SCSI command, 1 -> SCSI task
management function, .... */
__u32 request_len; /* [i] in bytes */
__u64 request; /* [i], [*i] {SCSI: cdb} */
__u64 request_tag; /* [i] {SCSI: task tag (only if flagged)} */
__u32 request_attr; /* [i] {SCSI: task attribute} */
__u32 request_priority; /* [i] {SCSI: task priority} */
__u32 request_extra; /* [i] {spare, for padding} */
__u32 max_response_len; /* [i] in bytes */
__u64 response; /* [i], [*o] {SCSI: (auto)sense data} */
/* "dout_": data out (to device); "din_": data in (from device) */
__u32 dout_iovec_count; /* [i] 0 -> "flat" dout transfer else
dout_xfer points to array of iovec */
__u32 dout_xfer_len; /* [i] bytes to be transferred to device */
__u32 din_iovec_count; /* [i] 0 -> "flat" din transfer */
__u32 din_xfer_len; /* [i] bytes to be transferred from device */
__u64 dout_xferp; /* [i], [*i] */
__u64 din_xferp; /* [i], [*o] */
__u32 timeout; /* [i] units: millisecond */
__u32 flags; /* [i] bit mask */
__u64 usr_ptr; /* [i->o] unused internally */
__u32 spare_in; /* [i] */
__u32 driver_status; /* [o] 0 -> ok */
__u32 transport_status; /* [o] 0 -> ok */
__u32 device_status; /* [o] {SCSI: command completion status} */
__u32 retry_delay; /* [o] {SCSI: status auxiliary information} */
__u32 info; /* [o] additional information */
__u32 duration; /* [o] time to complete, in milliseconds */
__u32 response_len; /* [o] bytes of response actually written */
__s32 din_resid; /* [o] din_xfer_len - actual_din_xfer_len */
__s32 dout_resid; /* [o] dout_xfer_len - actual_dout_xfer_len */
__u64 generated_tag; /* [o] {SCSI: transport generated task tag} */
__u32 spare_out; /* [o] */
__u32 padding;
};
#else
#include <linux/bsg.h>
#endif
struct sg_pt_linux_scsi {
struct sg_io_v4 io_hdr; /* use v4 header as it is more general */
/* Leave io_hdr in first place of this structure */
bool is_sg;
bool is_bsg;
bool is_nvme; /* OS device type, if false ignore nvme_direct */
bool nvme_direct; /* false: our SNTL; true: received NVMe command */
bool mdxfer_out; /* direction of metadata xfer, true->data-out */
bool scsi_dsense; /* SCSI descriptor sense active when true */
int dev_fd; /* -1 if not given (yet) */
int in_err;
int os_err;
uint32_t nvme_nsid; /* 1 to 0xfffffffe are possibly valid, 0
* implies dev_fd is not a NVMe device
* (is_nvme=false) or it is a NVMe char
* device (e.g. /dev/nvme0 ) */
uint32_t nvme_result; /* DW0 from completion queue */
uint32_t nvme_status; /* SCT|SC: DW3 27:17 from completion queue,
* note: the DNR+More bit are not there.
* The whole 16 byte completion q entry is
* sent back as sense data */
uint32_t mdxfer_len;
void * mdxferp;
uint8_t * nvme_id_ctlp; /* cached response to controller IDENTIFY */
uint8_t * free_nvme_id_ctlp;
unsigned char tmf_request[4];
};
struct sg_pt_base {
struct sg_pt_linux_scsi impl;
};
#ifndef sg_nvme_admin_cmd
#define sg_nvme_admin_cmd sg_nvme_passthru_cmd
#endif
/* Linux NVMe related ioctls */
#ifndef NVME_IOCTL_ID
#define NVME_IOCTL_ID _IO('N', 0x40)
#endif
#ifndef NVME_IOCTL_ADMIN_CMD
#define NVME_IOCTL_ADMIN_CMD _IOWR('N', 0x41, struct sg_nvme_admin_cmd)
#endif
#ifndef NVME_IOCTL_SUBMIT_IO
#define NVME_IOCTL_SUBMIT_IO _IOW('N', 0x42, struct sg_nvme_user_io)
#endif
#ifndef NVME_IOCTL_IO_CMD
#define NVME_IOCTL_IO_CMD _IOWR('N', 0x43, struct sg_nvme_passthru_cmd)
#endif
#ifndef NVME_IOCTL_RESET
#define NVME_IOCTL_RESET _IO('N', 0x44)
#endif
#ifndef NVME_IOCTL_SUBSYS_RESET
#define NVME_IOCTL_SUBSYS_RESET _IO('N', 0x45)
#endif
extern bool sg_bsg_nvme_char_major_checked;
extern int sg_bsg_major;
extern volatile int sg_nvme_char_major;
extern long sg_lin_page_size;
void sg_find_bsg_nvme_char_major(int verbose);
int sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int time_secs, int vb);
/* This trims given NVMe block device name in Linux (e.g. /dev/nvme0n1p5)
* to the name of its associated char device (e.g. /dev/nvme0). If this
* occurs true is returned and the char device name is placed in 'b' (as
* long as b_len is sufficient). Otherwise false is returned. */
bool sg_get_nvme_char_devname(const char * nvme_block_devname, uint32_t b_len,
char * b);
#ifdef __cplusplus
}
#endif
#endif /* end of SG_PT_LINUX_H */

View File

@ -1,172 +0,0 @@
#ifndef SG_PT_NVME_H
#define SG_PT_NVME_H
/*
* Copyright (c) 2017-2018 Douglas Gilbert.
* All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the BSD_LICENSE file.
*/
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/* structures copied and slightly modified from <linux/nvme_ioctl.h> which
* is Copyright (c) 2011-2014, Intel Corporation. */
/* Note that the command input structure is in (packed) "cpu" format. That
* means, for example, if the CPU is little endian (most are) then so is the
* structure. However what comes out in the data-in buffer (e.g. for the
* Admin Identify command response) is almost all little endian following ATA
* (but no SCSI and IP which are big endian) and Intel's preference. There
* are exceptions, for example the EUI-64 identifiers in the Admin Identify
* response are big endian.
*
* Code online (e.g. nvme-cli at github.com) seems to like packed strcutures,
* the author prefers byte offset plus a range of unaligned integer builders
* such as those in sg_unaligned.h .
*/
#ifdef __GNUC__
#ifndef __clang__
struct __attribute__((__packed__)) sg_nvme_user_io
#else
struct sg_nvme_user_io
#endif
#else
struct sg_nvme_user_io
#endif
{
uint8_t opcode;
uint8_t flags;
uint16_t control;
uint16_t nblocks;
uint16_t rsvd;
uint64_t metadata;
uint64_t addr;
uint64_t slba;
uint32_t dsmgmt;
uint32_t reftag;
uint16_t apptag;
uint16_t appmask;
}
#ifdef SG_LIB_FREEBSD
__packed;
#else
;
#endif
/* Using byte offsets and unaligned be/le copies safer than packed
* structures. These are for sg_nvme_user_io . */
#define SG_NVME_IO_OPCODE 0
#define SG_NVME_IO_FLAGS 1
#define SG_NVME_IO_CONTROL 2
#define SG_NVME_IO_NBLOCKS 4
#define SG_NVME_IO_RSVD 6
#define SG_NVME_IO_METADATA 8
#define SG_NVME_IO_ADDR 16
#define SG_NVME_IO_SLBA 24
#define SG_NVME_IO_DSMGMT 32
#define SG_NVME_IO_REFTAG 36
#define SG_NVME_IO_APPTAG 40
#define SG_NVME_IO_APPMASK 42
#ifdef __GNUC__
#ifndef __clang__
struct __attribute__((__packed__)) sg_nvme_passthru_cmd
#else
struct sg_nvme_passthru_cmd
#endif
#else
struct sg_nvme_passthru_cmd
#endif
{
uint8_t opcode;
uint8_t flags;
uint16_t rsvd1;
uint32_t nsid;
uint32_t cdw2;
uint32_t cdw3;
uint64_t metadata;
uint64_t addr;
uint32_t metadata_len;
uint32_t data_len;
uint32_t cdw10;
uint32_t cdw11;
uint32_t cdw12;
uint32_t cdw13;
uint32_t cdw14;
uint32_t cdw15;
#ifdef SG_LIB_LINUX
uint32_t timeout_ms;
uint32_t result; /* out: DWord(0) from completion queue */
#endif
}
#ifdef SG_LIB_FREEBSD
__packed;
#else
;
#endif
/* Using byte offsets and unaligned be/le copies safer than packed
* structures. These are for sg_nvme_passthru_cmd . */
#define SG_NVME_PT_OPCODE 0 /* length: 1 byte */
#define SG_NVME_PT_FLAGS 1 /* length: 1 byte */
#define SG_NVME_PT_RSVD1 2 /* length: 2 bytes */
#define SG_NVME_PT_NSID 4 /* length: 4 bytes */
#define SG_NVME_PT_CDW2 8 /* length: 4 bytes */
#define SG_NVME_PT_CDW3 12 /* length: 4 bytes */
#define SG_NVME_PT_METADATA 16 /* length: 8 bytes */
#define SG_NVME_PT_ADDR 24 /* length: 8 bytes */
#define SG_NVME_PT_METADATA_LEN 32 /* length: 4 bytes */
#define SG_NVME_PT_DATA_LEN 36 /* length: 4 bytes */
#define SG_NVME_PT_CDW10 40 /* length: 4 bytes */
#define SG_NVME_PT_CDW11 44 /* length: 4 bytes */
#define SG_NVME_PT_CDW12 48 /* length: 4 bytes */
#define SG_NVME_PT_CDW13 52 /* length: 4 bytes */
#define SG_NVME_PT_CDW14 56 /* length: 4 bytes */
#define SG_NVME_PT_CDW15 60 /* length: 4 bytes */
#ifdef SG_LIB_LINUX
/* General references state that "all NVMe commands are 64 bytes long". If
* so then the following are add-ons by Linux, go to the OS and not the
* the NVMe device. */
#define SG_NVME_PT_TIMEOUT_MS 64 /* length: 4 bytes */
#define SG_NVME_PT_RESULT 68 /* length: 4 bytes */
#endif
/* Byte offset of Result and Status (plus phase bit) in CQ */
#define SG_NVME_PT_CQ_RESULT 0 /* CDW0, length: 4 bytes */
#define SG_NVME_PT_CQ_DW0 0 /* CDW0, length: 4 bytes */
#define SG_NVME_PT_CQ_DW1 4 /* CDW1, length: 4 bytes */
#define SG_NVME_PT_CQ_DW2 8 /* CDW2, length: 4 bytes */
#define SG_NVME_PT_CQ_DW3 12 /* CDW3, length: 4 bytes */
#define SG_NVME_PT_CQ_STATUS_P 14 /* CDW3 31:16, length: 2 bytes */
/* Valid namespace IDs (nsid_s) range from 1 to 0xfffffffe, leaving: */
#define SG_NVME_BROADCAST_NSID 0xffffffff /* all namespaces */
#define SG_NVME_CTL_NSID 0x0 /* the "controller's" namespace */
/* Given the NVMe Identify Controller response and optionally the NVMe
* Identify Namespace response (NULL otherwise), generate the SCSI VPD
* page 0x83 (device identification) descriptor(s) in dop. Return the
* number of bytes written which will not exceed max_do_len. Probably use
* Peripheral Device Type (pdt) of 0 (disk) for don't know. Transport
* protocol (tproto) should be -1 if not known, else SCSI value.
* N.B. Does not write total VPD page length into dop[2:3] . */
int sg_make_vpd_devid_for_nvme(const uint8_t * nvme_id_ctl_p,
const uint8_t * nvme_id_ns_p, int pdt,
int tproto, uint8_t * dop, int max_do_len);
#ifdef __cplusplus
}
#endif
#endif /* SG_PT_NVME_H */

View File

@ -1,473 +0,0 @@
#ifndef SG_PT_WIN32_H
#define SG_PT_WIN32_H
/*
* The information in this file was obtained from scsi-wnt.h by
* Richard Stemmer, rs@epost.de . He in turn gives credit to
* Jay A. Key (for scsipt.c).
* The plscsi program (by Pat LaVarre <p.lavarre@ieee.org>) has
* also been used as a reference.
* Much of the information in this header can also be obtained
* from msdn.microsoft.com .
* Updated for cygwin version 1.7.17 changes 20121026
*/
/* WIN32_LEAN_AND_MEAN may be required to prevent inclusion of <winioctl.h> */
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#ifdef __cplusplus
extern "C" {
#endif
#define SCSI_MAX_SENSE_LEN 64
#define SCSI_MAX_CDB_LEN 16
#define SCSI_MAX_INDIRECT_DATA 16384
typedef struct {
USHORT Length;
UCHAR ScsiStatus;
UCHAR PathId;
UCHAR TargetId;
UCHAR Lun;
UCHAR CdbLength;
UCHAR SenseInfoLength;
UCHAR DataIn;
ULONG DataTransferLength;
ULONG TimeOutValue;
ULONG_PTR DataBufferOffset; /* was ULONG; problem in 64 bit */
ULONG SenseInfoOffset;
UCHAR Cdb[SCSI_MAX_CDB_LEN];
} SCSI_PASS_THROUGH, *PSCSI_PASS_THROUGH;
typedef struct {
USHORT Length;
UCHAR ScsiStatus;
UCHAR PathId;
UCHAR TargetId;
UCHAR Lun;
UCHAR CdbLength;
UCHAR SenseInfoLength;
UCHAR DataIn;
ULONG DataTransferLength;
ULONG TimeOutValue;
PVOID DataBuffer;
ULONG SenseInfoOffset;
UCHAR Cdb[SCSI_MAX_CDB_LEN];
} SCSI_PASS_THROUGH_DIRECT, *PSCSI_PASS_THROUGH_DIRECT;
typedef struct {
SCSI_PASS_THROUGH spt;
/* plscsi shows a follow on 16 bytes allowing 32 byte cdb */
ULONG Filler;
UCHAR ucSenseBuf[SCSI_MAX_SENSE_LEN];
UCHAR ucDataBuf[SCSI_MAX_INDIRECT_DATA];
} SCSI_PASS_THROUGH_WITH_BUFFERS, *PSCSI_PASS_THROUGH_WITH_BUFFERS;
typedef struct {
SCSI_PASS_THROUGH_DIRECT spt;
ULONG Filler;
UCHAR ucSenseBuf[SCSI_MAX_SENSE_LEN];
} SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, *PSCSI_PASS_THROUGH_DIRECT_WITH_BUFFER;
typedef struct {
UCHAR NumberOfLogicalUnits;
UCHAR InitiatorBusId;
ULONG InquiryDataOffset;
} SCSI_BUS_DATA, *PSCSI_BUS_DATA;
typedef struct {
UCHAR NumberOfBusses;
SCSI_BUS_DATA BusData[1];
} SCSI_ADAPTER_BUS_INFO, *PSCSI_ADAPTER_BUS_INFO;
typedef struct {
UCHAR PathId;
UCHAR TargetId;
UCHAR Lun;
BOOLEAN DeviceClaimed;
ULONG InquiryDataLength;
ULONG NextInquiryDataOffset;
UCHAR InquiryData[1];
} SCSI_INQUIRY_DATA, *PSCSI_INQUIRY_DATA;
typedef struct {
ULONG Length;
UCHAR PortNumber;
UCHAR PathId;
UCHAR TargetId;
UCHAR Lun;
} SCSI_ADDRESS, *PSCSI_ADDRESS;
/*
* Standard IOCTL define
*/
#ifndef CTL_CODE
#define CTL_CODE(DevType, Function, Method, Access) \
(((DevType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
#endif
/*
* file access values
*/
#ifndef FILE_ANY_ACCESS
#define FILE_ANY_ACCESS 0
#endif
#ifndef FILE_READ_ACCESS
#define FILE_READ_ACCESS 0x0001
#endif
#ifndef FILE_WRITE_ACCESS
#define FILE_WRITE_ACCESS 0x0002
#endif
// IOCTL_STORAGE_QUERY_PROPERTY
#define FILE_DEVICE_MASS_STORAGE 0x0000002d
#define IOCTL_STORAGE_BASE FILE_DEVICE_MASS_STORAGE
#define FILE_ANY_ACCESS 0
// #define METHOD_BUFFERED 0
#define IOCTL_STORAGE_QUERY_PROPERTY \
CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS)
#ifndef _DEVIOCTL_
typedef enum _STORAGE_BUS_TYPE {
BusTypeUnknown = 0x00,
BusTypeScsi = 0x01,
BusTypeAtapi = 0x02,
BusTypeAta = 0x03,
BusType1394 = 0x04,
BusTypeSsa = 0x05,
BusTypeFibre = 0x06,
BusTypeUsb = 0x07,
BusTypeRAID = 0x08,
BusTypeiScsi = 0x09,
BusTypeSas = 0x0A,
BusTypeSata = 0x0B,
BusTypeSd = 0x0C,
BusTypeMmc = 0x0D,
BusTypeVirtual = 0xE,
BusTypeFileBackedVirtual = 0xF,
BusTypeSpaces = 0x10,
BusTypeNvme = 0x11,
BusTypeSCM = 0x12,
BusTypeUfs = 0x13,
BusTypeMax = 0x14,
BusTypeMaxReserved = 0x7F
} STORAGE_BUS_TYPE, *PSTORAGE_BUS_TYPE;
typedef enum _STORAGE_PROTOCOL_TYPE {
ProtocolTypeUnknown = 0,
ProtocolTypeScsi,
ProtocolTypeAta,
ProtocolTypeNvme,
ProtocolTypeSd
} STORAGE_PROTOCOL_TYPE;
typedef enum _STORAGE_PROTOCOL_NVME_DATA_TYPE {
NVMeDataTypeUnknown = 0,
NVMeDataTypeIdentify,
NVMeDataTypeLogPage,
NVMeDataTypeFeature
} STORAGE_PROTOCOL_NVME_DATA_TYPE;
typedef struct _STORAGE_PROTOCOL_SPECIFIC_DATA {
STORAGE_PROTOCOL_TYPE ProtocolType;
ULONG DataType;
ULONG ProtocolDataRequestValue;
ULONG ProtocolDataRequestSubValue;
ULONG ProtocolDataOffset;
ULONG ProtocolDataLength;
ULONG FixedProtocolReturnData;
ULONG Reserved[3];
} STORAGE_PROTOCOL_SPECIFIC_DATA;
typedef struct _STORAGE_DEVICE_DESCRIPTOR {
ULONG Version;
ULONG Size;
UCHAR DeviceType;
UCHAR DeviceTypeModifier;
BOOLEAN RemovableMedia;
BOOLEAN CommandQueueing;
ULONG VendorIdOffset; /* 0 if not available */
ULONG ProductIdOffset; /* 0 if not available */
ULONG ProductRevisionOffset;/* 0 if not available */
ULONG SerialNumberOffset; /* -1 if not available ?? */
STORAGE_BUS_TYPE BusType;
ULONG RawPropertiesLength;
UCHAR RawDeviceProperties[1];
} STORAGE_DEVICE_DESCRIPTOR, *PSTORAGE_DEVICE_DESCRIPTOR;
#define STORAGE_PROTOCOL_STRUCTURE_VERSION 0x1
#define IOCTL_STORAGE_PROTOCOL_COMMAND \
CTL_CODE(IOCTL_STORAGE_BASE, 0x04F0, METHOD_BUFFERED, \
FILE_READ_ACCESS | FILE_WRITE_ACCESS)
typedef struct _STORAGE_PROTOCOL_COMMAND {
DWORD Version; /* STORAGE_PROTOCOL_STRUCTURE_VERSION */
DWORD Length;
STORAGE_PROTOCOL_TYPE ProtocolType;
DWORD Flags;
DWORD ReturnStatus;
DWORD ErrorCode;
DWORD CommandLength;
DWORD ErrorInfoLength;
DWORD DataToDeviceTransferLength;
DWORD DataFromDeviceTransferLength;
DWORD TimeOutValue;
DWORD ErrorInfoOffset;
DWORD DataToDeviceBufferOffset;
DWORD DataFromDeviceBufferOffset;
DWORD CommandSpecific;
DWORD Reserved0;
DWORD FixedProtocolReturnData;
DWORD Reserved1[3];
BYTE Command[1]; /* has CommandLength elements */
} STORAGE_PROTOCOL_COMMAND, *PSTORAGE_PROTOCOL_COMMAND;
#endif /* _DEVIOCTL_ */
typedef struct _STORAGE_DEVICE_UNIQUE_IDENTIFIER {
ULONG Version;
ULONG Size;
ULONG StorageDeviceIdOffset;
ULONG StorageDeviceOffset;
ULONG DriveLayoutSignatureOffset;
} STORAGE_DEVICE_UNIQUE_IDENTIFIER, *PSTORAGE_DEVICE_UNIQUE_IDENTIFIER;
// Use CompareStorageDuids(PSTORAGE_DEVICE_UNIQUE_IDENTIFIER duid1, duid2)
// to test for equality
#ifndef _DEVIOCTL_
typedef enum _STORAGE_QUERY_TYPE {
PropertyStandardQuery = 0,
PropertyExistsQuery,
PropertyMaskQuery,
PropertyQueryMaxDefined
} STORAGE_QUERY_TYPE, *PSTORAGE_QUERY_TYPE;
typedef enum _STORAGE_PROPERTY_ID {
StorageDeviceProperty = 0,
StorageAdapterProperty,
StorageDeviceIdProperty,
StorageDeviceUniqueIdProperty,
StorageDeviceWriteCacheProperty,
StorageMiniportProperty,
StorageAccessAlignmentProperty,
/* Identify controller goes to adapter; Identify namespace to device */
StorageAdapterProtocolSpecificProperty = 49,
StorageDeviceProtocolSpecificProperty = 50
} STORAGE_PROPERTY_ID, *PSTORAGE_PROPERTY_ID;
typedef struct _STORAGE_PROPERTY_QUERY {
STORAGE_PROPERTY_ID PropertyId;
STORAGE_QUERY_TYPE QueryType;
UCHAR AdditionalParameters[1];
} STORAGE_PROPERTY_QUERY, *PSTORAGE_PROPERTY_QUERY;
typedef struct _STORAGE_PROTOCOL_DATA_DESCRIPTOR {
DWORD Version;
DWORD Size;
STORAGE_PROTOCOL_SPECIFIC_DATA ProtocolSpecificData;
} STORAGE_PROTOCOL_DATA_DESCRIPTOR, *PSTORAGE_PROTOCOL_DATA_DESCRIPTOR;
// Command completion status
// The "Phase Tag" field and "Status Field" are separated in spec. We define
// them in the same data structure to ease the memory access from software.
//
typedef union {
struct {
USHORT P : 1; // Phase Tag (P)
USHORT SC : 8; // Status Code (SC)
USHORT SCT : 3; // Status Code Type (SCT)
USHORT Reserved : 2;
USHORT M : 1; // More (M)
USHORT DNR : 1; // Do Not Retry (DNR)
} DUMMYSTRUCTNAME;
USHORT AsUshort;
} NVME_COMMAND_STATUS, *PNVME_COMMAND_STATUS;
// Information of log: NVME_LOG_PAGE_ERROR_INFO. Size: 64 bytes
//
typedef struct {
ULONGLONG ErrorCount;
USHORT SQID; // Submission Queue ID
USHORT CMDID; // Command ID
NVME_COMMAND_STATUS Status; // Status Field: This field indicates the
// Status Field for the command that
// completed. The Status Field is located in
// bits 15:01, bit 00 corresponds to the Phase
// Tag posted for the command.
struct {
USHORT Byte : 8; // Byte in command that contained error
USHORT Bit : 3; // Bit in command that contained error
USHORT Reserved : 5;
} ParameterErrorLocation;
ULONGLONG Lba; // LBA: This field indicates the first LBA
// that experienced the error condition, if
// applicable.
ULONG NameSpace; // Namespace: This field indicates the nsid
// that the error is associated with, if
// applicable.
UCHAR VendorInfoAvailable; // Vendor Specific Information Available
UCHAR Reserved0[3];
ULONGLONG CommandSpecificInfo; // This field contains command specific
// information. If used, the command
// definition specifies the information
// returned.
UCHAR Reserved1[24];
} NVME_ERROR_INFO_LOG, *PNVME_ERROR_INFO_LOG;
typedef struct {
ULONG DW0;
ULONG Reserved;
union {
struct {
USHORT SQHD; // SQ Head Pointer (SQHD)
USHORT SQID; // SQ Identifier (SQID)
} DUMMYSTRUCTNAME;
ULONG AsUlong;
} DW2;
union {
struct {
USHORT CID; // Command Identifier (CID)
NVME_COMMAND_STATUS Status;
} DUMMYSTRUCTNAME;
ULONG AsUlong;
} DW3;
} NVME_COMPLETION_ENTRY, *PNVME_COMPLETION_ENTRY;
// Bit-mask values for STORAGE_PROTOCOL_COMMAND - "Flags" field.
//
// Flag indicates the request targeting to adapter instead of device.
#define STORAGE_PROTOCOL_COMMAND_FLAG_ADAPTER_REQUEST 0x80000000
//
// Status values for STORAGE_PROTOCOL_COMMAND - "ReturnStatus" field.
//
#define STORAGE_PROTOCOL_STATUS_PENDING 0x0
#define STORAGE_PROTOCOL_STATUS_SUCCESS 0x1
#define STORAGE_PROTOCOL_STATUS_ERROR 0x2
#define STORAGE_PROTOCOL_STATUS_INVALID_REQUEST 0x3
#define STORAGE_PROTOCOL_STATUS_NO_DEVICE 0x4
#define STORAGE_PROTOCOL_STATUS_BUSY 0x5
#define STORAGE_PROTOCOL_STATUS_DATA_OVERRUN 0x6
#define STORAGE_PROTOCOL_STATUS_INSUFFICIENT_RESOURCES 0x7
#define STORAGE_PROTOCOL_STATUS_NOT_SUPPORTED 0xFF
// Command Length for Storage Protocols.
//
// NVMe commands are always 64 bytes.
#define STORAGE_PROTOCOL_COMMAND_LENGTH_NVME 0x40
// Command Specific Information for Storage Protocols - CommandSpecific field
//
#define STORAGE_PROTOCOL_SPECIFIC_NVME_ADMIN_COMMAND 0x01
#define STORAGE_PROTOCOL_SPECIFIC_NVME_NVM_COMMAND 0x02
#endif /* _DEVIOCTL_ */
// NVME_PASS_THROUGH
#ifndef STB_IO_CONTROL
typedef struct _SRB_IO_CONTROL {
ULONG HeaderLength;
UCHAR Signature[8];
ULONG Timeout;
ULONG ControlCode;
ULONG ReturnCode;
ULONG Length;
} SRB_IO_CONTROL, *PSRB_IO_CONTROL;
#endif
#ifndef NVME_PASS_THROUGH_SRB_IO_CODE
#define NVME_SIG_STR "NvmeMini"
#define NVME_STORPORT_DRIVER 0xe000
#define NVME_PASS_THROUGH_SRB_IO_CODE \
CTL_CODE(NVME_STORPORT_DRIVER, 0x0800, METHOD_BUFFERED, FILE_ANY_ACCESS)
#pragma pack(1)
/* Following is pre-Win10; used with DeviceIoControl(IOCTL_SCSI_MINIPORT),
* in Win10 need DeviceIoControl(IOCTL_STORAGE_PROTOCOL_COMMAND) for pure
* pass-through. Win10 also has "Protocol specific queries" for things like
* Identify and Get feature. */
typedef struct _NVME_PASS_THROUGH_IOCTL
{
SRB_IO_CONTROL SrbIoCtrl;
ULONG VendorSpecific[6];
ULONG NVMeCmd[16]; /* Command DW[0...15] */
ULONG CplEntry[4]; /* Completion DW[0...3] */
ULONG Direction; /* 0=None, 1=Out, 2=In, 3=I/O */
ULONG QueueId; /* 0=AdminQ */
ULONG DataBufferLen; /* sizeof(DataBuffer) if Data In */
ULONG MetaDataLen;
ULONG ReturnBufferLen; /* offsetof(DataBuffer), plus
* sizeof(DataBuffer) if Data Out */
UCHAR DataBuffer[1];
} NVME_PASS_THROUGH_IOCTL;
#pragma pack()
#endif // NVME_PASS_THROUGH_SRB_IO_CODE
/*
* method codes
*/
#define METHOD_BUFFERED 0
#define METHOD_IN_DIRECT 1
#define METHOD_OUT_DIRECT 2
#define METHOD_NEITHER 3
#define IOCTL_SCSI_BASE 0x00000004
/*
* constants for DataIn member of SCSI_PASS_THROUGH* structures
*/
#define SCSI_IOCTL_DATA_OUT 0
#define SCSI_IOCTL_DATA_IN 1
#define SCSI_IOCTL_DATA_UNSPECIFIED 2
#define IOCTL_SCSI_PASS_THROUGH CTL_CODE(IOCTL_SCSI_BASE, 0x0401, \
METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define IOCTL_SCSI_MINIPORT CTL_CODE(IOCTL_SCSI_BASE, 0x0402, \
METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define IOCTL_SCSI_GET_INQUIRY_DATA CTL_CODE(IOCTL_SCSI_BASE, 0x0403, \
METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_SCSI_GET_CAPABILITIES CTL_CODE(IOCTL_SCSI_BASE, 0x0404, \
METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_SCSI_PASS_THROUGH_DIRECT CTL_CODE(IOCTL_SCSI_BASE, 0x0405, \
METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define IOCTL_SCSI_GET_ADDRESS CTL_CODE(IOCTL_SCSI_BASE, 0x0406, \
METHOD_BUFFERED, FILE_ANY_ACCESS)
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,325 +0,0 @@
#ifndef SG_UNALIGNED_H
#define SG_UNALIGNED_H
/*
* Copyright (c) 2014-2017 Douglas Gilbert.
* All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the BSD_LICENSE file.
*/
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Borrowed from the Linux kernel, via mhvtl */
/* In the first section below, functions that copy unsigned integers in a
* computer's native format, to and from an unaligned big endian sequence of
* bytes. Big endian byte format "on the wire" is the default used by SCSI
* standards (www.t10.org). Big endian is also the network byte order. */
static inline uint16_t __get_unaligned_be16(const uint8_t *p)
{
return p[0] << 8 | p[1];
}
static inline uint32_t __get_unaligned_be32(const uint8_t *p)
{
return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
}
/* Assume 48 bit value placed in uint64_t */
static inline uint64_t __get_unaligned_be48(const uint8_t *p)
{
return (uint64_t)__get_unaligned_be16(p) << 32 |
__get_unaligned_be32(p + 2);
}
static inline uint64_t __get_unaligned_be64(const uint8_t *p)
{
return (uint64_t)__get_unaligned_be32(p) << 32 |
__get_unaligned_be32(p + 4);
}
static inline void __put_unaligned_be16(uint16_t val, uint8_t *p)
{
*p++ = val >> 8;
*p++ = val;
}
static inline void __put_unaligned_be32(uint32_t val, uint8_t *p)
{
__put_unaligned_be16(val >> 16, p);
__put_unaligned_be16(val, p + 2);
}
/* Assume 48 bit value placed in uint64_t */
static inline void __put_unaligned_be48(uint64_t val, uint8_t *p)
{
__put_unaligned_be16(val >> 32, p);
__put_unaligned_be32(val, p + 2);
}
static inline void __put_unaligned_be64(uint64_t val, uint8_t *p)
{
__put_unaligned_be32(val >> 32, p);
__put_unaligned_be32(val, p + 4);
}
static inline uint16_t sg_get_unaligned_be16(const void *p)
{
return __get_unaligned_be16((const uint8_t *)p);
}
static inline uint32_t sg_get_unaligned_be24(const void *p)
{
return ((const uint8_t *)p)[0] << 16 | ((const uint8_t *)p)[1] << 8 |
((const uint8_t *)p)[2];
}
static inline uint32_t sg_get_unaligned_be32(const void *p)
{
return __get_unaligned_be32((const uint8_t *)p);
}
/* Assume 48 bit value placed in uint64_t */
static inline uint64_t sg_get_unaligned_be48(const void *p)
{
return __get_unaligned_be48((const uint8_t *)p);
}
static inline uint64_t sg_get_unaligned_be64(const void *p)
{
return __get_unaligned_be64((const uint8_t *)p);
}
/* Returns 0 if 'num_bytes' is less than or equal to 0 or greater than
* 8 (i.e. sizeof(uint64_t)). Else returns result in uint64_t which is
* an 8 byte unsigned integer. */
static inline uint64_t sg_get_unaligned_be(int num_bytes, const void *p)
{
if ((num_bytes <= 0) || (num_bytes > (int)sizeof(uint64_t)))
return 0;
else {
const uint8_t * xp = (const uint8_t *)p;
uint64_t res = *xp;
for (++xp; num_bytes > 1; ++xp, --num_bytes)
res = (res << 8) | *xp;
return res;
}
}
static inline void sg_put_unaligned_be16(uint16_t val, void *p)
{
__put_unaligned_be16(val, (uint8_t *)p);
}
static inline void sg_put_unaligned_be24(uint32_t val, void *p)
{
((uint8_t *)p)[0] = (val >> 16) & 0xff;
((uint8_t *)p)[1] = (val >> 8) & 0xff;
((uint8_t *)p)[2] = val & 0xff;
}
static inline void sg_put_unaligned_be32(uint32_t val, void *p)
{
__put_unaligned_be32(val, (uint8_t *)p);
}
/* Assume 48 bit value placed in uint64_t */
static inline void sg_put_unaligned_be48(uint64_t val, void *p)
{
__put_unaligned_be48(val, (uint8_t *)p);
}
static inline void sg_put_unaligned_be64(uint64_t val, void *p)
{
__put_unaligned_be64(val, (uint8_t *)p);
}
/* Since cdb and parameter blocks are often memset to zero before these
* unaligned function partially fill them, then check for a val of zero
* and ignore if it is with these variants. */
static inline void sg_nz_put_unaligned_be16(uint16_t val, void *p)
{
if (val)
__put_unaligned_be16(val, (uint8_t *)p);
}
static inline void sg_nz_put_unaligned_be24(uint32_t val, void *p)
{
if (val) {
((uint8_t *)p)[0] = (val >> 16) & 0xff;
((uint8_t *)p)[1] = (val >> 8) & 0xff;
((uint8_t *)p)[2] = val & 0xff;
}
}
static inline void sg_nz_put_unaligned_be32(uint32_t val, void *p)
{
if (val)
__put_unaligned_be32(val, (uint8_t *)p);
}
static inline void sg_nz_put_unaligned_be64(uint64_t val, void *p)
{
if (val)
__put_unaligned_be64(val, (uint8_t *)p);
}
/* Below are the little endian equivalents of the big endian functions
* above. Little endian is used by ATA, PCI and NVMe.
*/
static inline uint16_t __get_unaligned_le16(const uint8_t *p)
{
return p[1] << 8 | p[0];
}
static inline uint32_t __get_unaligned_le32(const uint8_t *p)
{
return p[3] << 24 | p[2] << 16 | p[1] << 8 | p[0];
}
static inline uint64_t __get_unaligned_le64(const uint8_t *p)
{
return (uint64_t)__get_unaligned_le32(p + 4) << 32 |
__get_unaligned_le32(p);
}
static inline void __put_unaligned_le16(uint16_t val, uint8_t *p)
{
*p++ = val;
*p++ = val >> 8;
}
static inline void __put_unaligned_le32(uint32_t val, uint8_t *p)
{
__put_unaligned_le16(val >> 16, p + 2);
__put_unaligned_le16(val, p);
}
static inline void __put_unaligned_le64(uint64_t val, uint8_t *p)
{
__put_unaligned_le32(val >> 32, p + 4);
__put_unaligned_le32(val, p);
}
static inline uint16_t sg_get_unaligned_le16(const void *p)
{
return __get_unaligned_le16((const uint8_t *)p);
}
static inline uint32_t sg_get_unaligned_le24(const void *p)
{
return (uint32_t)__get_unaligned_le16((const uint8_t *)p) |
((const uint8_t *)p)[2] << 16;
}
static inline uint32_t sg_get_unaligned_le32(const void *p)
{
return __get_unaligned_le32((const uint8_t *)p);
}
/* Assume 48 bit value placed in uint64_t */
static inline uint64_t sg_get_unaligned_le48(const void *p)
{
return (uint64_t)__get_unaligned_le16((const uint8_t *)p + 4) << 32 |
__get_unaligned_le32((const uint8_t *)p);
}
static inline uint64_t sg_get_unaligned_le64(const void *p)
{
return __get_unaligned_le64((const uint8_t *)p);
}
/* Returns 0 if 'num_bytes' is less than or equal to 0 or greater than
* 8 (i.e. sizeof(uint64_t)). Else returns result in uint64_t which is
* an 8 byte unsigned integer. */
static inline uint64_t sg_get_unaligned_le(int num_bytes, const void *p)
{
if ((num_bytes <= 0) || (num_bytes > (int)sizeof(uint64_t)))
return 0;
else {
const uint8_t * xp = (const uint8_t *)p + (num_bytes - 1);
uint64_t res = *xp;
for (--xp; num_bytes > 1; --xp, --num_bytes)
res = (res << 8) | *xp;
return res;
}
}
static inline void sg_put_unaligned_le16(uint16_t val, void *p)
{
__put_unaligned_le16(val, (uint8_t *)p);
}
static inline void sg_put_unaligned_le24(uint32_t val, void *p)
{
((uint8_t *)p)[2] = (val >> 16) & 0xff;
((uint8_t *)p)[1] = (val >> 8) & 0xff;
((uint8_t *)p)[0] = val & 0xff;
}
static inline void sg_put_unaligned_le32(uint32_t val, void *p)
{
__put_unaligned_le32(val, (uint8_t *)p);
}
/* Assume 48 bit value placed in uint64_t */
static inline void sg_put_unaligned_le48(uint64_t val, void *p)
{
((uint8_t *)p)[5] = (val >> 40) & 0xff;
((uint8_t *)p)[4] = (val >> 32) & 0xff;
((uint8_t *)p)[3] = (val >> 24) & 0xff;
((uint8_t *)p)[2] = (val >> 16) & 0xff;
((uint8_t *)p)[1] = (val >> 8) & 0xff;
((uint8_t *)p)[0] = val & 0xff;
}
static inline void sg_put_unaligned_le64(uint64_t val, void *p)
{
__put_unaligned_le64(val, (uint8_t *)p);
}
/* Since cdb and parameter blocks are often memset to zero before these
* unaligned function partially fill them, then check for a val of zero
* and ignore if it is with these variants. */
static inline void sg_nz_put_unaligned_le16(uint16_t val, void *p)
{
if (val)
__put_unaligned_le16(val, (uint8_t *)p);
}
static inline void sg_nz_put_unaligned_le24(uint32_t val, void *p)
{
if (val) {
((uint8_t *)p)[2] = (val >> 16) & 0xff;
((uint8_t *)p)[1] = (val >> 8) & 0xff;
((uint8_t *)p)[0] = val & 0xff;
}
}
static inline void sg_nz_put_unaligned_le32(uint32_t val, void *p)
{
if (val)
__put_unaligned_le32(val, (uint8_t *)p);
}
static inline void sg_nz_put_unaligned_le64(uint64_t val, void *p)
{
if (val)
__put_unaligned_le64(val, (uint8_t *)p);
}
#ifdef __cplusplus
}
#endif
#endif /* SG_UNALIGNED_H */

View File

@ -1,663 +0,0 @@
/*
* Copyright (c) 1999-2018 Douglas Gilbert.
* All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the BSD_LICENSE file.
*/
/*
* CONTENTS
* Some SCSI commands are executed in many contexts and hence began
* to appear in several sg3_utils utilities. This files centralizes
* some of the low level command execution code. In most cases the
* interpretation of the command response is left to the each
* utility.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "sg_lib.h"
#include "sg_cmds_basic.h"
#include "sg_pt.h"
#include "sg_unaligned.h"
/* Needs to be after config.h */
#ifdef SG_LIB_LINUX
#include <errno.h>
#endif
static const char * const version_str = "1.83 20180204";
#define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */
#define EBUFF_SZ 256
#define DEF_PT_TIMEOUT 60 /* 60 seconds */
#define START_PT_TIMEOUT 120 /* 120 seconds == 2 minutes */
#define LONG_PT_TIMEOUT 7200 /* 7,200 seconds == 120 minutes */
#define INQUIRY_CMD 0x12
#define INQUIRY_CMDLEN 6
#define REQUEST_SENSE_CMD 0x3
#define REQUEST_SENSE_CMDLEN 6
#define REPORT_LUNS_CMD 0xa0
#define REPORT_LUNS_CMDLEN 12
#define TUR_CMD 0x0
#define TUR_CMDLEN 6
#define SAFE_STD_INQ_RESP_LEN 36 /* other lengths lock up some devices */
const char *
sg_cmds_version()
{
return version_str;
}
#if defined(__GNUC__) || defined(__clang__)
static int pr2ws(const char * fmt, ...)
__attribute__ ((format (printf, 1, 2)));
#else
static int pr2ws(const char * fmt, ...);
#endif
static int
pr2ws(const char * fmt, ...)
{
va_list args;
int n;
va_start(args, fmt);
n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args);
va_end(args);
return n;
}
/* Returns file descriptor >= 0 if successful. If error in Unix returns
negated errno. */
int
sg_cmds_open_device(const char * device_name, bool read_only, int verbose)
{
/* The following 2 lines are temporary. It is to avoid a NULL pointer
* crash when an old utility is used with a newer library built after
* the sg_warnings_strm cleanup */
if (NULL == sg_warnings_strm)
sg_warnings_strm = stderr;
return scsi_pt_open_device(device_name, read_only, verbose);
}
/* Returns file descriptor >= 0 if successful. If error in Unix returns
negated errno. */
int
sg_cmds_open_flags(const char * device_name, int flags, int verbose)
{
return scsi_pt_open_flags(device_name, flags, verbose);
}
/* Returns 0 if successful. If error in Unix returns negated errno. */
int
sg_cmds_close_device(int device_fd)
{
return scsi_pt_close_device(device_fd);
}
static const char * const pass_through_s = "pass-through";
static int
sg_cmds_process_helper(const char * leadin, int mx_di_len, int resid,
const unsigned char * sbp, int slen, bool noisy,
int verbose, int * o_sense_cat)
{
int scat, got;
bool n = false;
bool check_data_in = false;
char b[512];
scat = sg_err_category_sense(sbp, slen);
switch (scat) {
case SG_LIB_CAT_NOT_READY:
case SG_LIB_CAT_INVALID_OP:
case SG_LIB_CAT_ILLEGAL_REQ:
case SG_LIB_CAT_ABORTED_COMMAND:
case SG_LIB_CAT_COPY_ABORTED:
case SG_LIB_CAT_DATA_PROTECT:
case SG_LIB_CAT_PROTECTION:
case SG_LIB_CAT_NO_SENSE:
case SG_LIB_CAT_MISCOMPARE:
n = false;
break;
case SG_LIB_CAT_RECOVERED:
case SG_LIB_CAT_MEDIUM_HARD:
check_data_in = true;
#if defined(__GNUC__)
#if (__GNUC__ >= 7)
__attribute__((fallthrough));
/* FALL THROUGH */
#endif
#endif
case SG_LIB_CAT_UNIT_ATTENTION:
case SG_LIB_CAT_SENSE:
default:
n = noisy;
break;
}
if (verbose || n) {
if (leadin && (strlen(leadin) > 0))
pr2ws("%s:\n", leadin);
sg_get_sense_str(NULL, sbp, slen, (verbose > 1),
sizeof(b), b);
pr2ws("%s", b);
if ((mx_di_len > 0) && (resid > 0)) {
got = mx_di_len - resid;
if ((verbose > 2) || check_data_in || (got > 0))
pr2ws(" %s requested %d bytes (data-in) but got %d "
"bytes\n", pass_through_s, mx_di_len, got);
}
}
if (o_sense_cat)
*o_sense_cat = scat;
return -2;
}
/* This is a helper function used by sg_cmds_* implementations after the
* call to the pass-through. pt_res is returned from do_scsi_pt(). If valid
* sense data is found it is decoded and output to sg_warnings_strm (def:
* stderr); depending on the 'noisy' and 'verbose' settings. Returns -2 for
* "sense" category (may not be fatal), -1 for failed, 0, or a positive
* number. If 'mx_di_len > 0' then asks pass-through for resid and returns
* (mx_di_len - resid); otherwise returns 0. So for data-in it should return
* the actual number of bytes received. For data-out (to device) or no data
* call with 'mx_di_len' set to 0 or less. If -2 returned then sense category
* output via 'o_sense_cat' pointer (if not NULL). Note that several sense
* categories also have data in bytes received; -2 is still returned. */
int
sg_cmds_process_resp(struct sg_pt_base * ptvp, const char * leadin,
int pt_res, int mx_di_len, const unsigned char * sbp,
bool noisy, int verbose, int * o_sense_cat)
{
int got, cat, duration, slen, resid, resp_code, sstat;
bool transport_sense;
char b[1024];
if (NULL == leadin)
leadin = "";
if (pt_res < 0) {
#ifdef SG_LIB_LINUX
if (verbose)
pr2ws("%s: %s os error: %s\n", leadin, pass_through_s,
safe_strerror(-pt_res));
if ((-ENXIO == pt_res) && o_sense_cat) {
if (verbose > 2)
pr2ws("map ENXIO to SG_LIB_CAT_NOT_READY\n");
*o_sense_cat = SG_LIB_CAT_NOT_READY;
return -2;
} else if (noisy && (0 == verbose))
pr2ws("%s: %s os error: %s\n", leadin, pass_through_s,
safe_strerror(-pt_res));
#else
if (noisy || verbose)
pr2ws("%s: %s os error: %s\n", leadin, pass_through_s,
safe_strerror(-pt_res));
#endif
return -1;
} else if (SCSI_PT_DO_BAD_PARAMS == pt_res) {
pr2ws("%s: bad %s setup\n", leadin, pass_through_s);
return -1;
} else if (SCSI_PT_DO_TIMEOUT == pt_res) {
pr2ws("%s: %s timeout\n", leadin, pass_through_s);
return -1;
}
if ((verbose > 2) && ((duration = get_scsi_pt_duration_ms(ptvp)) >= 0))
pr2ws(" duration=%d ms\n", duration);
resid = (mx_di_len > 0) ? get_scsi_pt_resid(ptvp) : 0;
slen = get_scsi_pt_sense_len(ptvp);
switch ((cat = get_scsi_pt_result_category(ptvp))) {
case SCSI_PT_RESULT_GOOD:
if (sbp && (slen > 7)) {
resp_code = sbp[0] & 0x7f;
/* SBC referrals can have status=GOOD and sense_key=COMPLETED */
if (resp_code >= 0x70) {
if (resp_code < 0x72) {
if (SPC_SK_NO_SENSE != (0xf & sbp[2]))
sg_err_category_sense(sbp, slen);
} else if (resp_code < 0x74) {
if (SPC_SK_NO_SENSE != (0xf & sbp[1]))
sg_err_category_sense(sbp, slen);
}
}
}
if (mx_di_len > 0) {
got = mx_di_len - resid;
if ((verbose > 1) && (resid != 0))
pr2ws(" %s: %s requested %d bytes (data-in) but got %d "
"bytes\n", leadin, pass_through_s, mx_di_len, got);
if (got >= 0)
return got;
else {
if (verbose)
pr2ws(" %s: %s can't get negative bytes, say it got "
"none\n", leadin, pass_through_s);
return 0;
}
} else
return 0;
case SCSI_PT_RESULT_STATUS: /* other than GOOD and CHECK CONDITION */
sstat = get_scsi_pt_status_response(ptvp);
if (o_sense_cat) {
switch (sstat) {
case SAM_STAT_RESERVATION_CONFLICT:
*o_sense_cat = SG_LIB_CAT_RES_CONFLICT;
return -2;
case SAM_STAT_CONDITION_MET:
*o_sense_cat = SG_LIB_CAT_CONDITION_MET;
return -2;
case SAM_STAT_BUSY:
*o_sense_cat = SG_LIB_CAT_BUSY;
return -2;
case SAM_STAT_TASK_SET_FULL:
*o_sense_cat = SG_LIB_CAT_TS_FULL;
return -2;
case SAM_STAT_ACA_ACTIVE:
*o_sense_cat = SG_LIB_CAT_ACA_ACTIVE;
return -2;
case SAM_STAT_TASK_ABORTED:
*o_sense_cat = SG_LIB_CAT_TASK_ABORTED;
return -2;
default:
break;
}
}
if (verbose || noisy) {
sg_get_scsi_status_str(sstat, sizeof(b), b);
pr2ws("%s: scsi status: %s\n", leadin, b);
}
return -1;
case SCSI_PT_RESULT_SENSE:
return sg_cmds_process_helper(leadin, mx_di_len, resid, sbp, slen,
noisy, verbose, o_sense_cat);
case SCSI_PT_RESULT_TRANSPORT_ERR:
if (verbose || noisy) {
get_scsi_pt_transport_err_str(ptvp, sizeof(b), b);
pr2ws("%s: transport: %s\n", leadin, b);
}
#ifdef SG_LIB_LINUX
transport_sense = (slen > 0);
#else
transport_sense = ((SAM_STAT_CHECK_CONDITION ==
get_scsi_pt_status_response(ptvp)) && (slen > 0));
#endif
if (transport_sense)
return sg_cmds_process_helper(leadin, mx_di_len, resid, sbp,
slen, noisy, verbose, o_sense_cat);
else
return -1;
case SCSI_PT_RESULT_OS_ERR:
if (verbose || noisy) {
get_scsi_pt_os_err_str(ptvp, sizeof(b), b);
pr2ws("%s: os: %s\n", leadin, b);
}
return -1;
default:
pr2ws("%s: unknown %s result category (%d)\n", leadin, pass_through_s,
cat);
return -1;
}
}
bool
sg_cmds_is_nvme(const struct sg_pt_base * ptvp)
{
return pt_device_is_nvme(ptvp);
}
static struct sg_pt_base *
create_pt_obj(const char * cname)
{
struct sg_pt_base * ptvp = construct_scsi_pt_obj();
if (NULL == ptvp)
pr2ws("%s: out of memory\n", cname);
return ptvp;
}
static const char * const inquiry_s = "inquiry";
static int
sg_ll_inquiry_com(int sg_fd, bool cmddt, bool evpd, int pg_op, void * resp,
int mx_resp_len, int timeout_secs, int * residp,
bool noisy, int verbose)
{
int res, ret, k, sense_cat, resid;
unsigned char inq_cdb[INQUIRY_CMDLEN] = {INQUIRY_CMD, 0, 0, 0, 0, 0};
unsigned char sense_b[SENSE_BUFF_LEN];
unsigned char * up;
struct sg_pt_base * ptvp;
if (cmddt)
inq_cdb[1] |= 0x2;
if (evpd)
inq_cdb[1] |= 0x1;
inq_cdb[2] = (unsigned char)pg_op;
/* 16 bit allocation length (was 8, increased in spc3r09, 200209) */
sg_put_unaligned_be16((uint16_t)mx_resp_len, inq_cdb + 3);
if (verbose) {
pr2ws(" %s cdb: ", inquiry_s);
for (k = 0; k < INQUIRY_CMDLEN; ++k)
pr2ws("%02x ", inq_cdb[k]);
pr2ws("\n");
}
if (resp && (mx_resp_len > 0)) {
up = (unsigned char *)resp;
up[0] = 0x7f; /* defensive prefill */
if (mx_resp_len > 4)
up[4] = 0;
}
if (timeout_secs <= 0)
timeout_secs = DEF_PT_TIMEOUT;
ptvp = construct_scsi_pt_obj();
if (NULL == ptvp) {
pr2ws("%s: out of memory\n", __func__);
if (residp)
*residp = 0;
return -1;
}
set_scsi_pt_cdb(ptvp, inq_cdb, sizeof(inq_cdb));
set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
res = do_scsi_pt(ptvp, sg_fd, timeout_secs, verbose);
ret = sg_cmds_process_resp(ptvp, inquiry_s, res, mx_resp_len, sense_b,
noisy, verbose, &sense_cat);
resid = get_scsi_pt_resid(ptvp);
if (residp)
*residp = resid;
if (-1 == ret)
ret = sg_convert_errno(get_scsi_pt_os_err(ptvp));
else if (-2 == ret) {
switch (sense_cat) {
case SG_LIB_CAT_RECOVERED:
case SG_LIB_CAT_NO_SENSE:
ret = 0;
break;
default:
ret = sense_cat;
break;
}
} else if (ret < 4) {
if (verbose)
pr2ws("%s: got too few bytes (%d)\n", __func__, ret);
ret = SG_LIB_CAT_MALFORMED;
} else
ret = 0;
destruct_scsi_pt_obj(ptvp);
if (resid > 0) {
if (resid > mx_resp_len) {
pr2ws("%s resid (%d) should never exceed requested "
"len=%d\n", inquiry_s, resid, mx_resp_len);
return ret ? ret : SG_LIB_CAT_MALFORMED;
}
/* zero unfilled section of response buffer, based on resid */
memset((unsigned char *)resp + (mx_resp_len - resid), 0, resid);
}
return ret;
}
/* Invokes a SCSI INQUIRY command and yields the response. Returns 0 when
* successful, various SG_LIB_CAT_* positive values or -1 -> other errors.
* The CMDDT field is obsolete in the INQUIRY cdb. */
int
sg_ll_inquiry(int sg_fd, bool cmddt, bool evpd, int pg_op, void * resp,
int mx_resp_len, bool noisy, int verbose)
{
return sg_ll_inquiry_com(sg_fd, cmddt, evpd, pg_op, resp, mx_resp_len,
0 /* timeout_sec */, NULL, noisy, verbose);
}
/* Yields most of first 36 bytes of a standard INQUIRY (evpd==0) response.
* Returns 0 when successful, various SG_LIB_CAT_* positive values or
* -1 -> other errors */
int
sg_simple_inquiry(int sg_fd, struct sg_simple_inquiry_resp * inq_data,
bool noisy, int verbose)
{
int ret;
unsigned char inq_resp[SAFE_STD_INQ_RESP_LEN];
if (inq_data) {
memset(inq_data, 0, sizeof(* inq_data));
inq_data->peripheral_qualifier = 0x3;
inq_data->peripheral_type = 0x1f;
}
ret = sg_ll_inquiry_com(sg_fd, false, false, 0, inq_resp,
sizeof(inq_resp), 0, NULL, noisy, verbose);
if (inq_data && (0 == ret)) {
inq_data->peripheral_qualifier = (inq_resp[0] >> 5) & 0x7;
inq_data->peripheral_type = inq_resp[0] & 0x1f;
inq_data->byte_1 = inq_resp[1];
inq_data->version = inq_resp[2];
inq_data->byte_3 = inq_resp[3];
inq_data->byte_5 = inq_resp[5];
inq_data->byte_6 = inq_resp[6];
inq_data->byte_7 = inq_resp[7];
memcpy(inq_data->vendor, inq_resp + 8, 8);
memcpy(inq_data->product, inq_resp + 16, 16);
memcpy(inq_data->revision, inq_resp + 32, 4);
}
return ret;
}
/* Invokes a SCSI INQUIRY command and yields the response. Returns 0 when
* successful, various SG_LIB_CAT_* positive values or -1 -> other errors.
* The CMDDT field is obsolete in the INQUIRY cdb (since spc3r16 in 2003) so
* an argument to set it has been removed (use the REPORT SUPPORTED OPERATION
* CODES command instead). Adds the ability to set the command abort timeout
* and the ability to report the residual count. If timeout_secs is zero
* or less the default command abort timeout (60 seconds) is used.
* If residp is non-NULL then the residual value is written where residp
* points. A residual value of 0 implies mx_resp_len bytes have be written
* where resp points. If the residual value equals mx_resp_len then no
* bytes have been written. */
int
sg_ll_inquiry_v2(int sg_fd, bool evpd, int pg_op, void * resp,
int mx_resp_len, int timeout_secs, int * residp,
bool noisy, int verbose)
{
return sg_ll_inquiry_com(sg_fd, false, evpd, pg_op, resp, mx_resp_len,
timeout_secs, residp, noisy, verbose);
}
/* Invokes a SCSI TEST UNIT READY command.
* 'pack_id' is just for diagnostics, safe to set to 0.
* Looks for progress indicator if 'progress' non-NULL;
* if found writes value [0..65535] else write -1.
* Returns 0 when successful, various SG_LIB_CAT_* positive values or
* -1 -> other errors */
int
sg_ll_test_unit_ready_progress(int sg_fd, int pack_id, int * progress,
bool noisy, int verbose)
{
static const char * const tur_s = "test unit ready";
int res, ret, k, sense_cat;
unsigned char tur_cdb[TUR_CMDLEN] = {TUR_CMD, 0, 0, 0, 0, 0};
unsigned char sense_b[SENSE_BUFF_LEN];
struct sg_pt_base * ptvp;
if (verbose) {
pr2ws(" %s cdb: ", tur_s);
for (k = 0; k < TUR_CMDLEN; ++k)
pr2ws("%02x ", tur_cdb[k]);
pr2ws("\n");
}
if (NULL == ((ptvp = create_pt_obj(tur_s))))
return -1;
set_scsi_pt_cdb(ptvp, tur_cdb, sizeof(tur_cdb));
set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
set_scsi_pt_packet_id(ptvp, pack_id);
res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
ret = sg_cmds_process_resp(ptvp, tur_s, res, SG_NO_DATA_IN, sense_b,
noisy, verbose, &sense_cat);
if (-1 == ret) {
int os_err = get_scsi_pt_os_err(ptvp);
if ((os_err > 0) && (os_err < 47))
ret = SG_LIB_OS_BASE_ERR + os_err;
} else if (-2 == ret) {
if (progress) {
int slen = get_scsi_pt_sense_len(ptvp);
if (! sg_get_sense_progress_fld(sense_b, slen, progress))
*progress = -1;
}
switch (sense_cat) {
case SG_LIB_CAT_RECOVERED:
case SG_LIB_CAT_NO_SENSE:
ret = 0;
break;
default:
ret = sense_cat;
break;
}
} else
ret = 0;
destruct_scsi_pt_obj(ptvp);
return ret;
}
/* Invokes a SCSI TEST UNIT READY command.
* 'pack_id' is just for diagnostics, safe to set to 0.
* Returns 0 when successful, various SG_LIB_CAT_* positive values or
* -1 -> other errors */
int
sg_ll_test_unit_ready(int sg_fd, int pack_id, bool noisy, int verbose)
{
return sg_ll_test_unit_ready_progress(sg_fd, pack_id, NULL, noisy,
verbose);
}
/* Invokes a SCSI REQUEST SENSE command. Returns 0 when successful, various
* SG_LIB_CAT_* positive values or -1 -> other errors */
int
sg_ll_request_sense(int sg_fd, bool desc, void * resp, int mx_resp_len,
bool noisy, int verbose)
{
static const char * const rq_s = "request sense";
int k, ret, res, sense_cat;
unsigned char rs_cdb[REQUEST_SENSE_CMDLEN] =
{REQUEST_SENSE_CMD, 0, 0, 0, 0, 0};
unsigned char sense_b[SENSE_BUFF_LEN];
struct sg_pt_base * ptvp;
if (desc)
rs_cdb[1] |= 0x1;
if (mx_resp_len > 0xff) {
pr2ws("mx_resp_len cannot exceed 255\n");
return -1;
}
rs_cdb[4] = mx_resp_len & 0xff;
if (verbose) {
pr2ws(" %s cmd: ", rq_s);
for (k = 0; k < REQUEST_SENSE_CMDLEN; ++k)
pr2ws("%02x ", rs_cdb[k]);
pr2ws("\n");
}
if (NULL == ((ptvp = create_pt_obj(rq_s))))
return -1;
set_scsi_pt_cdb(ptvp, rs_cdb, sizeof(rs_cdb));
set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
ret = sg_cmds_process_resp(ptvp, rq_s, res, mx_resp_len, sense_b, noisy,
verbose, &sense_cat);
if (-1 == ret) {
int os_err = get_scsi_pt_os_err(ptvp);
if ((os_err > 0) && (os_err < 47))
ret = SG_LIB_OS_BASE_ERR + os_err;
} else if (-2 == ret) {
switch (sense_cat) {
case SG_LIB_CAT_RECOVERED:
case SG_LIB_CAT_NO_SENSE:
ret = 0;
break;
default:
ret = sense_cat;
break;
}
} else {
if ((mx_resp_len >= 8) && (ret < 8)) {
if (verbose)
pr2ws(" %s: got %d bytes in response, too short\n", rq_s,
ret);
ret = -1;
} else
ret = 0;
}
destruct_scsi_pt_obj(ptvp);
return ret;
}
/* Invokes a SCSI REPORT LUNS command. Return of 0 -> success,
* various SG_LIB_CAT_* positive values or -1 -> other errors */
int
sg_ll_report_luns(int sg_fd, int select_report, void * resp, int mx_resp_len,
bool noisy, int verbose)
{
static const char * const report_luns_s = "report luns";
int k, ret, res, sense_cat;
unsigned char rl_cdb[REPORT_LUNS_CMDLEN] =
{REPORT_LUNS_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
unsigned char sense_b[SENSE_BUFF_LEN];
struct sg_pt_base * ptvp;
rl_cdb[2] = select_report & 0xff;
sg_put_unaligned_be32((uint32_t)mx_resp_len, rl_cdb + 6);
if (verbose) {
pr2ws(" %s cdb: ", report_luns_s);
for (k = 0; k < REPORT_LUNS_CMDLEN; ++k)
pr2ws("%02x ", rl_cdb[k]);
pr2ws("\n");
}
if (NULL == ((ptvp = create_pt_obj(report_luns_s))))
return -1;
set_scsi_pt_cdb(ptvp, rl_cdb, sizeof(rl_cdb));
set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
ret = sg_cmds_process_resp(ptvp, report_luns_s, res, mx_resp_len,
sense_b, noisy, verbose, &sense_cat);
if (-1 == ret) {
int os_err = get_scsi_pt_os_err(ptvp);
if ((os_err > 0) && (os_err < 47))
ret = SG_LIB_OS_BASE_ERR + os_err;
} else if (-2 == ret) {
switch (sense_cat) {
case SG_LIB_CAT_RECOVERED:
case SG_LIB_CAT_NO_SENSE:
ret = 0;
break;
default:
ret = sense_cat;
break;
}
} else
ret = 0;
destruct_scsi_pt_obj(ptvp);
return ret;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,382 +0,0 @@
/*
* Copyright (c) 2008-2018 Douglas Gilbert.
* All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the BSD_LICENSE file.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#define __STDC_FORMAT_MACROS 1
#include <inttypes.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "sg_lib.h"
#include "sg_cmds_basic.h"
#include "sg_cmds_mmc.h"
#include "sg_pt.h"
#include "sg_unaligned.h"
#define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */
#define DEF_PT_TIMEOUT 60 /* 60 seconds */
#define GET_CONFIG_CMD 0x46
#define GET_CONFIG_CMD_LEN 10
#define GET_PERFORMANCE_CMD 0xac
#define GET_PERFORMANCE_CMD_LEN 12
#define SET_CD_SPEED_CMD 0xbb
#define SET_CD_SPEED_CMDLEN 12
#define SET_STREAMING_CMD 0xb6
#define SET_STREAMING_CMDLEN 12
#if defined(__GNUC__) || defined(__clang__)
static int pr2ws(const char * fmt, ...)
__attribute__ ((format (printf, 1, 2)));
#else
static int pr2ws(const char * fmt, ...);
#endif
static int
pr2ws(const char * fmt, ...)
{
va_list args;
int n;
va_start(args, fmt);
n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args);
va_end(args);
return n;
}
static struct sg_pt_base *
create_pt_obj(const char * cname)
{
struct sg_pt_base * ptvp = construct_scsi_pt_obj();
if (NULL == ptvp)
pr2ws("%s: out of memory\n", cname);
return ptvp;
}
/* Invokes a SCSI SET CD SPEED command (MMC).
* Return of 0 -> success, SG_LIB_CAT_INVALID_OP -> command not supported,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
* SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
* -1 -> other failure */
int
sg_ll_set_cd_speed(int sg_fd, int rot_control, int drv_read_speed,
int drv_write_speed, bool noisy, int verbose)
{
static const char * const cdb_name_s = "set cd speed";
int res, ret, k, sense_cat;
unsigned char scsCmdBlk[SET_CD_SPEED_CMDLEN] = {SET_CD_SPEED_CMD, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0 ,0};
unsigned char sense_b[SENSE_BUFF_LEN];
struct sg_pt_base * ptvp;
scsCmdBlk[1] |= (rot_control & 0x3);
sg_put_unaligned_be16((uint16_t)drv_read_speed, scsCmdBlk + 2);
sg_put_unaligned_be16((uint16_t)drv_write_speed, scsCmdBlk + 4);
if (verbose) {
pr2ws(" %s cdb: ", cdb_name_s);
for (k = 0; k < SET_CD_SPEED_CMDLEN; ++k)
pr2ws("%02x ", scsCmdBlk[k]);
pr2ws("\n");
}
if (NULL == ((ptvp = create_pt_obj(cdb_name_s))))
return -1;
set_scsi_pt_cdb(ptvp, scsCmdBlk, sizeof(scsCmdBlk));
set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, SG_NO_DATA_IN, sense_b,
noisy, verbose, &sense_cat);
if (-1 == ret) {
int os_err = get_scsi_pt_os_err(ptvp);
if ((os_err > 0) && (os_err < 47))
ret = SG_LIB_OS_BASE_ERR + os_err;
} else if (-2 == ret) {
switch (sense_cat) {
case SG_LIB_CAT_NOT_READY:
case SG_LIB_CAT_UNIT_ATTENTION:
case SG_LIB_CAT_INVALID_OP:
case SG_LIB_CAT_ILLEGAL_REQ:
case SG_LIB_CAT_ABORTED_COMMAND:
ret = sense_cat;
break;
case SG_LIB_CAT_RECOVERED:
case SG_LIB_CAT_NO_SENSE:
ret = 0;
break;
default:
ret = -1;
break;
}
} else
ret = 0;
destruct_scsi_pt_obj(ptvp);
return ret;
}
/* Invokes a SCSI GET CONFIGURATION command (MMC-3,4,5).
* Returns 0 when successful, SG_LIB_CAT_INVALID_OP if command not
* supported, SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported,
* SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, else -1 */
int
sg_ll_get_config(int sg_fd, int rt, int starting, void * resp,
int mx_resp_len, bool noisy, int verbose)
{
static const char * const cdb_name_s = "get configuration";
int res, k, ret, sense_cat;
unsigned char gcCmdBlk[GET_CONFIG_CMD_LEN] = {GET_CONFIG_CMD, 0, 0, 0,
0, 0, 0, 0, 0, 0};
unsigned char sense_b[SENSE_BUFF_LEN];
struct sg_pt_base * ptvp;
if ((rt < 0) || (rt > 3)) {
pr2ws("Bad rt value: %d\n", rt);
return -1;
}
gcCmdBlk[1] = (rt & 0x3);
if ((starting < 0) || (starting > 0xffff)) {
pr2ws("Bad starting field number: 0x%x\n", starting);
return -1;
}
sg_put_unaligned_be16((uint16_t)starting, gcCmdBlk + 2);
if ((mx_resp_len < 0) || (mx_resp_len > 0xffff)) {
pr2ws("Bad mx_resp_len: 0x%x\n", starting);
return -1;
}
sg_put_unaligned_be16((uint16_t)mx_resp_len, gcCmdBlk + 7);
if (verbose) {
pr2ws(" %s cdb: ", cdb_name_s);
for (k = 0; k < GET_CONFIG_CMD_LEN; ++k)
pr2ws("%02x ", gcCmdBlk[k]);
pr2ws("\n");
}
if (NULL == ((ptvp = create_pt_obj(cdb_name_s))))
return -1;
set_scsi_pt_cdb(ptvp, gcCmdBlk, sizeof(gcCmdBlk));
set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, mx_resp_len,
sense_b, noisy, verbose, &sense_cat);
if (-1 == ret) {
int os_err = get_scsi_pt_os_err(ptvp);
if ((os_err > 0) && (os_err < 47))
ret = SG_LIB_OS_BASE_ERR + os_err;
} else if (-2 == ret) {
switch (sense_cat) {
case SG_LIB_CAT_INVALID_OP:
case SG_LIB_CAT_ILLEGAL_REQ:
case SG_LIB_CAT_UNIT_ATTENTION:
case SG_LIB_CAT_ABORTED_COMMAND:
ret = sense_cat;
break;
case SG_LIB_CAT_RECOVERED:
case SG_LIB_CAT_NO_SENSE:
ret = 0;
break;
default:
ret = -1;
break;
}
} else {
if ((verbose > 2) && (ret > 3)) {
unsigned char * bp;
int len;
bp = (unsigned char *)resp;
len = sg_get_unaligned_be32(bp + 0);
if (len < 0)
len = 0;
len = (ret < len) ? ret : len;
pr2ws(" %s: response:\n", cdb_name_s);
if (3 == verbose) {
pr2ws("%s:\n", (len > 256 ? ", first 256 bytes" : ""));
hex2stderr((const uint8_t *)resp, (len > 256 ? 256 : len),
-1);
} else {
pr2ws(":\n");
hex2stderr((const uint8_t *)resp, len, 0);
}
}
ret = 0;
}
destruct_scsi_pt_obj(ptvp);
return ret;
}
/* Invokes a SCSI GET PERFORMANCE command (MMC-3...6).
* Returns 0 when successful, SG_LIB_CAT_INVALID_OP if command not
* supported, SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported,
* SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, else -1 */
int
sg_ll_get_performance(int sg_fd, int data_type, unsigned int starting_lba,
int max_num_desc, int ttype, void * resp,
int mx_resp_len, bool noisy, int verbose)
{
static const char * const cdb_name_s = "get performance";
int res, k, ret, sense_cat;
unsigned char gpCmdBlk[GET_PERFORMANCE_CMD_LEN] = {GET_PERFORMANCE_CMD, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
unsigned char sense_b[SENSE_BUFF_LEN];
struct sg_pt_base * ptvp;
if ((data_type < 0) || (data_type > 0x1f)) {
pr2ws("Bad data_type value: %d\n", data_type);
return -1;
}
gpCmdBlk[1] = (data_type & 0x1f);
sg_put_unaligned_be32((uint32_t)starting_lba, gpCmdBlk + 2);
if ((max_num_desc < 0) || (max_num_desc > 0xffff)) {
pr2ws("Bad max_num_desc: 0x%x\n", max_num_desc);
return -1;
}
sg_put_unaligned_be16((uint16_t)max_num_desc, gpCmdBlk + 8);
if ((ttype < 0) || (ttype > 0xff)) {
pr2ws("Bad type: 0x%x\n", ttype);
return -1;
}
gpCmdBlk[10] = (unsigned char)ttype;
if (verbose) {
pr2ws(" %s cdb: ", cdb_name_s);
for (k = 0; k < GET_PERFORMANCE_CMD_LEN; ++k)
pr2ws("%02x ", gpCmdBlk[k]);
pr2ws("\n");
}
if (NULL == ((ptvp = create_pt_obj(cdb_name_s))))
return -1;
set_scsi_pt_cdb(ptvp, gpCmdBlk, sizeof(gpCmdBlk));
set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, mx_resp_len, sense_b,
noisy, verbose, &sense_cat);
if (-1 == ret) {
int os_err = get_scsi_pt_os_err(ptvp);
if ((os_err > 0) && (os_err < 47))
ret = SG_LIB_OS_BASE_ERR + os_err;
} else if (-2 == ret) {
switch (sense_cat) {
case SG_LIB_CAT_INVALID_OP:
case SG_LIB_CAT_ILLEGAL_REQ:
case SG_LIB_CAT_UNIT_ATTENTION:
case SG_LIB_CAT_ABORTED_COMMAND:
ret = sense_cat;
break;
case SG_LIB_CAT_RECOVERED:
case SG_LIB_CAT_NO_SENSE:
ret = 0;
break;
default:
ret = -1;
break;
}
} else {
if ((verbose > 2) && (ret > 3)) {
unsigned char * bp;
int len;
bp = (unsigned char *)resp;
len = sg_get_unaligned_be32(bp + 0);
if (len < 0)
len = 0;
len = (ret < len) ? ret : len;
pr2ws(" %s: response", cdb_name_s);
if (3 == verbose) {
pr2ws("%s:\n", (len > 256 ? ", first 256 bytes" : ""));
hex2stderr((const uint8_t *)resp, (len > 256 ? 256 : len),
-1);
} else {
pr2ws(":\n");
hex2stderr((const uint8_t *)resp, len, 0);
}
}
ret = 0;
}
destruct_scsi_pt_obj(ptvp);
return ret;
}
/* Invokes a SCSI SET STREAMING command (MMC). Return of 0 -> success,
* SG_LIB_CAT_INVALID_OP -> Set Streaming not supported,
* SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
* SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_NOT_READY -> device not ready,
* -1 -> other failure */
int
sg_ll_set_streaming(int sg_fd, int type, void * paramp, int param_len,
bool noisy, int verbose)
{
static const char * const cdb_name_s = "set streaming";
int k, res, ret, sense_cat;
unsigned char ssCmdBlk[SET_STREAMING_CMDLEN] =
{SET_STREAMING_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
unsigned char sense_b[SENSE_BUFF_LEN];
struct sg_pt_base * ptvp;
ssCmdBlk[8] = type;
sg_put_unaligned_be16((uint16_t)param_len, ssCmdBlk + 9);
if (verbose) {
pr2ws(" %s cdb: ", cdb_name_s);
for (k = 0; k < SET_STREAMING_CMDLEN; ++k)
pr2ws("%02x ", ssCmdBlk[k]);
pr2ws("\n");
if ((verbose > 1) && paramp && param_len) {
pr2ws(" %s parameter list:\n", cdb_name_s);
hex2stderr((const uint8_t *)paramp, param_len, -1);
}
}
if (NULL == ((ptvp = create_pt_obj(cdb_name_s))))
return -1;
set_scsi_pt_cdb(ptvp, ssCmdBlk, sizeof(ssCmdBlk));
set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len);
res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, SG_NO_DATA_IN, sense_b,
noisy, verbose, &sense_cat);
if (-1 == ret) {
int os_err = get_scsi_pt_os_err(ptvp);
if ((os_err > 0) && (os_err < 47))
ret = SG_LIB_OS_BASE_ERR + os_err;
} else if (-2 == ret) {
switch (sense_cat) {
case SG_LIB_CAT_NOT_READY:
case SG_LIB_CAT_INVALID_OP:
case SG_LIB_CAT_ILLEGAL_REQ:
case SG_LIB_CAT_UNIT_ATTENTION:
case SG_LIB_CAT_ABORTED_COMMAND:
ret = sense_cat;
break;
case SG_LIB_CAT_RECOVERED:
case SG_LIB_CAT_NO_SENSE:
ret = 0;
break;
default:
ret = -1;
break;
}
} else
ret = 0;
destruct_scsi_pt_obj(ptvp);
return ret;
}

View File

@ -1,256 +0,0 @@
/*
* Copyright (c) 1999-2018 Douglas Gilbert.
* All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the BSD_LICENSE file.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdbool.h>
#include <string.h>
#include <ctype.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef SG_LIB_LINUX
#include "sg_io_linux.h"
/* Version 1.07 20160405 */
#if defined(__GNUC__) || defined(__clang__)
static int pr2ws(const char * fmt, ...)
__attribute__ ((format (printf, 1, 2)));
#else
static int pr2ws(const char * fmt, ...);
#endif
static int
pr2ws(const char * fmt, ...)
{
va_list args;
int n;
va_start(args, fmt);
n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args);
va_end(args);
return n;
}
void
sg_print_masked_status(int masked_status)
{
int scsi_status = (masked_status << 1) & 0x7e;
sg_print_scsi_status(scsi_status);
}
static const char * linux_host_bytes[] = {
"DID_OK", "DID_NO_CONNECT", "DID_BUS_BUSY", "DID_TIME_OUT",
"DID_BAD_TARGET", "DID_ABORT", "DID_PARITY", "DID_ERROR",
"DID_RESET", "DID_BAD_INTR", "DID_PASSTHROUGH", "DID_SOFT_ERROR",
"DID_IMM_RETRY", "DID_REQUEUE", "DID_TRANSPORT_DISRUPTED",
"DID_TRANSPORT_FAILFAST", "DID_TARGET_FAILURE", "DID_NEXUS_FAILURE",
"DID_ALLOC_FAILURE", "DID_MEDIUM_ERROR",
};
#define LINUX_HOST_BYTES_SZ \
(int)(sizeof(linux_host_bytes) / sizeof(linux_host_bytes[0]))
void
sg_print_host_status(int host_status)
{
pr2ws("Host_status=0x%02x ", host_status);
if ((host_status < 0) || (host_status >= LINUX_HOST_BYTES_SZ))
pr2ws("is invalid ");
else
pr2ws("[%s] ", linux_host_bytes[host_status]);
}
static const char * linux_driver_bytes[] = {
"DRIVER_OK", "DRIVER_BUSY", "DRIVER_SOFT", "DRIVER_MEDIA",
"DRIVER_ERROR", "DRIVER_INVALID", "DRIVER_TIMEOUT", "DRIVER_HARD",
"DRIVER_SENSE"
};
#define LINUX_DRIVER_BYTES_SZ \
(int)(sizeof(linux_driver_bytes) / sizeof(linux_driver_bytes[0]))
#if 0
static const char * linux_driver_suggests[] = {
"SUGGEST_OK", "SUGGEST_RETRY", "SUGGEST_ABORT", "SUGGEST_REMAP",
"SUGGEST_DIE", "UNKNOWN","UNKNOWN","UNKNOWN",
"SUGGEST_SENSE"
};
#define LINUX_DRIVER_SUGGESTS_SZ \
(int)(sizeof(linux_driver_suggests) / sizeof(linux_driver_suggests[0]))
#endif
void
sg_print_driver_status(int driver_status)
{
int driv;
const char * driv_cp = "invalid";
driv = driver_status & SG_LIB_DRIVER_MASK;
if (driv < LINUX_DRIVER_BYTES_SZ)
driv_cp = linux_driver_bytes[driv];
#if 0
sugg = (driver_status & SG_LIB_SUGGEST_MASK) >> 4;
if (sugg < LINUX_DRIVER_SUGGESTS_SZ)
sugg_cp = linux_driver_suggests[sugg];
#endif
pr2ws("Driver_status=0x%02x", driver_status);
pr2ws(" [%s] ", driv_cp);
}
/* Returns 1 if no errors found and thus nothing printed; otherwise
prints error/warning (prefix by 'leadin') and returns 0. */
static int
sg_linux_sense_print(const char * leadin, int scsi_status, int host_status,
int driver_status, const unsigned char * sense_buffer,
int sb_len, bool raw_sinfo)
{
bool done_leadin = false;
bool done_sense = false;
scsi_status &= 0x7e; /*sanity */
if ((0 == scsi_status) && (0 == host_status) && (0 == driver_status))
return 1; /* No problems */
if (0 != scsi_status) {
if (leadin)
pr2ws("%s: ", leadin);
done_leadin = true;
pr2ws("SCSI status: ");
sg_print_scsi_status(scsi_status);
pr2ws("\n");
if (sense_buffer && ((scsi_status == SAM_STAT_CHECK_CONDITION) ||
(scsi_status == SAM_STAT_COMMAND_TERMINATED))) {
/* SAM_STAT_COMMAND_TERMINATED is obsolete */
sg_print_sense(0, sense_buffer, sb_len, raw_sinfo);
done_sense = true;
}
}
if (0 != host_status) {
if (leadin && (! done_leadin))
pr2ws("%s: ", leadin);
if (done_leadin)
pr2ws("plus...: ");
else
done_leadin = true;
sg_print_host_status(host_status);
pr2ws("\n");
}
if (0 != driver_status) {
if (done_sense &&
(SG_LIB_DRIVER_SENSE == (SG_LIB_DRIVER_MASK & driver_status)))
return 0;
if (leadin && (! done_leadin))
pr2ws("%s: ", leadin);
if (done_leadin)
pr2ws("plus...: ");
sg_print_driver_status(driver_status);
pr2ws("\n");
if (sense_buffer && (! done_sense) &&
(SG_LIB_DRIVER_SENSE == (SG_LIB_DRIVER_MASK & driver_status)))
sg_print_sense(0, sense_buffer, sb_len, raw_sinfo);
}
return 0;
}
#ifdef SG_IO
bool
sg_normalize_sense(const struct sg_io_hdr * hp,
struct sg_scsi_sense_hdr * sshp)
{
if ((NULL == hp) || (0 == hp->sb_len_wr)) {
if (sshp)
memset(sshp, 0, sizeof(struct sg_scsi_sense_hdr));
return 0;
}
return sg_scsi_normalize_sense(hp->sbp, hp->sb_len_wr, sshp);
}
/* Returns 1 if no errors found and thus nothing printed; otherwise
returns 0. */
int
sg_chk_n_print3(const char * leadin, struct sg_io_hdr * hp,
bool raw_sinfo)
{
return sg_linux_sense_print(leadin, hp->status, hp->host_status,
hp->driver_status, hp->sbp, hp->sb_len_wr,
raw_sinfo);
}
#endif
/* Returns 1 if no errors found and thus nothing printed; otherwise
returns 0. */
int
sg_chk_n_print(const char * leadin, int masked_status, int host_status,
int driver_status, const unsigned char * sense_buffer,
int sb_len, bool raw_sinfo)
{
int scsi_status = (masked_status << 1) & 0x7e;
return sg_linux_sense_print(leadin, scsi_status, host_status,
driver_status, sense_buffer, sb_len,
raw_sinfo);
}
#ifdef SG_IO
int
sg_err_category3(struct sg_io_hdr * hp)
{
return sg_err_category_new(hp->status, hp->host_status,
hp->driver_status, hp->sbp, hp->sb_len_wr);
}
#endif
int
sg_err_category(int masked_status, int host_status, int driver_status,
const unsigned char * sense_buffer, int sb_len)
{
int scsi_status = (masked_status << 1) & 0x7e;
return sg_err_category_new(scsi_status, host_status, driver_status,
sense_buffer, sb_len);
}
int
sg_err_category_new(int scsi_status, int host_status, int driver_status,
const unsigned char * sense_buffer, int sb_len)
{
int masked_driver_status = (SG_LIB_DRIVER_MASK & driver_status);
scsi_status &= 0x7e;
if ((0 == scsi_status) && (0 == host_status) &&
(0 == masked_driver_status))
return SG_LIB_CAT_CLEAN;
if ((SAM_STAT_CHECK_CONDITION == scsi_status) ||
(SAM_STAT_COMMAND_TERMINATED == scsi_status) ||
(SG_LIB_DRIVER_SENSE == masked_driver_status))
return sg_err_category_sense(sense_buffer, sb_len);
if (0 != host_status) {
if ((SG_LIB_DID_NO_CONNECT == host_status) ||
(SG_LIB_DID_BUS_BUSY == host_status) ||
(SG_LIB_DID_TIME_OUT == host_status))
return SG_LIB_CAT_TIMEOUT;
if (SG_LIB_DID_NEXUS_FAILURE == host_status)
return SG_LIB_CAT_RES_CONFLICT;
}
if (SG_LIB_DRIVER_TIMEOUT == masked_driver_status)
return SG_LIB_CAT_TIMEOUT;
return SG_LIB_CAT_OTHER;
}
#endif /* if SG_LIB_LINUX defined */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,141 +0,0 @@
/*
* Copyright (c) 2009-2018 Douglas Gilbert.
* All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the BSD_LICENSE file.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdbool.h>
#include <string.h>
#include <ctype.h>
#define __STDC_FORMAT_MACROS 1
#include <inttypes.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "sg_lib.h"
#include "sg_pt.h"
#include "sg_pt_nvme.h"
static const char * scsi_pt_version_str = "3.03 20180115";
static const char * nvme_scsi_vendor_str = "NVMe ";
const char *
scsi_pt_version()
{
return scsi_pt_version_str;
}
/* Given the NVMe Identify controller response and optionally the NVMe
* Identify namespace response (NULL otherwise), generate the SCSI VPD
* page 0x83 (device identification) descriptor(s) in dop. Return the
* number of bytes written which will not exceed max_do_len. Probably use
* Peripheral Device Type (pdt) of 0 (disk) for don't know. Transport
* protocol (tproto) should be -1 if not known, else SCSI value.
* N.B. Does not write total VPD page length into dop[2:3] . */
int
sg_make_vpd_devid_for_nvme(const uint8_t * nvme_id_ctl_p,
const uint8_t * nvme_id_ns_p, int pdt,
int tproto, uint8_t * dop, int max_do_len)
{
bool have_nguid, have_eui64;
int k, n;
char b[4];
if ((NULL == nvme_id_ctl_p) || (NULL == dop) || (max_do_len < 56))
return 0;
memset(dop, 0, max_do_len);
dop[0] = 0x1f & pdt; /* (PQ=0)<<5 | (PDT=pdt); 0 or 0xd (SES) */
dop[1] = 0x83; /* Device Identification VPD page number */
/* Build a T10 Vendor ID based designator (desig_id=1) for controller */
if (tproto >= 0) {
dop[4] = ((0xf & tproto) << 4) | 0x2;
dop[5] = 0xa1; /* PIV=1, ASSOC=2 (target device), desig_id=1 */
} else {
dop[4] = 0x2; /* Prococol id=0, code_set=2 (ASCII) */
dop[5] = 0x21; /* PIV=0, ASSOC=2 (target device), desig_id=1 */
}
memcpy(dop + 8, nvme_scsi_vendor_str, 8); /* N.B. this is "NVMe " */
memcpy(dop + 16, nvme_id_ctl_p + 24, 40); /* MN */
for (k = 40; k > 0; --k) {
if (' ' == dop[15 + k])
dop[15 + k] = '_'; /* convert trailing spaces */
else
break;
}
if (40 == k)
--k;
n = 16 + 1 + k;
if (max_do_len < (n + 20))
return 0;
memcpy(dop + n, nvme_id_ctl_p + 4, 20); /* SN */
for (k = 20; k > 0; --k) { /* trim trailing spaces */
if (' ' == dop[n + k - 1])
dop[n + k - 1] = '\0';
else
break;
}
n += k;
if (0 != (n % 4))
n = ((n / 4) + 1) * 4; /* round up to next modulo 4 */
dop[7] = n - 8;
if (NULL == nvme_id_ns_p)
return n;
/* Look for NGUID (16 byte identifier) or EUI64 (8 byte) fields in
* NVME Identify for namespace. If found form a EUI and a SCSI string
* descriptor for non-zero NGUID or EUI64 (prefer NGUID if both). */
have_nguid = ! sg_all_zeros(nvme_id_ns_p + 104, 16);
have_eui64 = ! sg_all_zeros(nvme_id_ns_p + 120, 8);
if ((! have_nguid) && (! have_eui64))
return n;
if (have_nguid) {
if (max_do_len < (n + 20))
return n;
dop[n + 0] = 0x1; /* Prococol id=0, code_set=1 (binary) */
dop[n + 1] = 0x02; /* PIV=0, ASSOC=0 (lu), desig_id=2 (eui) */
dop[n + 3] = 16;
memcpy(dop + n + 4, nvme_id_ns_p + 104, 16);
n += 20;
if (max_do_len < (n + 40))
return n;
dop[n + 0] = 0x3; /* Prococol id=0, code_set=3 (utf8) */
dop[n + 1] = 0x08; /* PIV=0, ASSOC=0 (lu), desig_id=8 (scsi string) */
dop[n + 3] = 36;
memcpy(dop + n + 4, "eui.", 4);
for (k = 0; k < 16; ++k) {
snprintf(b, sizeof(b), "%02X", nvme_id_ns_p[104 + k]);
memcpy(dop + n + 8 + (2 * k), b, 2);
}
return n + 40;
} else { /* have_eui64 is true, 8 byte identifier */
if (max_do_len < (n + 12))
return n;
dop[n + 0] = 0x1; /* Prococol id=0, code_set=1 (binary) */
dop[n + 1] = 0x02; /* PIV=0, ASSOC=0 (lu), desig_id=2 (eui) */
dop[n + 3] = 8;
memcpy(dop + n + 4, nvme_id_ns_p + 120, 8);
n += 12;
if (max_do_len < (n + 24))
return n;
dop[n + 0] = 0x3; /* Prococol id=0, code_set=3 (utf8) */
dop[n + 1] = 0x08; /* PIV=0, ASSOC=0 (lu), desig_id=8 (scsi string) */
dop[n + 3] = 20;
memcpy(dop + n + 4, "eui.", 4);
for (k = 0; k < 8; ++k) {
snprintf(b, sizeof(b), "%02X", nvme_id_ns_p[120 + k]);
memcpy(dop + n + 8 + (2 * k), b, 2);
}
return n + 24;
}
}

View File

@ -1,964 +0,0 @@
/*
* Copyright (c) 2005-2018 Douglas Gilbert.
* All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the BSD_LICENSE file.
*/
/* sg_pt_linux version 1.37 20180126 */
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdbool.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/sysmacros.h> /* to define 'major' */
#ifndef major
#include <sys/types.h>
#endif
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <linux/major.h>
#include "sg_pt.h"
#include "sg_lib.h"
#include "sg_linux_inc.h"
#include "sg_pt_linux.h"
#ifdef major
#define SG_DEV_MAJOR major
#else
#ifdef HAVE_LINUX_KDEV_T_H
#include <linux/kdev_t.h>
#endif
#define SG_DEV_MAJOR MAJOR /* MAJOR() macro faulty if > 255 minors */
#endif
#ifndef BLOCK_EXT_MAJOR
#define BLOCK_EXT_MAJOR 259
#endif
#define DEF_TIMEOUT 60000 /* 60,000 millisecs (60 seconds) */
static const char * linux_host_bytes[] = {
"DID_OK", "DID_NO_CONNECT", "DID_BUS_BUSY", "DID_TIME_OUT",
"DID_BAD_TARGET", "DID_ABORT", "DID_PARITY", "DID_ERROR",
"DID_RESET", "DID_BAD_INTR", "DID_PASSTHROUGH", "DID_SOFT_ERROR",
"DID_IMM_RETRY", "DID_REQUEUE" /* 0xd */,
"DID_TRANSPORT_DISRUPTED", "DID_TRANSPORT_FAILFAST",
"DID_TARGET_FAILURE" /* 0x10 */,
"DID_NEXUS_FAILURE (reservation conflict)",
"DID_ALLOC_FAILURE",
"DID_MEDIUM_ERROR",
};
#define LINUX_HOST_BYTES_SZ \
(int)(sizeof(linux_host_bytes) / sizeof(linux_host_bytes[0]))
static const char * linux_driver_bytes[] = {
"DRIVER_OK", "DRIVER_BUSY", "DRIVER_SOFT", "DRIVER_MEDIA",
"DRIVER_ERROR", "DRIVER_INVALID", "DRIVER_TIMEOUT", "DRIVER_HARD",
"DRIVER_SENSE"
};
#define LINUX_DRIVER_BYTES_SZ \
(int)(sizeof(linux_driver_bytes) / sizeof(linux_driver_bytes[0]))
#if 0
static const char * linux_driver_suggests[] = {
"SUGGEST_OK", "SUGGEST_RETRY", "SUGGEST_ABORT", "SUGGEST_REMAP",
"SUGGEST_DIE", "UNKNOWN","UNKNOWN","UNKNOWN",
"SUGGEST_SENSE"
};
#define LINUX_DRIVER_SUGGESTS_SZ \
(int)(sizeof(linux_driver_suggests) / sizeof(linux_driver_suggests[0]))
#endif
/*
* These defines are for constants that should be visible in the
* /usr/include/scsi directory (brought in by sg_linux_inc.h).
* Redefined and aliased here to decouple this code from
* sg_io_linux.h N.B. the SUGGEST_* constants are no longer used.
*/
#ifndef DRIVER_MASK
#define DRIVER_MASK 0x0f
#endif
#ifndef SUGGEST_MASK
#define SUGGEST_MASK 0xf0
#endif
#ifndef DRIVER_SENSE
#define DRIVER_SENSE 0x08
#endif
#define SG_LIB_DRIVER_MASK DRIVER_MASK
#define SG_LIB_SUGGEST_MASK SUGGEST_MASK
#define SG_LIB_DRIVER_SENSE DRIVER_SENSE
bool sg_bsg_nvme_char_major_checked = false;
int sg_bsg_major = 0;
volatile int sg_nvme_char_major = 0;
long sg_lin_page_size = 4096; /* default, overridden with correct value */
#if defined(__GNUC__) || defined(__clang__)
static int pr2ws(const char * fmt, ...)
__attribute__ ((format (printf, 1, 2)));
#else
static int pr2ws(const char * fmt, ...);
#endif
static int
pr2ws(const char * fmt, ...)
{
va_list args;
int n;
va_start(args, fmt);
n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args);
va_end(args);
return n;
}
/* This function only needs to be called once (unless a NVMe controller
* can be hot-plugged into system in which case it should be called
* (again) after that event). */
void
sg_find_bsg_nvme_char_major(int verbose)
{
bool got_one = false;
int n;
const char * proc_devices = "/proc/devices";
char * cp;
FILE *fp;
char a[128];
char b[128];
sg_lin_page_size = sysconf(_SC_PAGESIZE);
if (NULL == (fp = fopen(proc_devices, "r"))) {
if (verbose)
pr2ws("fopen %s failed: %s\n", proc_devices, strerror(errno));
return;
}
while ((cp = fgets(b, sizeof(b), fp))) {
if ((1 == sscanf(b, "%126s", a)) &&
(0 == memcmp(a, "Character", 9)))
break;
}
while (cp && (cp = fgets(b, sizeof(b), fp))) {
if (2 == sscanf(b, "%d %126s", &n, a)) {
if (0 == strcmp("bsg", a)) {
sg_bsg_major = n;
if (got_one)
break;
got_one = true;
} else if (0 == strcmp("nvme", a)) {
sg_nvme_char_major = n;
if (got_one)
break;
got_one = true;
}
} else
break;
}
if (verbose > 3) {
if (cp) {
if (sg_bsg_major > 0)
pr2ws("found sg_bsg_major=%d\n", sg_bsg_major);
if (sg_nvme_char_major > 0)
pr2ws("found sg_nvme_char_major=%d\n", sg_nvme_char_major);
} else
pr2ws("found no bsg not nvme char device in %s\n", proc_devices);
}
fclose(fp);
}
/* Assumes that sg_find_bsg_nvme_char_major() has already been called. Returns
* true if dev_fd is a scsi generic pass-through device. If yields
* *is_nvme_p = true with *nsid_p = 0 then dev_fd is a NVMe char device.
* If yields *nsid_p > 0 then dev_fd is a NVMe block device. */
static bool
check_file_type(int dev_fd, struct stat * dev_statp, bool * is_bsg_p,
bool * is_nvme_p, uint32_t * nsid_p, int * os_err_p,
int verbose)
{
bool is_nvme = false;
bool is_sg = false;
bool is_bsg = false;
bool is_block = false;
int os_err = 0;
int major_num;
uint32_t nsid = 0; /* invalid NSID */
if (dev_fd >= 0) {
if (fstat(dev_fd, dev_statp) < 0) {
os_err = errno;
if (verbose)
pr2ws("%s: fstat() failed: %s (errno=%d)\n", __func__,
safe_strerror(os_err), os_err);
goto skip_out;
}
major_num = (int)SG_DEV_MAJOR(dev_statp->st_rdev);
if (S_ISCHR(dev_statp->st_mode)) {
if (SCSI_GENERIC_MAJOR == major_num)
is_sg = true;
else if (sg_bsg_major == major_num)
is_bsg = true;
else if (sg_nvme_char_major == major_num)
is_nvme = true;
} else if (S_ISBLK(dev_statp->st_mode)) {
is_block = true;
if (BLOCK_EXT_MAJOR == major_num) {
is_nvme = true;
nsid = ioctl(dev_fd, NVME_IOCTL_ID, NULL);
if (SG_NVME_BROADCAST_NSID == nsid) { /* means ioctl error */
os_err = errno;
if (verbose)
pr2ws("%s: ioctl(NVME_IOCTL_ID) failed: %s "
"(errno=%d)\n", __func__, safe_strerror(os_err),
os_err);
} else
os_err = 0;
}
}
} else {
os_err = EBADF;
if (verbose)
pr2ws("%s: invalid file descriptor (%d)\n", __func__, dev_fd);
}
skip_out:
if (verbose > 3) {
pr2ws("%s: file descriptor is ", __func__);
if (is_sg)
pr2ws("sg device\n");
else if (is_bsg)
pr2ws("bsg device\n");
else if (is_nvme && (0 == nsid))
pr2ws("NVMe char device\n");
else if (is_nvme)
pr2ws("NVMe block device, nsid=%lld\n",
((uint32_t)-1 == nsid) ? -1LL : (long long)nsid);
else if (is_block)
pr2ws("block device\n");
else
pr2ws("undetermined device, could be regular file\n");
}
if (is_bsg_p)
*is_bsg_p = is_bsg;
if (is_nvme_p)
*is_nvme_p = is_nvme;
if (nsid_p)
*nsid_p = nsid;
if (os_err_p)
*os_err_p = os_err;
return is_sg;
}
/* Assumes dev_fd is an "open" file handle associated with device_name. If
* the implementation (possibly for one OS) cannot determine from dev_fd if
* a SCSI or NVMe pass-through is referenced, then it might guess based on
* device_name. Returns 1 if SCSI generic pass-though device, returns 2 if
* secondary SCSI pass-through device (in Linux a bsg device); returns 3 is
* char NVMe device (i.e. no NSID); returns 4 if block NVMe device (includes
* NSID), or 0 if something else (e.g. ATA block device) or dev_fd < 0.
* If error, returns negated errno (operating system) value. */
int
check_pt_file_handle(int dev_fd, const char * device_name, int verbose)
{
if (verbose > 4)
pr2ws("%s: dev_fd=%d, device_name: %s\n", __func__, dev_fd,
device_name);
/* Linux doesn't need device_name to determine which pass-through */
if (! sg_bsg_nvme_char_major_checked) {
sg_bsg_nvme_char_major_checked = true;
sg_find_bsg_nvme_char_major(verbose);
}
if (dev_fd >= 0) {
bool is_sg, is_bsg, is_nvme;
int err;
uint32_t nsid;
struct stat a_stat;
is_sg = check_file_type(dev_fd, &a_stat, &is_bsg, &is_nvme, &nsid,
&err, verbose);
if (err)
return -err;
else if (is_sg)
return 1;
else if (is_bsg)
return 2;
else if (is_nvme && (0 == nsid))
return 3;
else if (is_nvme)
return 4;
else
return 0;
} else
return 0;
}
/*
* We make a runtime decision whether to use the sg v3 interface or the sg
* v4 interface (currently exclusively used by the bsg driver). If all the
* following are true we use sg v4 which is only currently supported on bsg
* device nodes:
* a) there is a bsg entry in the /proc/devices file
* b) the device node given to scsi_pt_open() is a char device
* c) the char major number of the device node given to scsi_pt_open()
* matches the char major number of the bsg entry in /proc/devices
* Otherwise the sg v3 interface is used.
*
* Note that in either case we prepare the data in a sg v4 structure. If
* the runtime tests indicate that the v3 interface is needed then
* do_scsi_pt_v3() transfers the input data into a v3 structure and
* then the output data is transferred back into a sg v4 structure.
* That implementation detail could change in the future.
*
* [20120806] Only use MAJOR() macro in kdev_t.h if that header file is
* available and major() macro [N.B. lower case] is not available.
*/
#ifdef major
#define SG_DEV_MAJOR major
#else
#ifdef HAVE_LINUX_KDEV_T_H
#include <linux/kdev_t.h>
#endif
#define SG_DEV_MAJOR MAJOR /* MAJOR() macro faulty if > 255 minors */
#endif
/* Returns >= 0 if successful. If error in Unix returns negated errno. */
int
scsi_pt_open_device(const char * device_name, bool read_only, int verbose)
{
int oflags = O_NONBLOCK;
oflags |= (read_only ? O_RDONLY : O_RDWR);
return scsi_pt_open_flags(device_name, oflags, verbose);
}
/* Similar to scsi_pt_open_device() but takes Unix style open flags OR-ed */
/* together. The 'flags' argument is advisory and may be ignored. */
/* Returns >= 0 if successful, otherwise returns negated errno. */
int
scsi_pt_open_flags(const char * device_name, int flags, int verbose)
{
int fd;
if (! sg_bsg_nvme_char_major_checked) {
sg_bsg_nvme_char_major_checked = true;
sg_find_bsg_nvme_char_major(verbose);
}
if (verbose > 1) {
pr2ws("open %s with flags=0x%x\n", device_name, flags);
}
fd = open(device_name, flags);
if (fd < 0) {
fd = -errno;
if (verbose > 1)
pr2ws("%s: open(%s, 0x%x) failed: %s\n", __func__, device_name,
flags, safe_strerror(-fd));
}
return fd;
}
/* Returns 0 if successful. If error in Unix returns negated errno. */
int
scsi_pt_close_device(int device_fd)
{
int res;
res = close(device_fd);
if (res < 0)
res = -errno;
return res;
}
/* Caller should additionally call get_scsi_pt_os_err() after this call */
struct sg_pt_base *
construct_scsi_pt_obj_with_fd(int dev_fd, int verbose)
{
int err;
struct sg_pt_linux_scsi * ptp;
/* The following 2 lines are temporary. It is to avoid a NULL pointer
* crash when an old utility is used with a newer library built after
* the sg_warnings_strm cleanup */
if (NULL == sg_warnings_strm)
sg_warnings_strm = stderr;
ptp = (struct sg_pt_linux_scsi *)
calloc(1, sizeof(struct sg_pt_linux_scsi));
if (ptp) {
err = set_pt_file_handle((struct sg_pt_base *)ptp, dev_fd, verbose);
if ((0 == err) && (! ptp->is_nvme)) {
ptp->io_hdr.guard = 'Q';
#ifdef BSG_PROTOCOL_SCSI
ptp->io_hdr.protocol = BSG_PROTOCOL_SCSI;
#endif
#ifdef BSG_SUB_PROTOCOL_SCSI_CMD
ptp->io_hdr.subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD;
#endif
}
} else if (verbose)
pr2ws("%s: calloc() failed, out of memory?\n", __func__);
return (struct sg_pt_base *)ptp;
}
struct sg_pt_base *
construct_scsi_pt_obj()
{
return construct_scsi_pt_obj_with_fd(-1 /* dev_fd */, 0 /* verbose */);
}
void
destruct_scsi_pt_obj(struct sg_pt_base * vp)
{
struct sg_pt_linux_scsi * ptp = &vp->impl;
if (ptp->free_nvme_id_ctlp) {
free(ptp->free_nvme_id_ctlp);
ptp->free_nvme_id_ctlp = NULL;
ptp->nvme_id_ctlp = NULL;
}
if (ptp)
free(ptp);
}
/* Remembers previous device file descriptor */
void
clear_scsi_pt_obj(struct sg_pt_base * vp)
{
bool is_sg, is_bsg, is_nvme;
int fd;
uint32_t nvme_nsid;
struct sg_pt_linux_scsi * ptp = &vp->impl;
if (ptp) {
fd = ptp->dev_fd;
is_sg = ptp->is_sg;
is_bsg = ptp->is_bsg;
is_nvme = ptp->is_nvme;
nvme_nsid = ptp->nvme_nsid;
memset(ptp, 0, sizeof(struct sg_pt_linux_scsi));
ptp->io_hdr.guard = 'Q';
#ifdef BSG_PROTOCOL_SCSI
ptp->io_hdr.protocol = BSG_PROTOCOL_SCSI;
#endif
#ifdef BSG_SUB_PROTOCOL_SCSI_CMD
ptp->io_hdr.subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD;
#endif
ptp->dev_fd = fd;
ptp->is_sg = is_sg;
ptp->is_bsg = is_bsg;
ptp->is_nvme = is_nvme;
ptp->nvme_direct = false;
ptp->nvme_nsid = nvme_nsid;
}
}
/* Forget any previous dev_fd and install the one given. May attempt to
* find file type (e.g. if pass-though) from OS so there could be an error.
* Returns 0 for success or the same value as get_scsi_pt_os_err()
* will return. dev_fd should be >= 0 for a valid file handle or -1 . */
int
set_pt_file_handle(struct sg_pt_base * vp, int dev_fd, int verbose)
{
struct sg_pt_linux_scsi * ptp = &vp->impl;
struct stat a_stat;
if (! sg_bsg_nvme_char_major_checked) {
sg_bsg_nvme_char_major_checked = true;
sg_find_bsg_nvme_char_major(verbose);
}
ptp->dev_fd = dev_fd;
if (dev_fd >= 0)
ptp->is_sg = check_file_type(dev_fd, &a_stat, &ptp->is_bsg,
&ptp->is_nvme, &ptp->nvme_nsid,
&ptp->os_err, verbose);
else {
ptp->is_sg = false;
ptp->is_bsg = false;
ptp->is_nvme = false;
ptp->nvme_direct = false;
ptp->nvme_nsid = 0;
ptp->os_err = 0;
}
return ptp->os_err;
}
/* Valid file handles (which is the return value) are >= 0 . Returns -1
* if there is no valid file handle. */
int
get_pt_file_handle(const struct sg_pt_base * vp)
{
const struct sg_pt_linux_scsi * ptp = &vp->impl;
return ptp->dev_fd;
}
void
set_scsi_pt_cdb(struct sg_pt_base * vp, const unsigned char * cdb,
int cdb_len)
{
struct sg_pt_linux_scsi * ptp = &vp->impl;
if (ptp->io_hdr.request)
++ptp->in_err;
ptp->io_hdr.request = (__u64)(sg_uintptr_t)cdb;
ptp->io_hdr.request_len = cdb_len;
}
void
set_scsi_pt_sense(struct sg_pt_base * vp, unsigned char * sense,
int max_sense_len)
{
struct sg_pt_linux_scsi * ptp = &vp->impl;
if (ptp->io_hdr.response)
++ptp->in_err;
memset(sense, 0, max_sense_len);
ptp->io_hdr.response = (__u64)(sg_uintptr_t)sense;
ptp->io_hdr.max_response_len = max_sense_len;
}
/* Setup for data transfer from device */
void
set_scsi_pt_data_in(struct sg_pt_base * vp, unsigned char * dxferp,
int dxfer_ilen)
{
struct sg_pt_linux_scsi * ptp = &vp->impl;
if (ptp->io_hdr.din_xferp)
++ptp->in_err;
if (dxfer_ilen > 0) {
ptp->io_hdr.din_xferp = (__u64)(sg_uintptr_t)dxferp;
ptp->io_hdr.din_xfer_len = dxfer_ilen;
}
}
/* Setup for data transfer toward device */
void
set_scsi_pt_data_out(struct sg_pt_base * vp, const unsigned char * dxferp,
int dxfer_olen)
{
struct sg_pt_linux_scsi * ptp = &vp->impl;
if (ptp->io_hdr.dout_xferp)
++ptp->in_err;
if (dxfer_olen > 0) {
ptp->io_hdr.dout_xferp = (__u64)(sg_uintptr_t)dxferp;
ptp->io_hdr.dout_xfer_len = dxfer_olen;
}
}
void
set_pt_metadata_xfer(struct sg_pt_base * vp, unsigned char * dxferp,
uint32_t dxfer_len, bool out_true)
{
struct sg_pt_linux_scsi * ptp = &vp->impl;
if (dxfer_len > 0) {
ptp->mdxferp = dxferp;
ptp->mdxfer_len = dxfer_len;
ptp->mdxfer_out = out_true;
}
}
void
set_scsi_pt_packet_id(struct sg_pt_base * vp, int pack_id)
{
struct sg_pt_linux_scsi * ptp = &vp->impl;
ptp->io_hdr.spare_in = pack_id;
}
void
set_scsi_pt_tag(struct sg_pt_base * vp, uint64_t tag)
{
struct sg_pt_linux_scsi * ptp = &vp->impl;
ptp->io_hdr.request_tag = tag;
}
/* Note that task management function codes are transport specific */
void
set_scsi_pt_task_management(struct sg_pt_base * vp, int tmf_code)
{
struct sg_pt_linux_scsi * ptp = &vp->impl;
ptp->io_hdr.subprotocol = 1; /* SCSI task management function */
ptp->tmf_request[0] = (unsigned char)tmf_code; /* assume it fits */
ptp->io_hdr.request = (__u64)(sg_uintptr_t)(&(ptp->tmf_request[0]));
ptp->io_hdr.request_len = 1;
}
void
set_scsi_pt_task_attr(struct sg_pt_base * vp, int attribute, int priority)
{
struct sg_pt_linux_scsi * ptp = &vp->impl;
ptp->io_hdr.request_attr = attribute;
ptp->io_hdr.request_priority = priority;
}
#ifndef BSG_FLAG_Q_AT_TAIL
#define BSG_FLAG_Q_AT_TAIL 0x10
#endif
#ifndef BSG_FLAG_Q_AT_HEAD
#define BSG_FLAG_Q_AT_HEAD 0x20
#endif
/* Need this later if translated to v3 interface */
#ifndef SG_FLAG_Q_AT_TAIL
#define SG_FLAG_Q_AT_TAIL 0x10
#endif
#ifndef SG_FLAG_Q_AT_HEAD
#define SG_FLAG_Q_AT_HEAD 0x20
#endif
void
set_scsi_pt_flags(struct sg_pt_base * vp, int flags)
{
struct sg_pt_linux_scsi * ptp = &vp->impl;
/* default action of bsg driver (sg v4) is QUEUE_AT_HEAD */
/* default action of block layer SG_IO ioctl is QUEUE_AT_TAIL */
if (SCSI_PT_FLAGS_QUEUE_AT_HEAD & flags) { /* favour AT_HEAD */
ptp->io_hdr.flags |= BSG_FLAG_Q_AT_HEAD;
ptp->io_hdr.flags &= ~BSG_FLAG_Q_AT_TAIL;
} else if (SCSI_PT_FLAGS_QUEUE_AT_TAIL & flags) {
ptp->io_hdr.flags |= BSG_FLAG_Q_AT_TAIL;
ptp->io_hdr.flags &= ~BSG_FLAG_Q_AT_HEAD;
}
}
/* N.B. Returns din_resid and ignores dout_resid */
int
get_scsi_pt_resid(const struct sg_pt_base * vp)
{
const struct sg_pt_linux_scsi * ptp = &vp->impl;
if (NULL == ptp)
return 0;
return ptp->nvme_direct ? 0 : ptp->io_hdr.din_resid;
}
int
get_scsi_pt_status_response(const struct sg_pt_base * vp)
{
const struct sg_pt_linux_scsi * ptp = &vp->impl;
if (NULL == ptp)
return 0;
return (int)(ptp->nvme_direct ? ptp->nvme_status :
ptp->io_hdr.device_status);
}
uint32_t
get_pt_result(const struct sg_pt_base * vp)
{
const struct sg_pt_linux_scsi * ptp = &vp->impl;
if (NULL == ptp)
return 0;
return ptp->nvme_direct ? ptp->nvme_result :
ptp->io_hdr.device_status;
}
int
get_scsi_pt_sense_len(const struct sg_pt_base * vp)
{
const struct sg_pt_linux_scsi * ptp = &vp->impl;
return ptp->io_hdr.response_len;
}
int
get_scsi_pt_duration_ms(const struct sg_pt_base * vp)
{
const struct sg_pt_linux_scsi * ptp = &vp->impl;
return ptp->io_hdr.duration;
}
int
get_scsi_pt_transport_err(const struct sg_pt_base * vp)
{
const struct sg_pt_linux_scsi * ptp = &vp->impl;
return ptp->io_hdr.transport_status;
}
void
set_scsi_pt_transport_err(struct sg_pt_base * vp, int err)
{
struct sg_pt_linux_scsi * ptp = &vp->impl;
ptp->io_hdr.transport_status = err;
}
/* Returns b which will contain a null char terminated string (if
* max_b_len > 0). Combined driver and transport (called "host" in Linux
* kernel) statuses */
char *
get_scsi_pt_transport_err_str(const struct sg_pt_base * vp, int max_b_len,
char * b)
{
const struct sg_pt_linux_scsi * ptp = &vp->impl;
int ds = ptp->io_hdr.driver_status;
int hs = ptp->io_hdr.transport_status;
int n, m;
char * cp = b;
int driv;
const char * driv_cp = "invalid";
if (max_b_len < 1)
return b;
m = max_b_len;
n = 0;
if (hs) {
if ((hs < 0) || (hs >= LINUX_HOST_BYTES_SZ))
n = snprintf(cp, m, "Host_status=0x%02x is invalid\n", hs);
else
n = snprintf(cp, m, "Host_status=0x%02x [%s]\n", hs,
linux_host_bytes[hs]);
}
m -= n;
if (m < 1) {
b[max_b_len - 1] = '\0';
return b;
}
cp += n;
driv = ds & SG_LIB_DRIVER_MASK;
if (driv < LINUX_DRIVER_BYTES_SZ)
driv_cp = linux_driver_bytes[driv];
#if 0
sugg = (ds & SG_LIB_SUGGEST_MASK) >> 4;
if (sugg < LINUX_DRIVER_SUGGESTS_SZ)
sugg_cp = linux_driver_suggests[sugg];
#endif
n = snprintf(cp, m, "Driver_status=0x%02x [%s]\n", ds, driv_cp);
m -= n;
if (m < 1)
b[max_b_len - 1] = '\0';
return b;
}
int
get_scsi_pt_result_category(const struct sg_pt_base * vp)
{
const struct sg_pt_linux_scsi * ptp = &vp->impl;
int dr_st = ptp->io_hdr.driver_status & SG_LIB_DRIVER_MASK;
int scsi_st = ptp->io_hdr.device_status & 0x7e;
if (ptp->os_err)
return SCSI_PT_RESULT_OS_ERR;
else if (ptp->io_hdr.transport_status)
return SCSI_PT_RESULT_TRANSPORT_ERR;
else if (dr_st && (SG_LIB_DRIVER_SENSE != dr_st))
return SCSI_PT_RESULT_TRANSPORT_ERR;
else if ((SG_LIB_DRIVER_SENSE == dr_st) ||
(SAM_STAT_CHECK_CONDITION == scsi_st) ||
(SAM_STAT_COMMAND_TERMINATED == scsi_st))
return SCSI_PT_RESULT_SENSE;
else if (scsi_st)
return SCSI_PT_RESULT_STATUS;
else
return SCSI_PT_RESULT_GOOD;
}
int
get_scsi_pt_os_err(const struct sg_pt_base * vp)
{
const struct sg_pt_linux_scsi * ptp = &vp->impl;
return ptp->os_err;
}
char *
get_scsi_pt_os_err_str(const struct sg_pt_base * vp, int max_b_len, char * b)
{
const struct sg_pt_linux_scsi * ptp = &vp->impl;
const char * cp;
cp = safe_strerror(ptp->os_err);
strncpy(b, cp, max_b_len);
if ((int)strlen(cp) >= max_b_len)
b[max_b_len - 1] = '\0';
return b;
}
bool
pt_device_is_nvme(const struct sg_pt_base * vp)
{
const struct sg_pt_linux_scsi * ptp = &vp->impl;
return ptp->is_nvme;
}
/* If a NVMe block device (which includes the NSID) handle is associated
* with 'vp', then its NSID is returned (values range from 0x1 to
* 0xffffffe). Otherwise 0 is returned. */
uint32_t
get_pt_nvme_nsid(const struct sg_pt_base * vp)
{
const struct sg_pt_linux_scsi * ptp = &vp->impl;
return ptp->nvme_nsid;
}
/* Executes SCSI command using sg v3 interface */
static int
do_scsi_pt_v3(struct sg_pt_linux_scsi * ptp, int fd, int time_secs,
int verbose)
{
struct sg_io_hdr v3_hdr;
memset(&v3_hdr, 0, sizeof(v3_hdr));
/* convert v4 to v3 header */
v3_hdr.interface_id = 'S';
v3_hdr.dxfer_direction = SG_DXFER_NONE;
v3_hdr.cmdp = (unsigned char *)(long)ptp->io_hdr.request;
v3_hdr.cmd_len = (unsigned char)ptp->io_hdr.request_len;
if (ptp->io_hdr.din_xfer_len > 0) {
if (ptp->io_hdr.dout_xfer_len > 0) {
if (verbose)
pr2ws("sgv3 doesn't support bidi\n");
return SCSI_PT_DO_BAD_PARAMS;
}
v3_hdr.dxferp = (void *)(long)ptp->io_hdr.din_xferp;
v3_hdr.dxfer_len = (unsigned int)ptp->io_hdr.din_xfer_len;
v3_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
} else if (ptp->io_hdr.dout_xfer_len > 0) {
v3_hdr.dxferp = (void *)(long)ptp->io_hdr.dout_xferp;
v3_hdr.dxfer_len = (unsigned int)ptp->io_hdr.dout_xfer_len;
v3_hdr.dxfer_direction = SG_DXFER_TO_DEV;
}
if (ptp->io_hdr.response && (ptp->io_hdr.max_response_len > 0)) {
v3_hdr.sbp = (unsigned char *)(long)ptp->io_hdr.response;
v3_hdr.mx_sb_len = (unsigned char)ptp->io_hdr.max_response_len;
}
v3_hdr.pack_id = (int)ptp->io_hdr.spare_in;
if (BSG_FLAG_Q_AT_HEAD & ptp->io_hdr.flags)
v3_hdr.flags |= SG_FLAG_Q_AT_HEAD; /* favour AT_HEAD */
else if (BSG_FLAG_Q_AT_TAIL & ptp->io_hdr.flags)
v3_hdr.flags |= SG_FLAG_Q_AT_TAIL;
if (NULL == v3_hdr.cmdp) {
if (verbose)
pr2ws("No SCSI command (cdb) given\n");
return SCSI_PT_DO_BAD_PARAMS;
}
/* io_hdr.timeout is in milliseconds, if greater than zero */
v3_hdr.timeout = ((time_secs > 0) ? (time_secs * 1000) : DEF_TIMEOUT);
/* Finally do the v3 SG_IO ioctl */
if (ioctl(fd, SG_IO, &v3_hdr) < 0) {
ptp->os_err = errno;
if (verbose > 1)
pr2ws("ioctl(SG_IO v3) failed: %s (errno=%d)\n",
safe_strerror(ptp->os_err), ptp->os_err);
return -ptp->os_err;
}
ptp->io_hdr.device_status = (__u32)v3_hdr.status;
ptp->io_hdr.driver_status = (__u32)v3_hdr.driver_status;
ptp->io_hdr.transport_status = (__u32)v3_hdr.host_status;
ptp->io_hdr.response_len = (__u32)v3_hdr.sb_len_wr;
ptp->io_hdr.duration = (__u32)v3_hdr.duration;
ptp->io_hdr.din_resid = (__s32)v3_hdr.resid;
/* v3_hdr.info not passed back since no mapping defined (yet) */
return 0;
}
/* Executes SCSI command (or at least forwards it to lower layers).
* Returns 0 for success, negative numbers are negated 'errno' values from
* OS system calls. Positive return values are errors from this package. */
int
do_scsi_pt(struct sg_pt_base * vp, int fd, int time_secs, int verbose)
{
int err;
struct sg_pt_linux_scsi * ptp = &vp->impl;
bool have_checked_for_type = (ptp->dev_fd >= 0);
if (! sg_bsg_nvme_char_major_checked) {
sg_bsg_nvme_char_major_checked = true;
sg_find_bsg_nvme_char_major(verbose);
}
if (ptp->in_err) {
if (verbose)
pr2ws("Replicated or unused set_scsi_pt... functions\n");
return SCSI_PT_DO_BAD_PARAMS;
}
if (fd >= 0) {
if ((ptp->dev_fd >= 0) && (fd != ptp->dev_fd)) {
if (verbose)
pr2ws("%s: file descriptor given to create() and here "
"differ\n", __func__);
return SCSI_PT_DO_BAD_PARAMS;
}
ptp->dev_fd = fd;
} else if (ptp->dev_fd < 0) {
if (verbose)
pr2ws("%s: invalid file descriptors\n", __func__);
return SCSI_PT_DO_BAD_PARAMS;
} else
fd = ptp->dev_fd;
if (! have_checked_for_type) {
err = set_pt_file_handle(vp, ptp->dev_fd, verbose);
if (err)
return -ptp->os_err;
}
if (ptp->os_err)
return -ptp->os_err;
if (ptp->is_nvme)
return sg_do_nvme_pt(vp, -1, time_secs, verbose);
else if (sg_bsg_major <= 0)
return do_scsi_pt_v3(ptp, fd, time_secs, verbose);
else if (ptp->is_bsg)
; /* drop through to sg v4 implementation */
else
return do_scsi_pt_v3(ptp, fd, time_secs, verbose);
if (! ptp->io_hdr.request) {
if (verbose)
pr2ws("No SCSI command (cdb) given (v4)\n");
return SCSI_PT_DO_BAD_PARAMS;
}
/* io_hdr.timeout is in milliseconds */
ptp->io_hdr.timeout = ((time_secs > 0) ? (time_secs * 1000) :
DEF_TIMEOUT);
#if 0
/* sense buffer already zeroed */
if (ptp->io_hdr.response && (ptp->io_hdr.max_response_len > 0)) {
void * p;
p = (void *)(long)ptp->io_hdr.response;
memset(p, 0, ptp->io_hdr.max_response_len);
}
#endif
if (ioctl(fd, SG_IO, &ptp->io_hdr) < 0) {
ptp->os_err = errno;
if (verbose > 1)
pr2ws("ioctl(SG_IO v4) failed: %s (errno=%d)\n",
safe_strerror(ptp->os_err), ptp->os_err);
return -ptp->os_err;
}
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,516 +0,0 @@
/*
* Copyright (c) 2006-2018 Luben Tuikov and Douglas Gilbert.
* All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the BSD_LICENSE file.
*/
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdbool.h>
#include <ctype.h>
#include <string.h>
#include <getopt.h>
#define __STDC_FORMAT_MACROS 1
#include <inttypes.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "sg_lib.h"
#include "sg_cmds_basic.h"
#include "sg_cmds_extra.h"
#include "sg_unaligned.h"
#include "sg_pr2serr.h"
#ifdef SG_LIB_WIN32
#ifdef SG_LIB_WIN32_DIRECT
#include "sg_pt.h" /* needed for scsi_pt_win32_direct() */
#endif
#endif
/*
* This utility issues the SCSI WRITE BUFFER command to the given device.
*/
static const char * version_str = "1.24 20180111"; /* spc5r18 */
#define ME "sg_write_buffer: "
#define DEF_XFER_LEN (8 * 1024 * 1024)
#define EBUFF_SZ 256
#define WRITE_BUFFER_CMD 0x3b
#define WRITE_BUFFER_CMDLEN 10
#define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */
#define DEF_PT_TIMEOUT 300 /* 300 seconds, 5 minutes */
static struct option long_options[] = {
{"bpw", required_argument, 0, 'b'},
{"dry-run", no_argument, 0, 'd'},
{"dry_run", no_argument, 0, 'd'},
{"help", no_argument, 0, 'h'},
{"id", required_argument, 0, 'i'},
{"in", required_argument, 0, 'I'},
{"length", required_argument, 0, 'l'},
{"mode", required_argument, 0, 'm'},
{"offset", required_argument, 0, 'o'},
{"read-stdin", no_argument, 0, 'r'},
{"read_stdin", no_argument, 0, 'r'},
{"raw", no_argument, 0, 'r'},
{"skip", required_argument, 0, 's'},
{"specific", required_argument, 0, 'S'},
{"timeout", required_argument, 0, 't' },
{"verbose", no_argument, 0, 'v'},
{"version", no_argument, 0, 'V'},
{0, 0, 0, 0},
};
static void
usage()
{
pr2serr("Usage: "
"sg_write_buffer [--bpw=CS] [--dry-run] [--help] [--id=ID] "
"[--in=FILE]\n"
" [--length=LEN] [--mode=MO] "
"[--offset=OFF]\n"
" [--read-stdin] [--skip=SKIP] "
"[--specific=MS]\n"
" [--timeout=TO] [--verbose] [--version] "
"DEVICE\n"
" where:\n"
" --bpw=CS|-b CS CS is chunk size: bytes per write "
"buffer\n"
" command (def: 0 -> as many as "
"possible)\n"
" --dry-run|-d skip WRITE BUFFER commands, do "
"everything else\n"
" --help|-h print out usage message then exit\n"
" --id=ID|-i ID buffer identifier (0 (default) to "
"255)\n"
" --in=FILE|-I FILE read from FILE ('-I -' read "
"from stdin)\n"
" --length=LEN|-l LEN length in bytes to write; may be "
"deduced from\n"
" FILE\n"
" --mode=MO|-m MO write buffer mode, MO is number or "
"acronym\n"
" (def: 0 -> 'combined header and "
"data' (obs))\n"
" --offset=OFF|-o OFF buffer offset (unit: bytes, def: 0)\n"
" --read-stdin|-r read from stdin (same as '-I -')\n"
" --skip=SKIP|-s SKIP bytes in file FILE to skip before "
"reading\n"
" --specific=MS|-S MS mode specific value; 3 bit field "
"(0 to 7)\n"
" --timeout=TO|-t TO command timeout in seconds (def: "
"300)\n"
" --verbose|-v increase verbosity\n"
" --version|-V print version string and exit\n\n"
"Performs one or more SCSI WRITE BUFFER commands. Use '-m xxx' "
"to list\navailable modes. A chunk size of 4 KB ('--bpw=4k') "
"seems to work well.\nExample: sg_write_buffer -b 4k -I xxx.lod "
"-m 7 /dev/sg3\n"
);
}
#define MODE_HEADER_DATA 0
#define MODE_VENDOR 1
#define MODE_DATA 2
#define MODE_DNLD_MC 4
#define MODE_DNLD_MC_SAVE 5
#define MODE_DNLD_MC_OFFS 6
#define MODE_DNLD_MC_OFFS_SAVE 7
#define MODE_ECHO_BUFFER 0x0A
#define MODE_DNLD_MC_EV_OFFS_DEFER 0x0D
#define MODE_DNLD_MC_OFFS_DEFER 0x0E
#define MODE_ACTIVATE_MC 0x0F
#define MODE_EN_EX_ECHO 0x1A
#define MODE_DIS_EX 0x1B
#define MODE_DNLD_ERR_HISTORY 0x1C
struct mode_s {
const char *mode_string;
int mode;
const char *comment;
};
static struct mode_s mode_arr[] = {
{"hd", MODE_HEADER_DATA, "combined header and data "
"(obsolete)"},
{"vendor", MODE_VENDOR, "vendor specific"},
{"data", MODE_DATA, "data"},
{"dmc", MODE_DNLD_MC, "download microcode and activate"},
{"dmc_save", MODE_DNLD_MC_SAVE, "download microcode, save and "
"activate"},
{"dmc_offs", MODE_DNLD_MC_OFFS, "download microcode with offsets "
"and activate"},
{"dmc_offs_save", MODE_DNLD_MC_OFFS_SAVE, "download microcode with "
"offsets, save and\n\t\t\t\tactivate"},
{"echo", MODE_ECHO_BUFFER, "write data to echo buffer"},
{"dmc_offs_ev_defer", MODE_DNLD_MC_EV_OFFS_DEFER, "download "
"microcode with offsets, select\n\t\t\t\tactivation event, "
"save and defer activation"},
{"dmc_offs_defer", MODE_DNLD_MC_OFFS_DEFER, "download microcode "
"with offsets, save and\n\t\t\t\tdefer activation"},
{"activate_mc", MODE_ACTIVATE_MC, "activate deferred microcode"},
{"en_ex", MODE_EN_EX_ECHO, "enable expander communications "
"protocol and\n\t\t\t\techo buffer (obsolete)"},
{"dis_ex", MODE_DIS_EX, "disable expander communications "
"protocol\n\t\t\t\t(obsolete)"},
{"deh", MODE_DNLD_ERR_HISTORY, "download application client "
"error history "},
{NULL, 0, NULL},
};
static void
print_modes(void)
{
const struct mode_s * mp;
pr2serr("The modes parameter argument can be numeric (hex or decimal)\n"
"or symbolic:\n");
for (mp = mode_arr; mp->mode_string; ++mp) {
pr2serr(" %2d (0x%02x) %-18s%s\n", mp->mode, mp->mode,
mp->mode_string, mp->comment);
}
pr2serr("\nAdditionally '--bpw=<val>,act' does a activate deferred "
"microcode after\nsuccessful dmc_offs_defer and "
"dmc_offs_ev_defer mode downloads.\n");
}
int
main(int argc, char * argv[])
{
bool bpw_then_activate = false;
bool dry_run = false;
bool got_stdin = false;
bool wb_len_given = false;
int sg_fd, infd, res, c, len, k, n;
int bpw = 0;
int do_help = 0;
int ret = 0;
int verbose = 0;
int wb_id = 0;
int wb_len = 0;
int wb_mode = 0;
int wb_offset = 0;
int wb_skip = 0;
int wb_timeout = DEF_PT_TIMEOUT;
int wb_mspec = 0;
const char * device_name = NULL;
const char * file_name = NULL;
unsigned char * dop = NULL;
char * cp;
const struct mode_s * mp;
char ebuff[EBUFF_SZ];
while (1) {
int option_index = 0;
c = getopt_long(argc, argv, "b:dhi:I:l:m:o:rs:S:t:vV", long_options,
&option_index);
if (c == -1)
break;
switch (c) {
case 'b':
bpw = sg_get_num(optarg);
if (bpw < 0) {
pr2serr("argument to '--bpw' should be in a positive "
"number\n");
return SG_LIB_SYNTAX_ERROR;
}
if ((cp = strchr(optarg, ','))) {
if (0 == strncmp("act", cp + 1, 3))
bpw_then_activate = true;
}
break;
case 'd':
dry_run = true;
break;
case 'h':
case '?':
++do_help;
break;
case 'i':
wb_id = sg_get_num(optarg);
if ((wb_id < 0) || (wb_id > 255)) {
pr2serr("argument to '--id' should be in the range 0 to "
"255\n");
return SG_LIB_SYNTAX_ERROR;
}
break;
case 'I':
file_name = optarg;
break;
case 'l':
wb_len = sg_get_num(optarg);
if (wb_len < 0) {
pr2serr("bad argument to '--length'\n");
return SG_LIB_SYNTAX_ERROR;
}
wb_len_given = true;
break;
case 'm':
if (isdigit(*optarg)) {
wb_mode = sg_get_num(optarg);
if ((wb_mode < 0) || (wb_mode > 31)) {
pr2serr("argument to '--mode' should be in the range 0 "
"to 31\n");
return SG_LIB_SYNTAX_ERROR;
}
} else {
len = strlen(optarg);
for (mp = mode_arr; mp->mode_string; ++mp) {
if (0 == strncmp(mp->mode_string, optarg, len)) {
wb_mode = mp->mode;
break;
}
}
if (! mp->mode_string) {
print_modes();
return SG_LIB_SYNTAX_ERROR;
}
}
break;
case 'o':
wb_offset = sg_get_num(optarg);
if (wb_offset < 0) {
pr2serr("bad argument to '--offset'\n");
return SG_LIB_SYNTAX_ERROR;
}
break;
case 'r': /* --read-stdin and --raw (previous name) */
file_name = "-";
break;
case 's':
wb_skip = sg_get_num(optarg);
if (wb_skip < 0) {
pr2serr("bad argument to '--skip'\n");
return SG_LIB_SYNTAX_ERROR;
}
break;
case 'S':
wb_mspec = sg_get_num(optarg);
if ((wb_mspec < 0) || (wb_mspec > 7)) {
pr2serr("expected argument to '--specific' to be 0 to 7\n");
return SG_LIB_SYNTAX_ERROR;
}
break;
case 't':
wb_timeout = sg_get_num(optarg);
if (wb_timeout < 0) {
pr2serr("Invalid argument to '--timeout'\n");
return SG_LIB_SYNTAX_ERROR;
}
break;
case 'v':
++verbose;
break;
case 'V':
pr2serr(ME "version: %s\n", version_str);
return 0;
default:
pr2serr("unrecognised option code 0x%x ??\n", c);
usage();
return SG_LIB_SYNTAX_ERROR;
}
}
if (do_help) {
if (do_help > 1) {
usage();
pr2serr("\n");
print_modes();
} else
usage();
return 0;
}
if (optind < argc) {
if (NULL == device_name) {
device_name = argv[optind];
++optind;
}
if (optind < argc) {
for (; optind < argc; ++optind)
pr2serr("Unexpected extra argument: %s\n", argv[optind]);
usage();
return SG_LIB_SYNTAX_ERROR;
}
}
if (NULL == device_name) {
pr2serr("missing device name!\n");
usage();
return SG_LIB_SYNTAX_ERROR;
}
if ((wb_len > 0) && (bpw > wb_len)) {
pr2serr("trim chunk size (CS) to be the same as LEN\n");
bpw = wb_len;
}
#ifdef SG_LIB_WIN32
#ifdef SG_LIB_WIN32_DIRECT
if (verbose > 4)
pr2serr("Initial win32 SPT interface state: %s\n",
scsi_pt_win32_spt_state() ? "direct" : "indirect");
scsi_pt_win32_direct(SG_LIB_WIN32_DIRECT /* SPT pt interface */);
#endif
#endif
sg_fd = sg_cmds_open_device(device_name, false /* rw */, verbose);
if (sg_fd < 0) {
pr2serr(ME "open error: %s: %s\n", device_name,
safe_strerror(-sg_fd));
return SG_LIB_FILE_ERROR;
}
if (file_name || (wb_len > 0)) {
if (0 == wb_len)
wb_len = DEF_XFER_LEN;
if (NULL == (dop = (unsigned char *)malloc(wb_len))) {
pr2serr(ME "out of memory\n");
ret = SG_LIB_SYNTAX_ERROR;
goto err_out;
}
memset(dop, 0xff, wb_len);
if (file_name) {
got_stdin = (0 == strcmp(file_name, "-"));
if (got_stdin) {
if (wb_skip > 0) {
pr2serr("Can't skip on stdin\n");
ret = SG_LIB_FILE_ERROR;
goto err_out;
}
infd = STDIN_FILENO;
} else {
if ((infd = open(file_name, O_RDONLY)) < 0) {
snprintf(ebuff, EBUFF_SZ,
ME "could not open %s for reading", file_name);
perror(ebuff);
ret = SG_LIB_FILE_ERROR;
goto err_out;
} else if (sg_set_binary_mode(infd) < 0)
perror("sg_set_binary_mode");
if (wb_skip > 0) {
if (lseek(infd, wb_skip, SEEK_SET) < 0) {
snprintf(ebuff, EBUFF_SZ, ME "couldn't skip to "
"required position on %s", file_name);
perror(ebuff);
close(infd);
ret = SG_LIB_FILE_ERROR;
goto err_out;
}
}
}
res = read(infd, dop, wb_len);
if (res < 0) {
snprintf(ebuff, EBUFF_SZ, ME "couldn't read from %s",
file_name);
perror(ebuff);
if (! got_stdin)
close(infd);
ret = SG_LIB_FILE_ERROR;
goto err_out;
}
if (res < wb_len) {
if (wb_len_given) {
pr2serr("tried to read %d bytes from %s, got %d bytes\n",
wb_len, file_name, res);
pr2serr("pad with 0xff bytes and continue\n");
} else {
if (verbose) {
pr2serr("tried to read %d bytes from %s, got %d "
"bytes\n", wb_len, file_name, res);
pr2serr("will write %d bytes", res);
if ((bpw > 0) && (bpw < wb_len))
pr2serr(", %d bytes per WRITE BUFFER command\n",
bpw);
else
pr2serr("\n");
}
wb_len = res;
}
}
if (! got_stdin)
close(infd);
}
}
res = 0;
if (bpw > 0) {
for (k = 0; k < wb_len; k += n) {
n = wb_len - k;
if (n > bpw)
n = bpw;
if (verbose)
pr2serr("sending write buffer, mode=0x%x, mspec=%d, id=%d, "
" offset=%d, len=%d\n", wb_mode, wb_mspec, wb_id,
wb_offset + k, n);
if (dry_run) {
if (verbose)
pr2serr("skipping WRITE BUFFER command due to "
"--dry-run\n");
res = 0;
} else
res = sg_ll_write_buffer_v2(sg_fd, wb_mode, wb_mspec, wb_id,
wb_offset + k, dop + k, n,
wb_timeout, true, verbose);
if (res)
break;
}
if (bpw_then_activate) {
if (verbose)
pr2serr("sending Activate deferred microcode [0xf]\n");
if (dry_run) {
if (verbose)
pr2serr("skipping WRITE BUFFER(ACTIVATE) command due to "
"--dry-run\n");
res = 0;
} else
res = sg_ll_write_buffer_v2(sg_fd, MODE_ACTIVATE_MC,
0 /* buffer_id */,
0 /* buffer_offset */, 0,
NULL, 0, wb_timeout, true,
verbose);
}
} else {
if (verbose)
pr2serr("sending single write buffer, mode=0x%x, mpsec=%d, "
"id=%d, offset=%d, len=%d\n", wb_mode, wb_mspec, wb_id,
wb_offset, wb_len);
if (dry_run) {
if (verbose)
pr2serr("skipping WRITE BUFFER(all in one) command due to "
"--dry-run\n");
res = 0;
} else
res = sg_ll_write_buffer_v2(sg_fd, wb_mode, wb_mspec, wb_id,
wb_offset, dop, wb_len, wb_timeout,
true, verbose);
}
if (0 != res) {
char b[80];
ret = res;
sg_get_category_sense_str(res, sizeof(b), b, verbose);
pr2serr("Write buffer failed: %s\n", b);
}
err_out:
if (dop)
free(dop);
res = sg_cmds_close_device(sg_fd);
if (res < 0) {
pr2serr("close error: %s\n", safe_strerror(-res));
if (0 == ret)
return SG_LIB_FILE_ERROR;
}
return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
}