7655 lines
190 KiB
C
7655 lines
190 KiB
C
|
|
/* Test code for D. Gilbert's extensions to the Linux OS SCSI generic ("sg")
|
||
|
|
device driver.
|
||
|
|
* Copyright (C) 1999 - 2002 D. Gilbert
|
||
|
|
* This program is free software; you can redistribute it and/or modify
|
||
|
|
* it under the terms of the GNU General Public License as published by
|
||
|
|
* the Free Software Foundation; either version 2, or (at your option)
|
||
|
|
* any later version.
|
||
|
|
|
||
|
|
This program scans the "sg" device space (ie actual + simulated SCSI
|
||
|
|
generic devices).
|
||
|
|
Options: -w open writable (new driver opens readable unless -i)
|
||
|
|
-n numeric scan: scan /dev/sg0,1,2, ....
|
||
|
|
-a alpha scan: scan /dev/sga,b,c, ....
|
||
|
|
-i do SCSI inquiry on device (implies -w)
|
||
|
|
-x extra information output
|
||
|
|
|
||
|
|
By default this program will look for /dev/sg0 first (i.e. numeric scan)
|
||
|
|
|
||
|
|
Note: This program is written to work under both the original and
|
||
|
|
the new sg driver.
|
||
|
|
|
||
|
|
Version 1.00 20031022
|
||
|
|
|
||
|
|
F. Jansen - modification to extend beyond 26 sg devices.
|
||
|
|
M. Ridgeway - Roll code together for SCSI testing with one command line
|
||
|
|
|
||
|
|
6 byte INQUIRY command:
|
||
|
|
[0x12][ |lu][pg cde][res ][al len][cntrl ]
|
||
|
|
*/
|
||
|
|
|
||
|
|
#include <unistd.h>
|
||
|
|
#include <fcntl.h>
|
||
|
|
#include <stdio.h>
|
||
|
|
#include <stdlib.h>
|
||
|
|
#include <string.h>
|
||
|
|
#include <errno.h>
|
||
|
|
#include <dirent.h>
|
||
|
|
#include <limits.h>
|
||
|
|
#include <time.h>
|
||
|
|
#include <sys/ioctl.h>
|
||
|
|
#include <sys/types.h>
|
||
|
|
#include <sys/stat.h>
|
||
|
|
#include <signal.h>
|
||
|
|
#include <ctype.h>
|
||
|
|
#include <pthread.h>
|
||
|
|
#include <sys/sysmacros.h>
|
||
|
|
#include <sys/time.h>
|
||
|
|
#include <sys/mman.h>
|
||
|
|
#include <linux/major.h>
|
||
|
|
#include "sg_include.h"
|
||
|
|
#include "sg_err.h"
|
||
|
|
#include "llseek.h"
|
||
|
|
|
||
|
|
#define ME "scsimain: "
|
||
|
|
|
||
|
|
#ifndef O_DIRECT
|
||
|
|
#define O_DIRECT 040000
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#define NUMERIC_SCAN_DEF 1 /* change to 0 to make alpha scan default */
|
||
|
|
//static char * version_str = "0.21 20030513";
|
||
|
|
|
||
|
|
#define BPI (signed)(sizeof(int))
|
||
|
|
#define READWRITE_BASE_NUM 0x12345678
|
||
|
|
#define DEF_BLOCK_SIZE 512
|
||
|
|
#define DEF_NUM_THREADS 16
|
||
|
|
#define MAX_NUM_THREADS SG_MAX_QUEUE
|
||
|
|
#define DEF_BLOCKS_PER_TRANSFER 128
|
||
|
|
#define DEF_SCSI_CDBSZ 10
|
||
|
|
#define MAX_SCSI_CDBSZ 16
|
||
|
|
#define TUR_CMD_LEN 6
|
||
|
|
#define DEVNAME_SZ 256
|
||
|
|
#define MAX_HOLES 4
|
||
|
|
|
||
|
|
#define OFF sizeof(struct sg_header)
|
||
|
|
#define INQ_REPLY_LEN 96 /* logic assumes >= sizeof(inqCmdBlk) */
|
||
|
|
#define INQUIRY_CMDLEN 6
|
||
|
|
#define INQUIRY_CMD 0x12
|
||
|
|
#define SENSE_BUFF_LEN 32 /* Arbitrary, could be larger */
|
||
|
|
#define DEF_TIMEOUT 60000 /* 60,000 millisecs == 60 seconds */
|
||
|
|
#define REASON_SZ 128
|
||
|
|
|
||
|
|
#define SENSE_BUFF_SZ 64
|
||
|
|
#define RCAP_REPLY_LEN 8
|
||
|
|
#define LOG_SENSE_CMD 0x4d
|
||
|
|
#define LOG_SENSE_CMDLEN 10
|
||
|
|
#define MX_ALLOC_LEN (1024 * 17)
|
||
|
|
#define D_ROOT_SZ 512
|
||
|
|
#define STR_SZ 1024
|
||
|
|
#define INOUTF_SZ 512
|
||
|
|
#define EBUFF_SZ 512
|
||
|
|
#define MDEV_NAME_SZ 256
|
||
|
|
|
||
|
|
#define PG_CODE_ALL 0x00
|
||
|
|
|
||
|
|
#define TRUE 1
|
||
|
|
#define FALSE 0
|
||
|
|
#define MAX_DEVICES 50
|
||
|
|
|
||
|
|
#define NAME_LEN_MAX 256
|
||
|
|
#define LEVELS 4
|
||
|
|
|
||
|
|
#define SENSE_BUFF_LEN 32 /* Arbitrary, could be larger */
|
||
|
|
#define INQ_ALLOC_LEN 255
|
||
|
|
|
||
|
|
#ifndef SCSI_IOCTL_GET_PCI
|
||
|
|
#define SCSI_IOCTL_GET_PCI 0x5387
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#define READ_CAP_REPLY_LEN 8
|
||
|
|
|
||
|
|
#ifndef RAW_MAJOR
|
||
|
|
#define RAW_MAJOR 255 /*unlikey value */
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#define FT_OTHER 1 /* filetype is probably normal */
|
||
|
|
#define FT_SG 2 /* filetype is sg char device or supports
|
||
|
|
SG_IO ioctl */
|
||
|
|
#define FT_RAW 4 /* filetype is raw char device */
|
||
|
|
#define FT_DEV_NULL 8 /* either "/dev/null" or "." as filename */
|
||
|
|
#define FT_ST 16 /* filetype is st char device (tape) */
|
||
|
|
#define FT_BLOCK 32 /* filetype is block device */
|
||
|
|
|
||
|
|
#define DEV_NULL_MINOR_NUM 3
|
||
|
|
|
||
|
|
#ifdef SG_GET_RESERVED_SIZE
|
||
|
|
#define OPEN_FLAG O_RDONLY
|
||
|
|
#else
|
||
|
|
#define OPEN_FLAG O_RDWR
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#ifndef SG_MAX_SENSE
|
||
|
|
#define SG_MAX_SENSE 16
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#define TEST_START 0
|
||
|
|
#define TEST_BREAK 1
|
||
|
|
#define TEST_STOP 2
|
||
|
|
#define MAX_SG_DEVS 128
|
||
|
|
#define MAX_SD_DEVS 128
|
||
|
|
#define MAX_SR_DEVS 128
|
||
|
|
#define MAX_ST_DEVS 128
|
||
|
|
#define MAX_OSST_DEVS 128
|
||
|
|
#define MAX_ERRORS 5
|
||
|
|
|
||
|
|
#define LIN_DEV_TYPE_UNKNOWN 0
|
||
|
|
#define LIN_DEV_TYPE_SD 1
|
||
|
|
#define LIN_DEV_TYPE_SR 2
|
||
|
|
#define LIN_DEV_TYPE_ST 3
|
||
|
|
#define LIN_DEV_TYPE_SCD 4
|
||
|
|
#define LIN_DEV_TYPE_OSST 5
|
||
|
|
|
||
|
|
#define MODE_SENSE6_CMD 0x1a
|
||
|
|
#define MODE_SENSE6_CMDLEN 6
|
||
|
|
#define MODE_SENSE10_CMD 0x5a
|
||
|
|
#define MODE_SENSE10_CMDLEN 10
|
||
|
|
#define INQUIRY_CMD 0x12
|
||
|
|
#define INQUIRY_CMDLEN 6
|
||
|
|
#define MODE_ALLOC_LEN (1024 * 4)
|
||
|
|
|
||
|
|
#define MODE_CODE_ALL 0x3f
|
||
|
|
|
||
|
|
#define RB_MODE_DESC 3
|
||
|
|
#define RB_MODE_DATA 2
|
||
|
|
#define RB_DESC_LEN 4
|
||
|
|
#define RB_MB_TO_READ 200
|
||
|
|
#define RB_OPCODE 0x3C
|
||
|
|
#define RB_CMD_LEN 10
|
||
|
|
|
||
|
|
/* #define SG_DEBUG */
|
||
|
|
|
||
|
|
#ifndef SG_FLAG_MMAP_IO
|
||
|
|
#define SG_FLAG_MMAP_IO 4
|
||
|
|
#endif
|
||
|
|
#ifndef SG_SCSI_RESET
|
||
|
|
#define SG_SCSI_RESET 0x2284
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#ifndef SG_SCSI_RESET_NOTHING
|
||
|
|
#define SG_SCSI_RESET_NOTHING 0
|
||
|
|
#define SG_SCSI_RESET_DEVICE 1
|
||
|
|
#define SG_SCSI_RESET_BUS 2
|
||
|
|
#define SG_SCSI_RESET_HOST 3
|
||
|
|
#endif
|
||
|
|
#define LONG_TIMEOUT 2400000 /* 2,400,000 millisecs == 40 minutes */
|
||
|
|
|
||
|
|
#define SEND_DIAGNOSTIC_CMD 0x1d
|
||
|
|
#define SEND_DIAGNOSTIC_CMDLEN 6
|
||
|
|
#define RECEIVE_DIAGNOSTIC_CMD 0x1c
|
||
|
|
#define RECEIVE_DIAGNOSTIC_CMDLEN 6
|
||
|
|
|
||
|
|
#define START_STOP 0x1b
|
||
|
|
#define SYNCHRONIZE_CACHE 0x35
|
||
|
|
|
||
|
|
#define DEF_START_TIMEOUT 120000 /* 120,000 millisecs == 2 minutes */
|
||
|
|
|
||
|
|
#define DEVICE_RESET 0
|
||
|
|
#define HOST_RESET 1
|
||
|
|
#define BUS_RESET 2
|
||
|
|
#define SG_HSZ sizeof(struct sg_header)
|
||
|
|
#define OFFSET_HEADER (SG_HSZ - (2 * sizeof(int)))
|
||
|
|
#define SIZEOF_BUFFER (256*1024)
|
||
|
|
#define SIZEOF_BUFFER1 (16*1024)
|
||
|
|
#define MAXPARM 32
|
||
|
|
|
||
|
|
#define SETUP_MODE_PAGE(NPAGE, NPARAM) \
|
||
|
|
status = get_mode_page(NPAGE, page_code); \
|
||
|
|
if (status) { printf("\n"); return status; } \
|
||
|
|
bdlen = buffer[11]; \
|
||
|
|
pagestart = buffer + 12 + bdlen;
|
||
|
|
|
||
|
|
typedef struct request_collection { /* one instance visible to all threads */
|
||
|
|
int infd;
|
||
|
|
int skip;
|
||
|
|
int in_type;
|
||
|
|
int in_scsi_type;
|
||
|
|
int in_blk; /* -\ next block address to read */
|
||
|
|
int in_count; /* | blocks remaining for next read */
|
||
|
|
int in_done_count; /* | count of completed in blocks */
|
||
|
|
int in_partial; /* | */
|
||
|
|
int in_stop; /* | */
|
||
|
|
pthread_mutex_t in_mutex; /* -/ */
|
||
|
|
int outfd;
|
||
|
|
int seek;
|
||
|
|
int out_type;
|
||
|
|
int out_scsi_type;
|
||
|
|
int out_blk; /* -\ next block address to write */
|
||
|
|
int out_count; /* | blocks remaining for next write */
|
||
|
|
int out_done_count; /* | count of completed out blocks */
|
||
|
|
int out_partial; /* | */
|
||
|
|
int out_stop; /* | */
|
||
|
|
pthread_mutex_t out_mutex; /* | */
|
||
|
|
pthread_cond_t out_sync_cv; /* -/ hold writes until "in order" */
|
||
|
|
int bs;
|
||
|
|
int bpt;
|
||
|
|
int fua_mode;
|
||
|
|
int dio;
|
||
|
|
int dio_incomplete; /* -\ */
|
||
|
|
int sum_of_resids; /* | */
|
||
|
|
pthread_mutex_t aux_mutex; /* -/ (also serializes some printf()s */
|
||
|
|
int coe;
|
||
|
|
int cdbsz;
|
||
|
|
int debug;
|
||
|
|
} Rq_coll;
|
||
|
|
|
||
|
|
typedef struct request_element { /* one instance per worker thread */
|
||
|
|
int infd;
|
||
|
|
int outfd;
|
||
|
|
int wr;
|
||
|
|
int blk;
|
||
|
|
int num_blks;
|
||
|
|
unsigned char *buffp;
|
||
|
|
unsigned char *alloc_bp;
|
||
|
|
sg_io_hdr_t io_hdr;
|
||
|
|
unsigned char cmd[MAX_SCSI_CDBSZ];
|
||
|
|
unsigned char sb[SENSE_BUFF_LEN];
|
||
|
|
int bs;
|
||
|
|
int fua_mode;
|
||
|
|
int dio;
|
||
|
|
int dio_incomplete;
|
||
|
|
int resid;
|
||
|
|
int in_scsi_type;
|
||
|
|
int out_scsi_type;
|
||
|
|
int cdbsz;
|
||
|
|
int debug;
|
||
|
|
} Rq_elem;
|
||
|
|
|
||
|
|
typedef struct my_map_info {
|
||
|
|
int active;
|
||
|
|
int lin_dev_type;
|
||
|
|
int oth_dev_num;
|
||
|
|
struct sg_scsi_id sg_dat;
|
||
|
|
char vendor[8];
|
||
|
|
char product[16];
|
||
|
|
char revision[4];
|
||
|
|
} my_map_info_t;
|
||
|
|
|
||
|
|
typedef struct sg_map {
|
||
|
|
int bus;
|
||
|
|
int channel;
|
||
|
|
int target_id;
|
||
|
|
int lun;
|
||
|
|
char *dev_name;
|
||
|
|
} Sg_map;
|
||
|
|
|
||
|
|
typedef struct my_scsi_idlun {
|
||
|
|
/* why can't userland see this structure ??? */
|
||
|
|
int dev_id;
|
||
|
|
int host_unique_id;
|
||
|
|
} My_scsi_idlun;
|
||
|
|
|
||
|
|
struct page_code_desc {
|
||
|
|
int page_code;
|
||
|
|
const char *desc;
|
||
|
|
};
|
||
|
|
|
||
|
|
static const char *pg_control_str_arr[] = {
|
||
|
|
"current",
|
||
|
|
"changeable",
|
||
|
|
"default",
|
||
|
|
"saved"
|
||
|
|
};
|
||
|
|
|
||
|
|
char *devices[] =
|
||
|
|
{ "/dev/sda", "/dev/sdb", "/dev/sdc", "/dev/sdd", "/dev/sde", "/dev/sdf",
|
||
|
|
"/dev/sdg", "/dev/sdh", "/dev/sdi", "/dev/sdj", "/dev/sdk", "/dev/sdl",
|
||
|
|
"/dev/sdm", "/dev/sdn", "/dev/sdo", "/dev/sdp", "/dev/sdq", "/dev/sdr",
|
||
|
|
"/dev/sds", "/dev/sdt", "/dev/sdu", "/dev/sdv", "/dev/sdw", "/dev/sdx",
|
||
|
|
"/dev/sdy", "/dev/sdz", "/dev/sdaa", "/dev/sdab", "/dev/sdac",
|
||
|
|
"/dev/sdad",
|
||
|
|
"/dev/scd0", "/dev/scd1", "/dev/scd2", "/dev/scd3", "/dev/scd4",
|
||
|
|
"/dev/scd5",
|
||
|
|
"/dev/scd6", "/dev/scd7", "/dev/scd8", "/dev/scd9", "/dev/scd10",
|
||
|
|
"/dev/scd11",
|
||
|
|
"/dev/sr0", "/dev/sr1", "/dev/sr2", "/dev/sr3", "/dev/sr4", "/dev/sr5",
|
||
|
|
"/dev/sr6", "/dev/sr7", "/dev/sr8", "/dev/sr9", "/dev/sr10",
|
||
|
|
"/dev/sr11",
|
||
|
|
"/dev/nst0", "/dev/nst1", "/dev/nst2", "/dev/nst3", "/dev/nst4",
|
||
|
|
"/dev/nst5",
|
||
|
|
"/dev/nosst0", "/dev/nosst1", "/dev/nosst2", "/dev/nosst3",
|
||
|
|
"/dev/nosst4"
|
||
|
|
};
|
||
|
|
|
||
|
|
static char *page_names[] = {
|
||
|
|
NULL,
|
||
|
|
"Read-Write Error Recovery",
|
||
|
|
"Disconnect-Reconnect",
|
||
|
|
"Format Device",
|
||
|
|
"Rigid Disk Geometry",
|
||
|
|
/* "Flexible Disk" */ NULL,
|
||
|
|
NULL,
|
||
|
|
"Verify Error Recovery",
|
||
|
|
"Caching",
|
||
|
|
"Peripheral Device",
|
||
|
|
"Control Mode",
|
||
|
|
/* "Medium Types Supported" */ NULL,
|
||
|
|
"Notch and Partition",
|
||
|
|
/* "CD-ROM" */ NULL,
|
||
|
|
/* "CD-ROM Audio Control" */ NULL,
|
||
|
|
NULL,
|
||
|
|
/* "Medium Partition (1)" */ NULL,
|
||
|
|
/* "Medium Partition (2)" */ NULL,
|
||
|
|
/* "Medium Partition (3)" */ NULL,
|
||
|
|
/* "Medium Partition (4)" */ NULL
|
||
|
|
};
|
||
|
|
|
||
|
|
#define MAX_PAGENO (sizeof(page_names)/sizeof(char *))
|
||
|
|
|
||
|
|
/* Following 2 macros from D.R. Butenhof's POSIX threads book:
|
||
|
|
ISBN 0-201-63392-2 . [Highly recommended book.] */
|
||
|
|
#define err_exit(code,text) do { \
|
||
|
|
fprintf(stderr, "%s at \"%s\":%d: %s\n", \
|
||
|
|
text, __FILE__, __LINE__, strerror(code)); \
|
||
|
|
exit(1); \
|
||
|
|
} while (0)
|
||
|
|
|
||
|
|
static Sg_map sg_map_arr[(sizeof(devices) / sizeof(char *)) + 1];
|
||
|
|
|
||
|
|
static const unsigned char scsi_command_size[8] = { 6, 10, 10, 12,
|
||
|
|
12, 12, 10, 10
|
||
|
|
};
|
||
|
|
const unsigned char rbCmdBlk[10] = { READ_BUFFER, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||
|
|
static const char *level_arr[LEVELS] = { "host", "bus", "target", "lun" };
|
||
|
|
|
||
|
|
static const char *proc_allow_dio = "/proc/scsi/sg/allow_dio";
|
||
|
|
static const char *devfs_id = "/dev/.devfsd";
|
||
|
|
static my_map_info_t map_arr[MAX_SG_DEVS];
|
||
|
|
static char ebuff[EBUFF_SZ];
|
||
|
|
static int glob_fd;
|
||
|
|
static char defectformat = 0x4;
|
||
|
|
static sigset_t signal_set;
|
||
|
|
static pthread_t sig_listen_thread_id;
|
||
|
|
|
||
|
|
static int do_ide = 0;
|
||
|
|
static int do_inq = 1;
|
||
|
|
static int do_leaf = 1;
|
||
|
|
static int do_extra = 1;
|
||
|
|
static int do_quiet = 0;
|
||
|
|
static int checked_sg = 1;
|
||
|
|
static int sum_of_resids = 0;
|
||
|
|
|
||
|
|
static int dd_count = -1;
|
||
|
|
static int in_full = 0;
|
||
|
|
static int in_partial = 0;
|
||
|
|
static int out_full = 0;
|
||
|
|
static int out_partial = 0;
|
||
|
|
static int do_coe = 0;
|
||
|
|
int base = READWRITE_BASE_NUM;
|
||
|
|
unsigned char *cmpbuf = 0;
|
||
|
|
static unsigned char buff_a[SIZEOF_BUFFER + SG_HSZ + 12];
|
||
|
|
static unsigned char *buffer = buff_a + OFFSET_HEADER;
|
||
|
|
|
||
|
|
typedef struct my_sg_scsi_id {
|
||
|
|
int host_no; /* as in "scsi<n>" where 'n' is one of 0, 1, 2 etc */
|
||
|
|
int channel;
|
||
|
|
int scsi_id; /* scsi id of target device */
|
||
|
|
int lun;
|
||
|
|
int scsi_type; /* TYPE_... defined in scsi/scsi.h */
|
||
|
|
short h_cmd_per_lun; /* host (adapter) maximum commands per lun */
|
||
|
|
short d_queue_depth; /* device (or adapter) maximum queue length */
|
||
|
|
int unused1; /* probably find a good use, set 0 for now */
|
||
|
|
int unused2; /* ditto */
|
||
|
|
} My_sg_scsi_id;
|
||
|
|
|
||
|
|
// Prototypes
|
||
|
|
int do_scsi_sgp_read_write(char *device);
|
||
|
|
int do_scsi_sgm_read_write(char *device);
|
||
|
|
void sg_in_operation(Rq_coll * clp, Rq_elem * rep);
|
||
|
|
void sg_out_operation(Rq_coll * clp, Rq_elem * rep);
|
||
|
|
int normal_in_operation(Rq_coll * clp, Rq_elem * rep, int blocks);
|
||
|
|
void normal_out_operation(Rq_coll * clp, Rq_elem * rep, int blocks);
|
||
|
|
int sg_start_io(Rq_elem * rep);
|
||
|
|
int sg_finish_io(int wr, Rq_elem * rep, pthread_mutex_t * a_mutp);
|
||
|
|
int run_sg_scan_tests(void);
|
||
|
|
int show_scsi_logs(char *device);
|
||
|
|
int validate_device(char *device);
|
||
|
|
int show_devfs_devices(void);
|
||
|
|
void usage(void);
|
||
|
|
int do_scsi_device_read_write(char *device);
|
||
|
|
int do_scsi_inquiry(char *device, int hex_flag);
|
||
|
|
int show_scsi_maps(void);
|
||
|
|
int show_scsi_modes(char *device);
|
||
|
|
int do_scsi_read_buffer(char *device);
|
||
|
|
int show_scsi_read_capacity(char *device);
|
||
|
|
int do_scsi_reset_devices(char *device, int reset_opts);
|
||
|
|
int do_scsi_send_diagnostics(char *device);
|
||
|
|
int do_scsi_start_stop(char *device, int startstop);
|
||
|
|
int do_scsi_read_write_buffer(char *device);
|
||
|
|
int do_scsi_test_unit_ready(char *device);
|
||
|
|
int show_scsi_info(char *device);
|
||
|
|
void print_msg(int msg_num, const char *msg);
|
||
|
|
static void scan_dev_type(const char *leadin, int max_dev, int do_numeric,
|
||
|
|
int lin_dev_type, int last_sg_ind);
|
||
|
|
|
||
|
|
#ifdef SG_IO
|
||
|
|
int sg3_inq(int sg_fd, unsigned char *inqBuff, int do_extra);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
static unsigned char inqCmdBlk[INQUIRY_CMDLEN] =
|
||
|
|
{ 0x12, 0, 0, 0, INQ_REPLY_LEN, 0 };
|
||
|
|
|
||
|
|
void print_msg(int msg_num, const char *msg)
|
||
|
|
{
|
||
|
|
switch (msg_num) {
|
||
|
|
case TEST_START:
|
||
|
|
printf
|
||
|
|
("\n****************** Starting Tests ***************************\n");
|
||
|
|
break;
|
||
|
|
case TEST_STOP:
|
||
|
|
printf
|
||
|
|
("\n****************** Tests Complete ***************************\n");
|
||
|
|
break;
|
||
|
|
case TEST_BREAK:
|
||
|
|
printf("\n------------------ %s Test ------------------\n\n",
|
||
|
|
msg);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
int main(int argc, char *argv[])
|
||
|
|
{
|
||
|
|
int rc = 0;
|
||
|
|
|
||
|
|
if (argc < 2) {
|
||
|
|
printf("\n\nERROR:No device passed to test\n\n");
|
||
|
|
usage();
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
rc = validate_device(argv[1]);
|
||
|
|
if (rc == 0) {
|
||
|
|
|
||
|
|
print_msg(TEST_START, NULL);
|
||
|
|
|
||
|
|
rc = run_sg_scan_tests();
|
||
|
|
if (rc != 0) {
|
||
|
|
printf("ERROR: run_sg_scan_tests failed %d\n", rc);
|
||
|
|
}
|
||
|
|
|
||
|
|
rc = show_scsi_logs(argv[1]);
|
||
|
|
if (rc != 0) {
|
||
|
|
printf("ERROR: show_scsi_logs failed %d\n", rc);
|
||
|
|
}
|
||
|
|
|
||
|
|
rc = show_devfs_devices();
|
||
|
|
if (rc != 0) {
|
||
|
|
printf("ERROR: show_devfs_devices failed %d\n", rc);
|
||
|
|
}
|
||
|
|
|
||
|
|
rc = do_scsi_device_read_write(argv[1]);
|
||
|
|
if (rc != 0) {
|
||
|
|
printf("ERROR: do_scsi_devices_read_write failed %d\n",
|
||
|
|
rc);
|
||
|
|
}
|
||
|
|
|
||
|
|
rc = do_scsi_inquiry(argv[1], TRUE);
|
||
|
|
if (rc != 0) {
|
||
|
|
printf("ERROR: do_scsi_inquiry HEX failed %d\n", rc);
|
||
|
|
} else {
|
||
|
|
rc = do_scsi_inquiry(argv[1], FALSE);
|
||
|
|
if (rc != 0) {
|
||
|
|
printf("ERROR: do_scsi_inquiry PCI failed %d\n",
|
||
|
|
rc);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
rc = show_scsi_maps();
|
||
|
|
if (rc != 0) {
|
||
|
|
printf("ERROR: show_scsi_maps failed %d\n", rc);
|
||
|
|
}
|
||
|
|
|
||
|
|
rc = show_scsi_modes(argv[1]);
|
||
|
|
if (rc != 0) {
|
||
|
|
printf("ERROR: show_scsi_modes failed %d\n", rc);
|
||
|
|
}
|
||
|
|
|
||
|
|
rc = do_scsi_read_buffer(argv[1]);
|
||
|
|
if (rc != 0 && rc != 1) {
|
||
|
|
printf("ERROR: do_scsi_read_buffer failed %d\n", rc);
|
||
|
|
}
|
||
|
|
|
||
|
|
rc = show_scsi_read_capacity(argv[1]);
|
||
|
|
if (rc != 0) {
|
||
|
|
printf("ERROR: show_scsi_read_capacity failed %d\n",
|
||
|
|
rc);
|
||
|
|
}
|
||
|
|
|
||
|
|
rc |= do_scsi_reset_devices(argv[1], DEVICE_RESET);
|
||
|
|
rc |= do_scsi_reset_devices(argv[1], BUS_RESET);
|
||
|
|
rc |= do_scsi_reset_devices(argv[1], HOST_RESET);
|
||
|
|
if (rc != 0) {
|
||
|
|
printf("ERROR: do_scsi_reset_devices failed %d\n", rc);
|
||
|
|
}
|
||
|
|
|
||
|
|
rc = do_scsi_send_diagnostics(argv[1]);
|
||
|
|
if (rc != 0) {
|
||
|
|
printf("ERROR: do_scsi_send_diagnostics failed %d\n",
|
||
|
|
rc);
|
||
|
|
}
|
||
|
|
|
||
|
|
rc |= do_scsi_start_stop(argv[1], FALSE);
|
||
|
|
rc |= do_scsi_start_stop(argv[1], TRUE);
|
||
|
|
if (rc != 0) {
|
||
|
|
printf("ERROR: do_scsi_start_top failed %d\n", rc);
|
||
|
|
}
|
||
|
|
|
||
|
|
rc = do_scsi_read_write_buffer(argv[1]);
|
||
|
|
if (rc != 0 && rc != 1) {
|
||
|
|
printf("ERROR: do_scsi_read_write_buffer failed %d\n",
|
||
|
|
rc);
|
||
|
|
}
|
||
|
|
|
||
|
|
rc = do_scsi_test_unit_ready(argv[1]);
|
||
|
|
if (rc != 0) {
|
||
|
|
printf("ERROR: do_scsi_test_unit_ready failed %d\n",
|
||
|
|
rc);
|
||
|
|
}
|
||
|
|
|
||
|
|
rc = show_scsi_info(argv[1]);
|
||
|
|
if (rc != 0) {
|
||
|
|
printf("ERROR: show_scsi_info failed %d\n", rc);
|
||
|
|
}
|
||
|
|
|
||
|
|
rc = do_scsi_sgp_read_write(argv[1]);
|
||
|
|
if (rc != 0) {
|
||
|
|
printf("ERROR: do_scsi_sgp_read_write failed %d\n", rc);
|
||
|
|
}
|
||
|
|
|
||
|
|
rc = do_scsi_sgm_read_write(argv[1]);
|
||
|
|
if (rc != 0) {
|
||
|
|
printf("ERROR: do_scsi_sgm_read_write failed %d\n", rc);
|
||
|
|
}
|
||
|
|
|
||
|
|
print_msg(TEST_STOP, NULL);
|
||
|
|
} else {
|
||
|
|
printf("\nERROR: Invalid device passed to test\n\n\n");
|
||
|
|
usage();
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
int validate_device(char *device)
|
||
|
|
{
|
||
|
|
int rc = 0;
|
||
|
|
int i, found = FALSE;
|
||
|
|
char device_string[25];
|
||
|
|
|
||
|
|
for (i = 0; i < MAX_DEVICES && !found; i++) {
|
||
|
|
sprintf(device_string, "/dev/sg%d", i);
|
||
|
|
//printf("checking %s \n", device_string);
|
||
|
|
if (strcmp(device, device_string) == 0) {
|
||
|
|
found = TRUE;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return rc;
|
||
|
|
}
|
||
|
|
|
||
|
|
void usage()
|
||
|
|
{
|
||
|
|
printf("Usage: 'sg_scan [-a] [-n] [-w] [-i] [-x]'\n");
|
||
|
|
printf(" where: -a do alpha scan (ie sga, sgb, sgc)\n");
|
||
|
|
printf(" -n do numeric scan (ie sg0, sg1...) [default]\n");
|
||
|
|
printf(" -w force open with read/write flag\n");
|
||
|
|
printf(" -i do SCSI INQUIRY, output results\n");
|
||
|
|
printf(" -x extra information output about queuing\n\n\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
void make_dev_name(char *fname, const char *leadin, int k, int do_numeric)
|
||
|
|
{
|
||
|
|
char buff[64];
|
||
|
|
int big, little;
|
||
|
|
|
||
|
|
strcpy(fname, leadin ? leadin : "/dev/sg");
|
||
|
|
if (do_numeric) {
|
||
|
|
sprintf(buff, "%d", k);
|
||
|
|
strcat(fname, buff);
|
||
|
|
} else {
|
||
|
|
if (k < 26) {
|
||
|
|
buff[0] = 'a' + (char)k;
|
||
|
|
buff[1] = '\0';
|
||
|
|
strcat(fname, buff);
|
||
|
|
} else if (k <= 255) { /* assumes sequence goes x,y,z,aa,ab,ac etc */
|
||
|
|
big = k / 26;
|
||
|
|
little = k - (26 * big);
|
||
|
|
big = big - 1;
|
||
|
|
|
||
|
|
buff[0] = 'a' + (char)big;
|
||
|
|
buff[1] = 'a' + (char)little;
|
||
|
|
buff[2] = '\0';
|
||
|
|
strcat(fname, buff);
|
||
|
|
} else
|
||
|
|
strcat(fname, "xxxx");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
int run_sg_scan_tests()
|
||
|
|
{
|
||
|
|
int sg_fd, res, k, f;
|
||
|
|
unsigned char inqBuff[OFF + INQ_REPLY_LEN];
|
||
|
|
int inqInLen = OFF + sizeof(inqCmdBlk);
|
||
|
|
int inqOutLen = OFF + INQ_REPLY_LEN;
|
||
|
|
unsigned char *buffp = inqBuff + OFF;
|
||
|
|
struct sg_header *isghp = (struct sg_header *)inqBuff;
|
||
|
|
int do_numeric = NUMERIC_SCAN_DEF;
|
||
|
|
int do_inquiry = 0;
|
||
|
|
int do_extra = 1;
|
||
|
|
int writeable = 0;
|
||
|
|
int num_errors = 0;
|
||
|
|
int num_silent = 0;
|
||
|
|
int eacces_err = 0;
|
||
|
|
char fname[64];
|
||
|
|
My_scsi_idlun my_idlun;
|
||
|
|
int host_no;
|
||
|
|
int flags;
|
||
|
|
int emul;
|
||
|
|
|
||
|
|
print_msg(TEST_BREAK, __FUNCTION__);
|
||
|
|
|
||
|
|
flags = writeable ? O_RDWR : OPEN_FLAG;
|
||
|
|
|
||
|
|
do_numeric = 1;
|
||
|
|
writeable = O_RDONLY;
|
||
|
|
do_inquiry = 1;
|
||
|
|
do_extra = 1;
|
||
|
|
|
||
|
|
for (k = 0, res = 0; (k < 1000) && (num_errors < MAX_ERRORS);
|
||
|
|
++k, res = (sg_fd >= 0) ? close(sg_fd) : 0) {
|
||
|
|
if (res < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ, ME "Error closing %s ",
|
||
|
|
fname);
|
||
|
|
perror(ME "close error");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
make_dev_name(fname, NULL, k, do_numeric);
|
||
|
|
|
||
|
|
sg_fd = open(fname, flags | O_NONBLOCK);
|
||
|
|
if (sg_fd < 0) {
|
||
|
|
if (EBUSY == errno) {
|
||
|
|
printf
|
||
|
|
("%s: device busy (O_EXCL lock), skipping\n",
|
||
|
|
fname);
|
||
|
|
continue;
|
||
|
|
} else if ((ENODEV == errno) || (ENOENT == errno) ||
|
||
|
|
(ENXIO == errno)) {
|
||
|
|
++num_errors;
|
||
|
|
++num_silent;
|
||
|
|
continue;
|
||
|
|
} else {
|
||
|
|
if (EACCES == errno)
|
||
|
|
eacces_err = 1;
|
||
|
|
snprintf(ebuff, EBUFF_SZ,
|
||
|
|
ME "Error opening %s ", fname);
|
||
|
|
perror(ebuff);
|
||
|
|
++num_errors;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
res = ioctl(sg_fd, SCSI_IOCTL_GET_IDLUN, &my_idlun);
|
||
|
|
if (res < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ,
|
||
|
|
ME "device %s failed on scsi ioctl, skip",
|
||
|
|
fname);
|
||
|
|
perror(ebuff);
|
||
|
|
++num_errors;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
res = ioctl(sg_fd, SCSI_IOCTL_GET_BUS_NUMBER, &host_no);
|
||
|
|
if (res < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ, ME "device %s failed on scsi "
|
||
|
|
"ioctl(2), skip", fname);
|
||
|
|
perror(ebuff);
|
||
|
|
++num_errors;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
#ifdef SG_EMULATED_HOST
|
||
|
|
res = ioctl(sg_fd, SG_EMULATED_HOST, &emul);
|
||
|
|
if (res < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ,
|
||
|
|
ME "device %s failed on sg ioctl(3), skip",
|
||
|
|
fname);
|
||
|
|
perror(ebuff);
|
||
|
|
++num_errors;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
#else
|
||
|
|
emul = 0;
|
||
|
|
#endif
|
||
|
|
printf("%s: scsi%d channel=%d id=%d lun=%d", fname, host_no,
|
||
|
|
(my_idlun.dev_id >> 16) & 0xff, my_idlun.dev_id & 0xff,
|
||
|
|
(my_idlun.dev_id >> 8) & 0xff);
|
||
|
|
if (emul)
|
||
|
|
printf(" [em]");
|
||
|
|
#if 0
|
||
|
|
printf(", huid=%d", my_idlun.host_unique_id);
|
||
|
|
#endif
|
||
|
|
#ifdef SG_GET_RESERVED_SIZE
|
||
|
|
{
|
||
|
|
My_sg_scsi_id m_id; /* compatible with sg_scsi_id_t in sg.h */
|
||
|
|
|
||
|
|
res = ioctl(sg_fd, SG_GET_SCSI_ID, &m_id);
|
||
|
|
if (res < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ,
|
||
|
|
ME "device %s ioctls(4), skip", fname);
|
||
|
|
perror(ebuff);
|
||
|
|
++num_errors;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
printf(" type=%d", m_id.scsi_type);
|
||
|
|
if (do_extra)
|
||
|
|
printf(" cmd_per_lun=%hd queue_depth=%hd\n",
|
||
|
|
m_id.h_cmd_per_lun, m_id.d_queue_depth);
|
||
|
|
else
|
||
|
|
printf("\n");
|
||
|
|
}
|
||
|
|
#else
|
||
|
|
printf("\n");
|
||
|
|
#endif
|
||
|
|
if (!do_inquiry)
|
||
|
|
continue;
|
||
|
|
|
||
|
|
#ifdef SG_IO
|
||
|
|
if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &f) >= 0) && (f >= 30000)) {
|
||
|
|
res = sg3_inq(sg_fd, inqBuff, do_extra);
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
memset(isghp, 0, sizeof(struct sg_header));
|
||
|
|
isghp->reply_len = inqOutLen;
|
||
|
|
memcpy(inqBuff + OFF, inqCmdBlk, INQUIRY_CMDLEN);
|
||
|
|
|
||
|
|
if (O_RDWR == (flags & O_ACCMODE)) { /* turn on blocking */
|
||
|
|
f = fcntl(sg_fd, F_GETFL);
|
||
|
|
fcntl(sg_fd, F_SETFL, f & (~O_NONBLOCK));
|
||
|
|
} else {
|
||
|
|
close(sg_fd);
|
||
|
|
sg_fd = open(fname, O_RDWR);
|
||
|
|
}
|
||
|
|
|
||
|
|
res = write(sg_fd, inqBuff, inqInLen);
|
||
|
|
if (res < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ, ME "device %s writing, skip",
|
||
|
|
fname);
|
||
|
|
perror(ebuff);
|
||
|
|
++num_errors;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
res = read(sg_fd, inqBuff, inqOutLen);
|
||
|
|
if (res < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ, ME "device %s reading, skip",
|
||
|
|
fname);
|
||
|
|
perror(ebuff);
|
||
|
|
++num_errors;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
#ifdef SG_GET_RESERVED_SIZE
|
||
|
|
if (!sg_chk_n_print("Error from Inquiry", isghp->target_status,
|
||
|
|
isghp->host_status, isghp->driver_status,
|
||
|
|
isghp->sense_buffer, SG_MAX_SENSE))
|
||
|
|
continue;
|
||
|
|
#else
|
||
|
|
if ((isghp->result != 0) || (0 != isghp->sense_buffer[0])) {
|
||
|
|
printf("Error from Inquiry: result=%d\n",
|
||
|
|
isghp->result);
|
||
|
|
if (0 != isghp->sense_buffer[0])
|
||
|
|
sg_print_sense("Error from Inquiry",
|
||
|
|
isghp->sense_buffer,
|
||
|
|
SG_MAX_SENSE);
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
f = (int)*(buffp + 7);
|
||
|
|
printf(" %.8s %.16s %.4s ", buffp + 8, buffp + 16,
|
||
|
|
buffp + 32);
|
||
|
|
printf("[wide=%d sync=%d cmdq=%d sftre=%d pq=0x%x]\n",
|
||
|
|
! !(f & 0x20), ! !(f & 0x10), ! !(f & 2), ! !(f & 1),
|
||
|
|
(*buffp & 0xe0) >> 5);
|
||
|
|
}
|
||
|
|
if ((num_errors >= MAX_ERRORS) && (num_silent < num_errors)) {
|
||
|
|
printf("Stopping because there are too many error\n");
|
||
|
|
if (eacces_err)
|
||
|
|
printf(" root access may be required\n");
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifdef SG_IO
|
||
|
|
int sg3_inq(int sg_fd, unsigned char *inqBuff, int do_extra)
|
||
|
|
{
|
||
|
|
sg_io_hdr_t io_hdr;
|
||
|
|
unsigned char sense_buffer[32];
|
||
|
|
int ok;
|
||
|
|
|
||
|
|
memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
|
||
|
|
io_hdr.interface_id = 'S';
|
||
|
|
io_hdr.cmd_len = sizeof(inqCmdBlk);
|
||
|
|
io_hdr.mx_sb_len = sizeof(sense_buffer);
|
||
|
|
io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
|
||
|
|
io_hdr.dxfer_len = INQ_REPLY_LEN;
|
||
|
|
io_hdr.dxferp = inqBuff;
|
||
|
|
io_hdr.cmdp = inqCmdBlk;
|
||
|
|
io_hdr.sbp = sense_buffer;
|
||
|
|
io_hdr.timeout = 20000; /* 20000 millisecs == 20 seconds */
|
||
|
|
|
||
|
|
if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
|
||
|
|
perror(ME "Inquiry SG_IO ioctl error");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* now for the error processing */
|
||
|
|
ok = 0;
|
||
|
|
switch (sg_err_category3(&io_hdr)) {
|
||
|
|
case SG_ERR_CAT_CLEAN:
|
||
|
|
case SG_ERR_CAT_RECOVERED:
|
||
|
|
ok = 1;
|
||
|
|
break;
|
||
|
|
default: /* won't bother decoding other categories */
|
||
|
|
sg_chk_n_print3("INQUIRY command error", &io_hdr);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (ok) { /* output result if it is available */
|
||
|
|
char *p = (char *)inqBuff;
|
||
|
|
int f = (int)*(p + 7);
|
||
|
|
printf(" %.8s %.16s %.4s ", p + 8, p + 16, p + 32);
|
||
|
|
printf("[wide=%d sync=%d cmdq=%d sftre=%d pq=0x%x] ",
|
||
|
|
! !(f & 0x20), ! !(f & 0x10), ! !(f & 2), ! !(f & 1),
|
||
|
|
(*p & 0xe0) >> 5);
|
||
|
|
if (do_extra)
|
||
|
|
printf("dur=%ums\n", io_hdr.duration);
|
||
|
|
else
|
||
|
|
printf("\n");
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
static int do_logs(int sg_fd, int ppc, int sp, int pc, int pg_code,
|
||
|
|
int paramp, void *resp, int mx_resp_len, int noisy)
|
||
|
|
{
|
||
|
|
int res;
|
||
|
|
unsigned char logsCmdBlk[LOG_SENSE_CMDLEN] =
|
||
|
|
{ LOG_SENSE_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||
|
|
unsigned char sense_b[SENSE_BUFF_LEN];
|
||
|
|
sg_io_hdr_t io_hdr;
|
||
|
|
|
||
|
|
logsCmdBlk[1] = (unsigned char)((ppc ? 2 : 0) | (sp ? 1 : 0));
|
||
|
|
logsCmdBlk[2] = (unsigned char)(((pc << 6) & 0xc0) | (pg_code & 0x3f));
|
||
|
|
logsCmdBlk[5] = (unsigned char)((paramp >> 8) & 0xff);
|
||
|
|
logsCmdBlk[6] = (unsigned char)(paramp & 0xff);
|
||
|
|
if (mx_resp_len > 0xffff) {
|
||
|
|
printf(ME "mx_resp_len too big\n");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
logsCmdBlk[7] = (unsigned char)((mx_resp_len >> 8) & 0xff);
|
||
|
|
logsCmdBlk[8] = (unsigned char)(mx_resp_len & 0xff);
|
||
|
|
|
||
|
|
memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
|
||
|
|
io_hdr.interface_id = 'S';
|
||
|
|
io_hdr.cmd_len = sizeof(logsCmdBlk);
|
||
|
|
io_hdr.mx_sb_len = sizeof(sense_b);
|
||
|
|
io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
|
||
|
|
io_hdr.dxfer_len = mx_resp_len;
|
||
|
|
io_hdr.dxferp = resp;
|
||
|
|
io_hdr.cmdp = logsCmdBlk;
|
||
|
|
io_hdr.sbp = sense_b;
|
||
|
|
io_hdr.timeout = DEF_TIMEOUT;
|
||
|
|
|
||
|
|
if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
|
||
|
|
perror("SG_IO (log sense) error");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
#if 0
|
||
|
|
printf("SG_IO ioctl: status=%d, info=%d, sb_len_wr=%d\n",
|
||
|
|
io_hdr.status, io_hdr.info, io_hdr.sb_len_wr);
|
||
|
|
#endif
|
||
|
|
res = sg_err_category3(&io_hdr);
|
||
|
|
switch (res) {
|
||
|
|
case SG_ERR_CAT_CLEAN:
|
||
|
|
case SG_ERR_CAT_RECOVERED:
|
||
|
|
return 0;
|
||
|
|
default:
|
||
|
|
if (noisy) {
|
||
|
|
char ebuff[EBUFF_SZ];
|
||
|
|
snprintf(ebuff, EBUFF_SZ, ME "ppc=%d, sp=%d, "
|
||
|
|
"pc=%d, page_code=%x, paramp=%x\n ", ppc,
|
||
|
|
sp, pc, pg_code, paramp);
|
||
|
|
sg_chk_n_print3(ebuff, &io_hdr);
|
||
|
|
}
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static void dStrHex(const char *str, int len, int no_ascii)
|
||
|
|
{
|
||
|
|
const char *p = str;
|
||
|
|
unsigned char c;
|
||
|
|
char buff[82];
|
||
|
|
int a = 0;
|
||
|
|
const int bpstart = 5;
|
||
|
|
const int cpstart = 60;
|
||
|
|
int cpos = cpstart;
|
||
|
|
int bpos = bpstart;
|
||
|
|
int i, k;
|
||
|
|
|
||
|
|
if (len <= 0)
|
||
|
|
return;
|
||
|
|
memset(buff, ' ', 80);
|
||
|
|
buff[80] = '\0';
|
||
|
|
k = sprintf(buff + 1, "%.2x", a);
|
||
|
|
buff[k + 1] = ' ';
|
||
|
|
if (bpos >= ((bpstart + (9 * 3))))
|
||
|
|
bpos++;
|
||
|
|
|
||
|
|
for (i = 0; i < len; i++) {
|
||
|
|
c = *p++;
|
||
|
|
bpos += 3;
|
||
|
|
if (bpos == (bpstart + (9 * 3)))
|
||
|
|
bpos++;
|
||
|
|
sprintf(&buff[bpos], "%.2x", (int)(unsigned char)c);
|
||
|
|
buff[bpos + 2] = ' ';
|
||
|
|
if (no_ascii)
|
||
|
|
buff[cpos++] = ' ';
|
||
|
|
else {
|
||
|
|
if ((c < ' ') || (c >= 0x7f))
|
||
|
|
c = '.';
|
||
|
|
buff[cpos++] = c;
|
||
|
|
}
|
||
|
|
if (cpos > (cpstart + 15)) {
|
||
|
|
printf("%s\n", buff);
|
||
|
|
bpos = bpstart;
|
||
|
|
cpos = cpstart;
|
||
|
|
a += 16;
|
||
|
|
memset(buff, ' ', 80);
|
||
|
|
k = sprintf(buff + 1, "%.2x", a);
|
||
|
|
buff[k + 1] = ' ';
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (cpos > cpstart) {
|
||
|
|
printf("%s\n", buff);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static void show_page_name(int page_no)
|
||
|
|
{
|
||
|
|
switch (page_no) {
|
||
|
|
case 0x0:
|
||
|
|
printf(" 0x00 Supported log pages\n");
|
||
|
|
break;
|
||
|
|
case 0x1:
|
||
|
|
printf(" 0x01 Buffer over-run/under-run\n");
|
||
|
|
break;
|
||
|
|
case 0x2:
|
||
|
|
printf(" 0x02 Error counters (write)\n");
|
||
|
|
break;
|
||
|
|
case 0x3:
|
||
|
|
printf(" 0x03 Error counters (read)\n");
|
||
|
|
break;
|
||
|
|
case 0x4:
|
||
|
|
printf(" 0x04 Error counters (read reverse)\n");
|
||
|
|
break;
|
||
|
|
case 0x5:
|
||
|
|
printf(" 0x05 Error counters (verify)\n");
|
||
|
|
break;
|
||
|
|
case 0x6:
|
||
|
|
printf(" 0x06 Non-medium errors\n");
|
||
|
|
break;
|
||
|
|
case 0x7:
|
||
|
|
printf(" 0x07 Last n error events\n");
|
||
|
|
break;
|
||
|
|
case 0x8:
|
||
|
|
printf(" 0x08 Format status (sbc2)\n");
|
||
|
|
break;
|
||
|
|
case 0xb:
|
||
|
|
printf(" 0x0b Last n deferred errors of "
|
||
|
|
"asynchronous events\n");
|
||
|
|
break;
|
||
|
|
case 0xc:
|
||
|
|
printf(" 0x0c Sequential Access (ssc-2)\n");
|
||
|
|
break;
|
||
|
|
case 0xd:
|
||
|
|
printf(" 0x0d Temperature\n");
|
||
|
|
break;
|
||
|
|
case 0xe:
|
||
|
|
printf(" 0x0e Start-stop cycle counter\n");
|
||
|
|
break;
|
||
|
|
case 0xf:
|
||
|
|
printf(" 0x0f Application client\n");
|
||
|
|
break;
|
||
|
|
case 0x10:
|
||
|
|
printf(" 0x10 Self-test results\n");
|
||
|
|
break;
|
||
|
|
case 0x18:
|
||
|
|
printf(" 0x18 Protocol specific port\n");
|
||
|
|
break;
|
||
|
|
case 0x2e:
|
||
|
|
printf(" 0x2e Tape alerts (ssc-2)\n");
|
||
|
|
break;
|
||
|
|
case 0x2f:
|
||
|
|
printf(" 0x2f Informational exceptions (SMART)\n");
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
printf(" 0x%.2x\n", page_no);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static void show_buffer_under_overrun_page(unsigned char *resp, int len)
|
||
|
|
{
|
||
|
|
int k, j, num, pl, count_basis, cause;
|
||
|
|
unsigned char *ucp;
|
||
|
|
unsigned char *xp;
|
||
|
|
unsigned long long ull;
|
||
|
|
|
||
|
|
printf("Buffer over-run/under-run page\n");
|
||
|
|
num = len - 4;
|
||
|
|
ucp = &resp[0] + 4;
|
||
|
|
while (num > 3) {
|
||
|
|
pl = ucp[3] + 4;
|
||
|
|
count_basis = (ucp[1] >> 5) & 0x7;
|
||
|
|
printf(" Count basis: ");
|
||
|
|
switch (count_basis) {
|
||
|
|
case 0:
|
||
|
|
printf("undefined");
|
||
|
|
break;
|
||
|
|
case 1:
|
||
|
|
printf("per command");
|
||
|
|
break;
|
||
|
|
case 2:
|
||
|
|
printf("per failed reconnect");
|
||
|
|
break;
|
||
|
|
case 3:
|
||
|
|
printf("per unit of time");
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
printf("reserved [0x%x]", count_basis);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
cause = (ucp[1] >> 1) & 0xf;
|
||
|
|
printf(", Cause: ");
|
||
|
|
switch (cause) {
|
||
|
|
case 0:
|
||
|
|
printf("bus busy");
|
||
|
|
break;
|
||
|
|
case 1:
|
||
|
|
printf("transfer rate too slow");
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
printf("reserved [0x%x]", cause);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
printf(", Type: ");
|
||
|
|
if (ucp[1] & 1)
|
||
|
|
printf("over-run");
|
||
|
|
else
|
||
|
|
printf("under-run");
|
||
|
|
printf(", count");
|
||
|
|
k = pl - 4;
|
||
|
|
xp = ucp + 4;
|
||
|
|
if (k > sizeof(ull)) {
|
||
|
|
xp += (k - sizeof(ull));
|
||
|
|
k = sizeof(ull);
|
||
|
|
}
|
||
|
|
ull = 0;
|
||
|
|
for (j = 0; j < k; ++j) {
|
||
|
|
if (j > 0)
|
||
|
|
ull <<= 8;
|
||
|
|
ull |= xp[j];
|
||
|
|
}
|
||
|
|
printf(" = %llu\n", ull);
|
||
|
|
num -= pl;
|
||
|
|
ucp += pl;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static void show_error_counter_page(unsigned char *resp, int len)
|
||
|
|
{
|
||
|
|
int k, j, num, pl, pc;
|
||
|
|
unsigned char *ucp;
|
||
|
|
unsigned char *xp;
|
||
|
|
unsigned long long ull;
|
||
|
|
|
||
|
|
switch (resp[0]) {
|
||
|
|
case 2:
|
||
|
|
printf("Write error counter page\n");
|
||
|
|
break;
|
||
|
|
case 3:
|
||
|
|
printf("Read error counter page\n");
|
||
|
|
break;
|
||
|
|
case 4:
|
||
|
|
printf("Read Reverse error counter page\n");
|
||
|
|
break;
|
||
|
|
case 5:
|
||
|
|
printf("Verify error counter page\n");
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
printf("expecting error counter page, got page=0x%x\n",
|
||
|
|
resp[0]);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
num = len - 4;
|
||
|
|
ucp = &resp[0] + 4;
|
||
|
|
while (num > 3) {
|
||
|
|
pc = (ucp[0] << 8) | ucp[1];
|
||
|
|
pl = ucp[3] + 4;
|
||
|
|
switch (pc) {
|
||
|
|
case 0:
|
||
|
|
printf(" Errors corrected without substantion delay");
|
||
|
|
break;
|
||
|
|
case 1:
|
||
|
|
printf(" Errors corrected with possible delays");
|
||
|
|
break;
|
||
|
|
case 2:
|
||
|
|
printf(" Total operations");
|
||
|
|
break;
|
||
|
|
case 3:
|
||
|
|
printf(" Total errors corrected");
|
||
|
|
break;
|
||
|
|
case 4:
|
||
|
|
printf(" Total times correction algorithm processed");
|
||
|
|
break;
|
||
|
|
case 5:
|
||
|
|
printf(" Total bytes processed");
|
||
|
|
break;
|
||
|
|
case 6:
|
||
|
|
printf(" Total uncorrected errors");
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
printf(" Reserved or vendor specific [0x%x]", pc);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
k = pl - 4;
|
||
|
|
xp = ucp + 4;
|
||
|
|
if (k > sizeof(ull)) {
|
||
|
|
xp += (k - sizeof(ull));
|
||
|
|
k = sizeof(ull);
|
||
|
|
}
|
||
|
|
ull = 0;
|
||
|
|
for (j = 0; j < k; ++j) {
|
||
|
|
if (j > 0)
|
||
|
|
ull <<= 8;
|
||
|
|
ull |= xp[j];
|
||
|
|
}
|
||
|
|
printf(" = %llu\n", ull);
|
||
|
|
num -= pl;
|
||
|
|
ucp += pl;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static void show_non_medium_error_page(unsigned char *resp, int len)
|
||
|
|
{
|
||
|
|
int k, j, num, pl, pc;
|
||
|
|
unsigned char *ucp;
|
||
|
|
unsigned char *xp;
|
||
|
|
unsigned long long ull;
|
||
|
|
|
||
|
|
printf("Non-medium error page\n");
|
||
|
|
num = len - 4;
|
||
|
|
ucp = &resp[0] + 4;
|
||
|
|
while (num > 3) {
|
||
|
|
pc = (ucp[0] << 8) | ucp[1];
|
||
|
|
pl = ucp[3] + 4;
|
||
|
|
switch (pc) {
|
||
|
|
case 0:
|
||
|
|
printf(" Non-medium error count");
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
if (pc <= 0x7fff)
|
||
|
|
printf(" Reserved [0x%x]", pc);
|
||
|
|
else
|
||
|
|
printf(" Vendor specific [0x%x]", pc);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
k = pl - 4;
|
||
|
|
xp = ucp + 4;
|
||
|
|
if (k > sizeof(ull)) {
|
||
|
|
xp += (k - sizeof(ull));
|
||
|
|
k = sizeof(ull);
|
||
|
|
}
|
||
|
|
ull = 0;
|
||
|
|
for (j = 0; j < k; ++j) {
|
||
|
|
if (j > 0)
|
||
|
|
ull <<= 8;
|
||
|
|
ull |= xp[j];
|
||
|
|
}
|
||
|
|
printf(" = %llu\n", ull);
|
||
|
|
num -= pl;
|
||
|
|
ucp += pl;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
const char *self_test_code[] = {
|
||
|
|
"default", "background short", "background extended", "reserved",
|
||
|
|
"aborted background", "foreground short", "foreground extended",
|
||
|
|
"reserved"
|
||
|
|
};
|
||
|
|
|
||
|
|
const char *self_test_result[] = {
|
||
|
|
"completed without error",
|
||
|
|
"aborted by SEND DIAGNOSTIC",
|
||
|
|
"aborted other than by SEND DIAGNOSTIC",
|
||
|
|
"unknown error, unable to complete",
|
||
|
|
"self test completed with failure in test segment (which one unkown)",
|
||
|
|
"first segment in self test failed",
|
||
|
|
"second segment in self test failed",
|
||
|
|
"another segment in self test failed",
|
||
|
|
"reserved", "reserved", "reserved", "reserved", "reserved", "reserved",
|
||
|
|
"reserved",
|
||
|
|
"self test in progress"
|
||
|
|
};
|
||
|
|
|
||
|
|
static void show_self_test_page(unsigned char *resp, int len)
|
||
|
|
{
|
||
|
|
int k, num, n, res;
|
||
|
|
unsigned char *ucp;
|
||
|
|
unsigned long long ull;
|
||
|
|
|
||
|
|
num = len - 4;
|
||
|
|
if (num < 0x190) {
|
||
|
|
printf("badly formed self-test results page\n");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
printf("Self-test results page\n");
|
||
|
|
for (k = 0, ucp = resp + 4; k < 20; ++k, ucp += 20) {
|
||
|
|
n = (ucp[6] << 8) | ucp[7];
|
||
|
|
if ((0 == n) && (0 == ucp[4]))
|
||
|
|
break;
|
||
|
|
printf(" Parameter code=%d, accumulated power-on hours=%d\n",
|
||
|
|
(ucp[0] << 8) | ucp[1], n);
|
||
|
|
printf(" self test code: %s [%d]\n",
|
||
|
|
self_test_code[(ucp[4] >> 5) & 0x7],
|
||
|
|
(ucp[4] >> 5) & 0x7);
|
||
|
|
res = ucp[4] & 0xf;
|
||
|
|
printf(" self test result: %s [%d]\n",
|
||
|
|
self_test_result[res], res);
|
||
|
|
if (ucp[5])
|
||
|
|
printf(" self-test number=%d\n", (int)ucp[5]);
|
||
|
|
ull = ucp[8];
|
||
|
|
ull <<= 8;
|
||
|
|
ull |= ucp[9];
|
||
|
|
ull <<= 8;
|
||
|
|
ull |= ucp[10];
|
||
|
|
ull <<= 8;
|
||
|
|
ull |= ucp[11];
|
||
|
|
ull <<= 8;
|
||
|
|
ull |= ucp[12];
|
||
|
|
ull <<= 8;
|
||
|
|
ull |= ucp[13];
|
||
|
|
ull <<= 8;
|
||
|
|
ull |= ucp[14];
|
||
|
|
ull <<= 8;
|
||
|
|
ull |= ucp[14];
|
||
|
|
ull <<= 8;
|
||
|
|
ull |= ucp[15];
|
||
|
|
if ((0xffffffffffffffffULL != ull) && (res > 0) && (res < 0xf))
|
||
|
|
printf(" address of first error=0x%llx\n", ull);
|
||
|
|
if (ucp[16] & 0xf)
|
||
|
|
printf(" sense key=0x%x, asc=0x%x, asq=0x%x\n",
|
||
|
|
ucp[16] & 0xf, ucp[17], ucp[18]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static void show_Temperature_page(unsigned char *resp, int len, int hdr)
|
||
|
|
{
|
||
|
|
int k, num, extra, pc;
|
||
|
|
unsigned char *ucp;
|
||
|
|
|
||
|
|
num = len - 4;
|
||
|
|
ucp = &resp[0] + 4;
|
||
|
|
if (num < 4) {
|
||
|
|
printf("badly formed Temperature log page\n");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (hdr)
|
||
|
|
printf("Temperature log page\n");
|
||
|
|
for (k = num; k > 0; k -= extra, ucp += extra) {
|
||
|
|
if (k < 3) {
|
||
|
|
printf("short Temperature log page\n");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
extra = ucp[3] + 4;
|
||
|
|
pc = ((ucp[0] << 8) & 0xff) + ucp[1];
|
||
|
|
if (0 == pc) {
|
||
|
|
if (extra > 5) {
|
||
|
|
if (ucp[5] < 0xff)
|
||
|
|
printf(" Current temperature= %d C\n",
|
||
|
|
ucp[5]);
|
||
|
|
else
|
||
|
|
printf
|
||
|
|
(" Current temperature=<not available>\n");
|
||
|
|
}
|
||
|
|
} else if (1 == pc) {
|
||
|
|
if (extra > 5) {
|
||
|
|
if (ucp[5] < 0xff)
|
||
|
|
printf
|
||
|
|
(" Reference temperature= %d C\n",
|
||
|
|
ucp[5]);
|
||
|
|
else
|
||
|
|
printf
|
||
|
|
(" Reference temperature=<not available>\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
} else {
|
||
|
|
printf(" parameter code=0x%x, contents in hex:\n", pc);
|
||
|
|
dStrHex((const char *)ucp, extra, 1);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static void show_IE_page(unsigned char *resp, int len, int full)
|
||
|
|
{
|
||
|
|
int k, num, extra, pc;
|
||
|
|
unsigned char *ucp;
|
||
|
|
|
||
|
|
num = len - 4;
|
||
|
|
ucp = &resp[0] + 4;
|
||
|
|
if (num < 4) {
|
||
|
|
printf("badly formed Informational Exceptions log page\n");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (full)
|
||
|
|
printf("Informational Exceptions log page\n");
|
||
|
|
for (k = num; k > 0; k -= extra, ucp += extra) {
|
||
|
|
if (k < 3) {
|
||
|
|
printf("short Informational Exceptions log page\n");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
extra = ucp[3] + 4;
|
||
|
|
pc = ((ucp[0] << 8) & 0xff) + ucp[1];
|
||
|
|
if (0 == pc) {
|
||
|
|
if (extra > 5) {
|
||
|
|
if (full)
|
||
|
|
printf(" IE asc=0x%x, ascq=0x%x",
|
||
|
|
ucp[4], ucp[5]);
|
||
|
|
if (extra > 6) {
|
||
|
|
if (full)
|
||
|
|
printf(",");
|
||
|
|
if (ucp[6] < 0xff)
|
||
|
|
printf
|
||
|
|
(" Current temperature=%d C",
|
||
|
|
ucp[6]);
|
||
|
|
else
|
||
|
|
printf
|
||
|
|
(" Current temperature=<not available>");
|
||
|
|
}
|
||
|
|
printf("\n");
|
||
|
|
}
|
||
|
|
} else if (full) {
|
||
|
|
printf(" parameter code=0x%x, contents in hex:\n", pc);
|
||
|
|
dStrHex((const char *)ucp, extra, 1);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static void show_ascii_page(unsigned char *resp, int len)
|
||
|
|
{
|
||
|
|
int k, n, num;
|
||
|
|
|
||
|
|
if (len < 0) {
|
||
|
|
printf("response has bad length\n");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
num = len - 4;
|
||
|
|
switch (resp[0]) {
|
||
|
|
case 0:
|
||
|
|
printf("Supported pages:\n");
|
||
|
|
for (k = 0; k < num; ++k)
|
||
|
|
show_page_name((int)resp[4 + k]);
|
||
|
|
break;
|
||
|
|
case 0x1:
|
||
|
|
show_buffer_under_overrun_page(resp, len);
|
||
|
|
break;
|
||
|
|
case 0x2:
|
||
|
|
case 0x3:
|
||
|
|
case 0x4:
|
||
|
|
case 0x5:
|
||
|
|
show_error_counter_page(resp, len);
|
||
|
|
break;
|
||
|
|
case 0x6:
|
||
|
|
show_non_medium_error_page(resp, len);
|
||
|
|
break;
|
||
|
|
case 0xd:
|
||
|
|
show_Temperature_page(resp, len, 1);
|
||
|
|
break;
|
||
|
|
case 0xe:
|
||
|
|
if (len < 40) {
|
||
|
|
printf("badly formed start-stop cycle counter page\n");
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
printf("Start-stop cycle counter page\n");
|
||
|
|
printf(" Date of manufacture, year: %.4s, week: %.2s\n",
|
||
|
|
&resp[8], &resp[12]);
|
||
|
|
printf(" Accounting date, year: %.4s, week: %.2s\n",
|
||
|
|
&resp[18], &resp[22]);
|
||
|
|
n = (resp[28] << 24) | (resp[29] << 16) | (resp[30] << 8) |
|
||
|
|
resp[31];
|
||
|
|
printf(" Specified cycle count over device lifetime=%d\n", n);
|
||
|
|
n = (resp[36] << 24) | (resp[37] << 16) | (resp[38] << 8) |
|
||
|
|
resp[39];
|
||
|
|
printf(" Accumulated start-stop cycles=%d\n", n);
|
||
|
|
break;
|
||
|
|
case 0x10:
|
||
|
|
show_self_test_page(resp, len);
|
||
|
|
break;
|
||
|
|
case 0x2f:
|
||
|
|
show_IE_page(resp, len, 1);
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
printf("No ascii information for page=0x%x, here is hex:\n",
|
||
|
|
resp[0]);
|
||
|
|
dStrHex((const char *)resp, len, 1);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static int fetchTemperature(int sg_fd, int do_hex, unsigned char *resp,
|
||
|
|
int max_len)
|
||
|
|
{
|
||
|
|
int res = 0;
|
||
|
|
|
||
|
|
if (0 == do_logs(sg_fd, 0, 0, 1, 0xd, 0, resp, max_len, 0))
|
||
|
|
show_Temperature_page(resp, (resp[2] << 8) + resp[3] + 4, 0);
|
||
|
|
else if (0 == do_logs(sg_fd, 0, 0, 1, 0x2f, 0, resp, max_len, 0))
|
||
|
|
show_IE_page(resp, (resp[2] << 8) + resp[3] + 4, 0);
|
||
|
|
else {
|
||
|
|
printf
|
||
|
|
("Unable to find temperature in either log page (temperature "
|
||
|
|
"or IE)\n");
|
||
|
|
res = 1;
|
||
|
|
}
|
||
|
|
close(sg_fd);
|
||
|
|
return res;
|
||
|
|
}
|
||
|
|
|
||
|
|
int show_scsi_logs(char *device)
|
||
|
|
{
|
||
|
|
int sg_fd, k, pg_len;
|
||
|
|
char *file_name = 0;
|
||
|
|
unsigned char rsp_buff[MX_ALLOC_LEN];
|
||
|
|
int pg_code = 0;
|
||
|
|
int pc = 1; /* N.B. some disks only give data for current cumulative */
|
||
|
|
int paramp = 0;
|
||
|
|
int do_list = 0;
|
||
|
|
int do_ppc = 0;
|
||
|
|
int do_sp = 0;
|
||
|
|
int do_hex = 0;
|
||
|
|
int do_all = 1;
|
||
|
|
int do_temp = 0;
|
||
|
|
int oflags = O_RDWR | O_NONBLOCK;
|
||
|
|
|
||
|
|
file_name = device;
|
||
|
|
print_msg(TEST_BREAK, __FUNCTION__);
|
||
|
|
|
||
|
|
if ((sg_fd = open(file_name, oflags)) < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ, ME "error opening file: %s",
|
||
|
|
file_name);
|
||
|
|
perror(ebuff);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
/* Just to be safe, check we have a new sg device by trying an ioctl */
|
||
|
|
if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) {
|
||
|
|
printf(ME "%s doesn't seem to be a version 3 sg device\n",
|
||
|
|
file_name);
|
||
|
|
close(sg_fd);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
if (do_list || do_all)
|
||
|
|
pg_code = PG_CODE_ALL;
|
||
|
|
pg_len = 0;
|
||
|
|
if (1 == do_temp)
|
||
|
|
return fetchTemperature(sg_fd, do_hex, rsp_buff, MX_ALLOC_LEN);
|
||
|
|
|
||
|
|
if (0 == do_logs(sg_fd, do_ppc, do_sp, pc, pg_code, paramp,
|
||
|
|
rsp_buff, MX_ALLOC_LEN, 1)) {
|
||
|
|
pg_len = (rsp_buff[2] << 8) + rsp_buff[3];
|
||
|
|
if ((pg_len + 4) > MX_ALLOC_LEN) {
|
||
|
|
printf
|
||
|
|
("Only fetched %d bytes of response, truncate output\n",
|
||
|
|
MX_ALLOC_LEN);
|
||
|
|
pg_len = MX_ALLOC_LEN - 4;
|
||
|
|
}
|
||
|
|
if (do_hex) {
|
||
|
|
printf("Returned log page code=0x%x, page len=0x%x\n",
|
||
|
|
rsp_buff[0], pg_len);
|
||
|
|
dStrHex((const char *)rsp_buff, pg_len + 4, 1);
|
||
|
|
} else
|
||
|
|
show_ascii_page(rsp_buff, pg_len + 4);
|
||
|
|
}
|
||
|
|
if (do_all && (pg_len > 1)) {
|
||
|
|
int my_len = pg_len - 1;
|
||
|
|
unsigned char parr[256];
|
||
|
|
|
||
|
|
memcpy(parr, rsp_buff + 5, my_len);
|
||
|
|
for (k = 0; k < my_len; ++k) {
|
||
|
|
printf("\n");
|
||
|
|
pg_code = parr[k];
|
||
|
|
if (0 ==
|
||
|
|
do_logs(sg_fd, do_ppc, do_sp, pc, pg_code, paramp,
|
||
|
|
rsp_buff, MX_ALLOC_LEN, 1)) {
|
||
|
|
pg_len = (rsp_buff[2] << 8) + rsp_buff[3];
|
||
|
|
if ((pg_len + 4) > MX_ALLOC_LEN) {
|
||
|
|
printf
|
||
|
|
("Only fetched %d bytes of response, truncate "
|
||
|
|
"output\n", MX_ALLOC_LEN);
|
||
|
|
pg_len = MX_ALLOC_LEN - 4;
|
||
|
|
}
|
||
|
|
if (do_hex) {
|
||
|
|
printf
|
||
|
|
("Returned log page code=0x%x, page len=0x%x\n",
|
||
|
|
rsp_buff[0], pg_len);
|
||
|
|
dStrHex((const char *)rsp_buff,
|
||
|
|
pg_len + 4, 1);
|
||
|
|
} else
|
||
|
|
show_ascii_page(rsp_buff, pg_len + 4);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
close(sg_fd);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int do_inquiry(int sg_fd, void *resp, int mx_resp_len)
|
||
|
|
{
|
||
|
|
int res;
|
||
|
|
unsigned char inqCmdBlk[INQUIRY_CMDLEN] =
|
||
|
|
{ INQUIRY_CMD, 0, 0, 0, 0, 0 };
|
||
|
|
unsigned char sense_b[SENSE_BUFF_LEN];
|
||
|
|
sg_io_hdr_t io_hdr;
|
||
|
|
|
||
|
|
inqCmdBlk[4] = (unsigned char)mx_resp_len;
|
||
|
|
memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
|
||
|
|
io_hdr.interface_id = 'S';
|
||
|
|
io_hdr.cmd_len = sizeof(inqCmdBlk);
|
||
|
|
io_hdr.mx_sb_len = sizeof(sense_b);
|
||
|
|
io_hdr.dxfer_direction = SG_DXFER_TO_FROM_DEV;
|
||
|
|
io_hdr.dxfer_len = mx_resp_len;
|
||
|
|
io_hdr.dxferp = resp;
|
||
|
|
io_hdr.cmdp = inqCmdBlk;
|
||
|
|
io_hdr.timeout = DEF_TIMEOUT;
|
||
|
|
|
||
|
|
if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
|
||
|
|
perror("SG_IO (inquiry) error");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
res = sg_err_category3(&io_hdr);
|
||
|
|
switch (res) {
|
||
|
|
case SG_ERR_CAT_CLEAN:
|
||
|
|
case SG_ERR_CAT_RECOVERED:
|
||
|
|
return 0;
|
||
|
|
default:
|
||
|
|
sg_chk_n_print3("Failed INQUIRY", &io_hdr);
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void leaf_dir(const char *lf, unsigned int *larr)
|
||
|
|
{
|
||
|
|
char name[NAME_LEN_MAX * 2];
|
||
|
|
int res;
|
||
|
|
|
||
|
|
if (do_quiet) {
|
||
|
|
printf("%u\t%u\t%u\t%u\n", larr[0], larr[1], larr[2], larr[3]);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
printf("%u\t%u\t%u\t%u\t%s\n", larr[0], larr[1], larr[2], larr[3], lf);
|
||
|
|
if (do_leaf) {
|
||
|
|
struct dirent *de_entry;
|
||
|
|
struct dirent *de_result;
|
||
|
|
DIR *sdir;
|
||
|
|
int outpos;
|
||
|
|
|
||
|
|
if (NULL == (sdir = opendir(lf))) {
|
||
|
|
fprintf(stderr, "leaf_dir: opendir of %s: failed\n",
|
||
|
|
lf);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
de_entry = malloc(sizeof(struct dirent) + NAME_LEN_MAX);
|
||
|
|
if (NULL == de_entry)
|
||
|
|
return;
|
||
|
|
res = 0;
|
||
|
|
printf("\t");
|
||
|
|
outpos = 8;
|
||
|
|
while (1) {
|
||
|
|
res = readdir_r(sdir, de_entry, &de_result);
|
||
|
|
if (0 != res) {
|
||
|
|
fprintf(stderr,
|
||
|
|
"leaf_dir: readdir_r of %s: %s\n", lf,
|
||
|
|
strerror(res));
|
||
|
|
res = -2;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
if (de_result == NULL)
|
||
|
|
break;
|
||
|
|
strncpy(name, de_entry->d_name, NAME_LEN_MAX * 2);
|
||
|
|
if ((0 == strcmp("..", name))
|
||
|
|
|| (0 == strcmp(".", name)))
|
||
|
|
continue;
|
||
|
|
if (do_extra) {
|
||
|
|
struct stat st;
|
||
|
|
char devname[NAME_LEN_MAX * 2];
|
||
|
|
|
||
|
|
strncpy(devname, lf, NAME_LEN_MAX * 2);
|
||
|
|
strcat(devname, "/");
|
||
|
|
strcat(devname, name);
|
||
|
|
if (stat(devname, &st) < 0)
|
||
|
|
return;
|
||
|
|
if (S_ISCHR(st.st_mode)) {
|
||
|
|
strcat(name, "(c ");
|
||
|
|
sprintf(name + strlen(name), "%d %d)",
|
||
|
|
major(st.st_rdev),
|
||
|
|
minor(st.st_rdev));
|
||
|
|
} else if (S_ISBLK(st.st_mode)) {
|
||
|
|
strcat(name, "(b ");
|
||
|
|
sprintf(name + strlen(name), "%d %d)",
|
||
|
|
major(st.st_rdev),
|
||
|
|
minor(st.st_rdev));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
res = strlen(name);
|
||
|
|
if ((outpos + res + 2) > 80) {
|
||
|
|
printf("\n\t");
|
||
|
|
outpos = 8;
|
||
|
|
}
|
||
|
|
printf("%s ", name);
|
||
|
|
outpos += res + 2;
|
||
|
|
}
|
||
|
|
printf("\n");
|
||
|
|
}
|
||
|
|
if (do_inq) {
|
||
|
|
int sg_fd;
|
||
|
|
char buff[64];
|
||
|
|
|
||
|
|
memset(buff, 0, sizeof(buff));
|
||
|
|
strncpy(name, lf, NAME_LEN_MAX * 2);
|
||
|
|
strcat(name, "/generic");
|
||
|
|
if ((sg_fd = open(name, O_RDONLY)) < 0) {
|
||
|
|
if (!checked_sg) {
|
||
|
|
checked_sg = 1;
|
||
|
|
if ((sg_fd = open("/dev/sg0", O_RDONLY)) >= 0)
|
||
|
|
close(sg_fd); /* try and get sg module loaded */
|
||
|
|
sg_fd = open(name, O_RDONLY);
|
||
|
|
}
|
||
|
|
if (sg_fd < 0) {
|
||
|
|
printf("Unable to open sg device: %s, %s\n",
|
||
|
|
name, strerror(errno));
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (0 != do_inquiry(sg_fd, buff, 64))
|
||
|
|
return;
|
||
|
|
close(sg_fd);
|
||
|
|
dStrHex(buff, 64, 0);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Return 0 -> ok, -1 -> opendir() error, -2 -> readdir_r error,
|
||
|
|
-3 -> malloc error */
|
||
|
|
int hbtl_scan(const char *path, int level, unsigned int *larr)
|
||
|
|
{
|
||
|
|
struct dirent *de_entry;
|
||
|
|
struct dirent *de_result;
|
||
|
|
char new_path[NAME_LEN_MAX * 2];
|
||
|
|
DIR *sdir;
|
||
|
|
int res;
|
||
|
|
size_t level_slen;
|
||
|
|
|
||
|
|
level_slen = strlen(level_arr[level]);
|
||
|
|
if (NULL == (sdir = opendir(path))) {
|
||
|
|
fprintf(stderr, "hbtl_scan: opendir of %s: failed\n", path);
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
de_entry = malloc(sizeof(struct dirent) + NAME_LEN_MAX);
|
||
|
|
if (NULL == de_entry)
|
||
|
|
return -3;
|
||
|
|
res = 0;
|
||
|
|
while (1) {
|
||
|
|
res = readdir_r(sdir, de_entry, &de_result);
|
||
|
|
if (0 != res) {
|
||
|
|
fprintf(stderr, "hbtl_scan: readdir_r of %s: %s\n",
|
||
|
|
path, strerror(res));
|
||
|
|
res = -2;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
if (de_result == NULL)
|
||
|
|
break;
|
||
|
|
if (0 ==
|
||
|
|
strncmp(level_arr[level], de_entry->d_name, level_slen)) {
|
||
|
|
if (1 !=
|
||
|
|
sscanf(de_entry->d_name + level_slen, "%u",
|
||
|
|
larr + level))
|
||
|
|
larr[level] = UINT_MAX;
|
||
|
|
strncpy(new_path, path, NAME_LEN_MAX * 2);
|
||
|
|
strcat(new_path, "/");
|
||
|
|
strcat(new_path, de_entry->d_name);
|
||
|
|
if ((level + 1) < LEVELS) {
|
||
|
|
res = hbtl_scan(new_path, level + 1, larr);
|
||
|
|
if (res < 0)
|
||
|
|
break;
|
||
|
|
} else
|
||
|
|
leaf_dir(new_path, larr);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
free(de_entry);
|
||
|
|
closedir(sdir);
|
||
|
|
return res;
|
||
|
|
}
|
||
|
|
|
||
|
|
int show_devfs_devices()
|
||
|
|
{
|
||
|
|
int res;
|
||
|
|
char ds_root[D_ROOT_SZ];
|
||
|
|
char di_root[D_ROOT_SZ];
|
||
|
|
unsigned int larr[LEVELS];
|
||
|
|
struct stat st;
|
||
|
|
|
||
|
|
print_msg(TEST_BREAK, __FUNCTION__);
|
||
|
|
strncpy(ds_root, "/dev", D_ROOT_SZ);
|
||
|
|
|
||
|
|
strncpy(di_root, ds_root, D_ROOT_SZ);
|
||
|
|
|
||
|
|
strcat(di_root, "/.devfsd");
|
||
|
|
|
||
|
|
if (stat(di_root, &st) < 0) {
|
||
|
|
printf("Didn't find %s so perhaps devfs is not present,"
|
||
|
|
" attempting to continue ...\n", di_root);
|
||
|
|
}
|
||
|
|
|
||
|
|
strncpy(di_root, ds_root, D_ROOT_SZ);
|
||
|
|
strcat(ds_root, "/scsi");
|
||
|
|
strcat(di_root, "/ide");
|
||
|
|
|
||
|
|
if (!do_ide)
|
||
|
|
printf("SCSI scan:\n");
|
||
|
|
|
||
|
|
res = hbtl_scan(ds_root, 0, larr);
|
||
|
|
|
||
|
|
if (res < 0)
|
||
|
|
printf("main: scsi hbtl_scan res=%d\n", res);
|
||
|
|
|
||
|
|
do_ide = TRUE;
|
||
|
|
do_inq = 0; /* won't try SCSI INQUIRY on IDE devices */
|
||
|
|
|
||
|
|
if (do_ide) {
|
||
|
|
printf("\nIDE scan:\n");
|
||
|
|
res = hbtl_scan(di_root, 0, larr);
|
||
|
|
|
||
|
|
if (res < 0)
|
||
|
|
printf("main: ide hbtl_scan res=%d\n", res);
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void install_handler(int sig_num, void (*sig_handler) (int sig))
|
||
|
|
{
|
||
|
|
struct sigaction sigact;
|
||
|
|
sigaction(sig_num, NULL, &sigact);
|
||
|
|
if (sigact.sa_handler != SIG_IGN) {
|
||
|
|
sigact.sa_handler = sig_handler;
|
||
|
|
sigemptyset(&sigact.sa_mask);
|
||
|
|
sigact.sa_flags = 0;
|
||
|
|
sigaction(sig_num, &sigact, NULL);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void print_stats()
|
||
|
|
{
|
||
|
|
if (0 != dd_count)
|
||
|
|
fprintf(stderr, " remaining block count=%d\n", dd_count);
|
||
|
|
fprintf(stderr, "%d+%d records in\n", in_full - in_partial, in_partial);
|
||
|
|
fprintf(stderr, "%d+%d records out\n", out_full - out_partial,
|
||
|
|
out_partial);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void interrupt_handler(int sig)
|
||
|
|
{
|
||
|
|
struct sigaction sigact;
|
||
|
|
|
||
|
|
sigact.sa_handler = SIG_DFL;
|
||
|
|
sigemptyset(&sigact.sa_mask);
|
||
|
|
sigact.sa_flags = 0;
|
||
|
|
sigaction(sig, &sigact, NULL);
|
||
|
|
fprintf(stderr, "Interrupted by signal,");
|
||
|
|
print_stats();
|
||
|
|
kill(getpid(), sig);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void siginfo_handler(int sig)
|
||
|
|
{
|
||
|
|
fprintf(stderr, "Progress report, continuing ...\n");
|
||
|
|
print_stats();
|
||
|
|
}
|
||
|
|
|
||
|
|
int dd_filetype(const char *filename)
|
||
|
|
{
|
||
|
|
struct stat st;
|
||
|
|
size_t len = strlen(filename);
|
||
|
|
|
||
|
|
if ((1 == len) && ('.' == filename[0]))
|
||
|
|
return FT_DEV_NULL;
|
||
|
|
if (stat(filename, &st) < 0)
|
||
|
|
return FT_OTHER;
|
||
|
|
if (S_ISCHR(st.st_mode)) {
|
||
|
|
if ((MEM_MAJOR == major(st.st_rdev)) &&
|
||
|
|
(DEV_NULL_MINOR_NUM == minor(st.st_rdev)))
|
||
|
|
return FT_DEV_NULL;
|
||
|
|
if (RAW_MAJOR == major(st.st_rdev))
|
||
|
|
return FT_RAW;
|
||
|
|
if (SCSI_GENERIC_MAJOR == major(st.st_rdev))
|
||
|
|
return FT_SG;
|
||
|
|
if (SCSI_TAPE_MAJOR == major(st.st_rdev))
|
||
|
|
return FT_ST;
|
||
|
|
} else if (S_ISBLK(st.st_mode))
|
||
|
|
return FT_BLOCK;
|
||
|
|
return FT_OTHER;
|
||
|
|
}
|
||
|
|
|
||
|
|
int read_capacity(int sg_fd, int *num_sect, int *sect_sz)
|
||
|
|
{
|
||
|
|
int res;
|
||
|
|
unsigned char rcCmdBlk[10] =
|
||
|
|
{ READ_CAPACITY, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||
|
|
unsigned char rcBuff[READ_CAP_REPLY_LEN];
|
||
|
|
unsigned char sense_b[64];
|
||
|
|
sg_io_hdr_t io_hdr;
|
||
|
|
|
||
|
|
memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
|
||
|
|
io_hdr.interface_id = 'S';
|
||
|
|
io_hdr.cmd_len = sizeof(rcCmdBlk);
|
||
|
|
io_hdr.mx_sb_len = sizeof(sense_b);
|
||
|
|
io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
|
||
|
|
io_hdr.dxfer_len = sizeof(rcBuff);
|
||
|
|
io_hdr.dxferp = rcBuff;
|
||
|
|
io_hdr.cmdp = rcCmdBlk;
|
||
|
|
io_hdr.sbp = sense_b;
|
||
|
|
io_hdr.timeout = DEF_TIMEOUT;
|
||
|
|
|
||
|
|
if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
|
||
|
|
perror("read_capacity (SG_IO) error");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
res = sg_err_category3(&io_hdr);
|
||
|
|
if (SG_ERR_CAT_MEDIA_CHANGED == res)
|
||
|
|
return 2; /* probably have another go ... */
|
||
|
|
else if (SG_ERR_CAT_CLEAN != res) {
|
||
|
|
sg_chk_n_print3("read capacity", &io_hdr);
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
*num_sect = 1 + ((rcBuff[0] << 24) | (rcBuff[1] << 16) |
|
||
|
|
(rcBuff[2] << 8) | rcBuff[3]);
|
||
|
|
*sect_sz = (rcBuff[4] << 24) | (rcBuff[5] << 16) |
|
||
|
|
(rcBuff[6] << 8) | rcBuff[7];
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Return of 0 -> success, -1 -> failure, 2 -> try again */
|
||
|
|
int sync_cache(int sg_fd)
|
||
|
|
{
|
||
|
|
int res;
|
||
|
|
unsigned char scCmdBlk[10] = { SYNCHRONIZE_CACHE, 0, 0, 0, 0, 0, 0,
|
||
|
|
0, 0, 0
|
||
|
|
};
|
||
|
|
unsigned char sense_b[64];
|
||
|
|
sg_io_hdr_t io_hdr;
|
||
|
|
|
||
|
|
memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
|
||
|
|
io_hdr.interface_id = 'S';
|
||
|
|
io_hdr.cmd_len = sizeof(scCmdBlk);
|
||
|
|
io_hdr.mx_sb_len = sizeof(sense_b);
|
||
|
|
io_hdr.dxfer_direction = SG_DXFER_NONE;
|
||
|
|
io_hdr.dxfer_len = 0;
|
||
|
|
io_hdr.dxferp = NULL;
|
||
|
|
io_hdr.cmdp = scCmdBlk;
|
||
|
|
io_hdr.sbp = sense_b;
|
||
|
|
io_hdr.timeout = DEF_TIMEOUT;
|
||
|
|
|
||
|
|
if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
|
||
|
|
perror("synchronize_cache (SG_IO) error");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
res = sg_err_category3(&io_hdr);
|
||
|
|
if (SG_ERR_CAT_MEDIA_CHANGED == res)
|
||
|
|
return 2; /* probably have another go ... */
|
||
|
|
else if (SG_ERR_CAT_CLEAN != res) {
|
||
|
|
sg_chk_n_print3("synchronize cache", &io_hdr);
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
int sg_build_scsi_cdb(unsigned char *cdbp, int cdb_sz, unsigned int blocks,
|
||
|
|
unsigned int start_block, int write_true, int fua,
|
||
|
|
int dpo)
|
||
|
|
{
|
||
|
|
int rd_opcode[] = { 0x8, 0x28, 0xa8, 0x88 };
|
||
|
|
int wr_opcode[] = { 0xa, 0x2a, 0xaa, 0x8a };
|
||
|
|
int sz_ind;
|
||
|
|
|
||
|
|
memset(cdbp, 0, cdb_sz);
|
||
|
|
if (dpo)
|
||
|
|
cdbp[1] |= 0x10;
|
||
|
|
if (fua)
|
||
|
|
cdbp[1] |= 0x8;
|
||
|
|
switch (cdb_sz) {
|
||
|
|
case 6:
|
||
|
|
sz_ind = 0;
|
||
|
|
cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] :
|
||
|
|
rd_opcode[sz_ind]);
|
||
|
|
cdbp[1] = (unsigned char)((start_block >> 16) & 0x1f);
|
||
|
|
cdbp[2] = (unsigned char)((start_block >> 8) & 0xff);
|
||
|
|
cdbp[3] = (unsigned char)(start_block & 0xff);
|
||
|
|
cdbp[4] = (256 == blocks) ? 0 : (unsigned char)blocks;
|
||
|
|
if (blocks > 256) {
|
||
|
|
fprintf(stderr,
|
||
|
|
ME "for 6 byte commands, maximum number of "
|
||
|
|
"blocks is 256\n");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
if ((start_block + blocks - 1) & (~0x1fffff)) {
|
||
|
|
fprintf(stderr,
|
||
|
|
ME "for 6 byte commands, can't address blocks"
|
||
|
|
" beyond %d\n", 0x1fffff);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
if (dpo || fua) {
|
||
|
|
fprintf(stderr,
|
||
|
|
ME "for 6 byte commands, neither dpo nor fua"
|
||
|
|
" bits supported\n");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
case 10:
|
||
|
|
sz_ind = 1;
|
||
|
|
cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] :
|
||
|
|
rd_opcode[sz_ind]);
|
||
|
|
cdbp[2] = (unsigned char)((start_block >> 24) & 0xff);
|
||
|
|
cdbp[3] = (unsigned char)((start_block >> 16) & 0xff);
|
||
|
|
cdbp[4] = (unsigned char)((start_block >> 8) & 0xff);
|
||
|
|
cdbp[5] = (unsigned char)(start_block & 0xff);
|
||
|
|
cdbp[7] = (unsigned char)((blocks >> 8) & 0xff);
|
||
|
|
cdbp[8] = (unsigned char)(blocks & 0xff);
|
||
|
|
if (blocks & (~0xffff)) {
|
||
|
|
fprintf(stderr,
|
||
|
|
ME "for 10 byte commands, maximum number of "
|
||
|
|
"blocks is %d\n", 0xffff);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
case 12:
|
||
|
|
sz_ind = 2;
|
||
|
|
cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] :
|
||
|
|
rd_opcode[sz_ind]);
|
||
|
|
cdbp[2] = (unsigned char)((start_block >> 24) & 0xff);
|
||
|
|
cdbp[3] = (unsigned char)((start_block >> 16) & 0xff);
|
||
|
|
cdbp[4] = (unsigned char)((start_block >> 8) & 0xff);
|
||
|
|
cdbp[5] = (unsigned char)(start_block & 0xff);
|
||
|
|
cdbp[6] = (unsigned char)((blocks >> 24) & 0xff);
|
||
|
|
cdbp[7] = (unsigned char)((blocks >> 16) & 0xff);
|
||
|
|
cdbp[8] = (unsigned char)((blocks >> 8) & 0xff);
|
||
|
|
cdbp[9] = (unsigned char)(blocks & 0xff);
|
||
|
|
break;
|
||
|
|
case 16:
|
||
|
|
sz_ind = 3;
|
||
|
|
cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] :
|
||
|
|
rd_opcode[sz_ind]);
|
||
|
|
/* can't cope with block number > 32 bits (yet) */
|
||
|
|
cdbp[6] = (unsigned char)((start_block >> 24) & 0xff);
|
||
|
|
cdbp[7] = (unsigned char)((start_block >> 16) & 0xff);
|
||
|
|
cdbp[8] = (unsigned char)((start_block >> 8) & 0xff);
|
||
|
|
cdbp[9] = (unsigned char)(start_block & 0xff);
|
||
|
|
cdbp[10] = (unsigned char)((blocks >> 24) & 0xff);
|
||
|
|
cdbp[11] = (unsigned char)((blocks >> 16) & 0xff);
|
||
|
|
cdbp[12] = (unsigned char)((blocks >> 8) & 0xff);
|
||
|
|
cdbp[13] = (unsigned char)(blocks & 0xff);
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
fprintf(stderr,
|
||
|
|
ME "expected cdb size of 6, 10, 12, or 16 but got"
|
||
|
|
"=%d\n", cdb_sz);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* -1 -> unrecoverable error, 0 -> successful, 1 -> recoverable (ENOMEM),
|
||
|
|
2 -> try again */
|
||
|
|
int sg_read(int sg_fd, unsigned char *buff, int blocks, int from_block,
|
||
|
|
int bs, int cdbsz, int fua, int *diop)
|
||
|
|
{
|
||
|
|
unsigned char rdCmd[MAX_SCSI_CDBSZ];
|
||
|
|
unsigned char senseBuff[SENSE_BUFF_LEN];
|
||
|
|
sg_io_hdr_t io_hdr;
|
||
|
|
|
||
|
|
if (sg_build_scsi_cdb(rdCmd, cdbsz, blocks, from_block, 0, fua, 0)) {
|
||
|
|
fprintf(stderr,
|
||
|
|
ME "bad rd cdb build, from_block=%d, blocks=%d\n",
|
||
|
|
from_block, blocks);
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
|
||
|
|
io_hdr.interface_id = 'S';
|
||
|
|
io_hdr.cmd_len = cdbsz;
|
||
|
|
io_hdr.cmdp = rdCmd;
|
||
|
|
io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
|
||
|
|
io_hdr.dxfer_len = bs * blocks;
|
||
|
|
io_hdr.dxferp = buff;
|
||
|
|
io_hdr.mx_sb_len = SENSE_BUFF_LEN;
|
||
|
|
io_hdr.sbp = senseBuff;
|
||
|
|
io_hdr.timeout = DEF_TIMEOUT;
|
||
|
|
io_hdr.pack_id = from_block;
|
||
|
|
if (diop && *diop)
|
||
|
|
io_hdr.flags |= SG_FLAG_DIRECT_IO;
|
||
|
|
|
||
|
|
if (ioctl(sg_fd, SG_IO, &io_hdr)) {
|
||
|
|
if (ENOMEM == errno)
|
||
|
|
return 1;
|
||
|
|
perror("reading (SG_IO) on sg device, error");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
switch (sg_err_category3(&io_hdr)) {
|
||
|
|
case SG_ERR_CAT_CLEAN:
|
||
|
|
break;
|
||
|
|
case SG_ERR_CAT_RECOVERED:
|
||
|
|
fprintf(stderr,
|
||
|
|
"Recovered error while reading block=%d, num=%d\n",
|
||
|
|
from_block, blocks);
|
||
|
|
break;
|
||
|
|
case SG_ERR_CAT_MEDIA_CHANGED:
|
||
|
|
return 2;
|
||
|
|
default:
|
||
|
|
sg_chk_n_print3("reading", &io_hdr);
|
||
|
|
if (do_coe) {
|
||
|
|
memset(buff, 0, bs * blocks);
|
||
|
|
fprintf(stderr, ">> unable to read at blk=%d for "
|
||
|
|
"%d bytes, use zeros\n", from_block,
|
||
|
|
bs * blocks);
|
||
|
|
return 0; /* fudge success */
|
||
|
|
} else
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
if (diop && *diop &&
|
||
|
|
((io_hdr.info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO))
|
||
|
|
*diop = 0; /* flag that dio not done (completely) */
|
||
|
|
sum_of_resids += io_hdr.resid;
|
||
|
|
#if SG_DEBUG
|
||
|
|
fprintf(stderr, "duration=%u ms\n", io_hdr.duration);
|
||
|
|
#endif
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* -1 -> unrecoverable error, 0 -> successful, 1 -> recoverable (ENOMEM),
|
||
|
|
2 -> try again */
|
||
|
|
int sg_write(int sg_fd, unsigned char *buff, int blocks, int to_block,
|
||
|
|
int bs, int cdbsz, int fua, int *diop)
|
||
|
|
{
|
||
|
|
unsigned char wrCmd[MAX_SCSI_CDBSZ];
|
||
|
|
unsigned char senseBuff[SENSE_BUFF_LEN];
|
||
|
|
sg_io_hdr_t io_hdr;
|
||
|
|
|
||
|
|
if (sg_build_scsi_cdb(wrCmd, cdbsz, blocks, to_block, 1, fua, 0)) {
|
||
|
|
fprintf(stderr, ME "bad wr cdb build, to_block=%d, blocks=%d\n",
|
||
|
|
to_block, blocks);
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
|
||
|
|
io_hdr.interface_id = 'S';
|
||
|
|
io_hdr.cmd_len = cdbsz;
|
||
|
|
io_hdr.cmdp = wrCmd;
|
||
|
|
io_hdr.dxfer_direction = SG_DXFER_TO_DEV;
|
||
|
|
io_hdr.dxfer_len = bs * blocks;
|
||
|
|
io_hdr.dxferp = buff;
|
||
|
|
io_hdr.mx_sb_len = SENSE_BUFF_LEN;
|
||
|
|
io_hdr.sbp = senseBuff;
|
||
|
|
io_hdr.timeout = DEF_TIMEOUT;
|
||
|
|
io_hdr.pack_id = to_block;
|
||
|
|
if (diop && *diop)
|
||
|
|
io_hdr.flags |= SG_FLAG_DIRECT_IO;
|
||
|
|
|
||
|
|
if (ioctl(sg_fd, SG_IO, &io_hdr)) {
|
||
|
|
if (ENOMEM == errno)
|
||
|
|
return 1;
|
||
|
|
perror("writing (SG_IO) on sg device, error");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
switch (sg_err_category3(&io_hdr)) {
|
||
|
|
case SG_ERR_CAT_CLEAN:
|
||
|
|
break;
|
||
|
|
case SG_ERR_CAT_RECOVERED:
|
||
|
|
fprintf(stderr,
|
||
|
|
"Recovered error while writing block=%d, num=%d\n",
|
||
|
|
to_block, blocks);
|
||
|
|
break;
|
||
|
|
case SG_ERR_CAT_MEDIA_CHANGED:
|
||
|
|
return 2;
|
||
|
|
default:
|
||
|
|
sg_chk_n_print3("writing", &io_hdr);
|
||
|
|
if (do_coe) {
|
||
|
|
fprintf(stderr, ">> ignored errors for out blk=%d for "
|
||
|
|
"%d bytes\n", to_block, bs * blocks);
|
||
|
|
return 0; /* fudge success */
|
||
|
|
} else
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
if (diop && *diop &&
|
||
|
|
((io_hdr.info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO))
|
||
|
|
*diop = 0; /* flag that dio not done (completely) */
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
int get_num(char *buf)
|
||
|
|
{
|
||
|
|
int res, num;
|
||
|
|
char c;
|
||
|
|
|
||
|
|
res = sscanf(buf, "%d%c", &num, &c);
|
||
|
|
if (0 == res)
|
||
|
|
return -1;
|
||
|
|
else if (1 == res)
|
||
|
|
return num;
|
||
|
|
else {
|
||
|
|
switch (c) {
|
||
|
|
case 'c':
|
||
|
|
case 'C':
|
||
|
|
return num;
|
||
|
|
case 'b':
|
||
|
|
case 'B':
|
||
|
|
return num * 512;
|
||
|
|
case 'k':
|
||
|
|
return num * 1024;
|
||
|
|
case 'K':
|
||
|
|
return num * 1000;
|
||
|
|
case 'm':
|
||
|
|
return num * 1024 * 1024;
|
||
|
|
case 'M':
|
||
|
|
return num * 1000000;
|
||
|
|
case 'g':
|
||
|
|
return num * 1024 * 1024 * 1024;
|
||
|
|
case 'G':
|
||
|
|
return num * 1000000000;
|
||
|
|
default:
|
||
|
|
fprintf(stderr, "unrecognized multiplier\n");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
int do_scsi_device_read_write(char *device)
|
||
|
|
{
|
||
|
|
int skip = 0;
|
||
|
|
int seek = 0;
|
||
|
|
int bs = 0;
|
||
|
|
int ibs = 0;
|
||
|
|
int obs = 0;
|
||
|
|
int bpt = DEF_BLOCKS_PER_TRANSFER;
|
||
|
|
char inf[INOUTF_SZ];
|
||
|
|
int in_type = FT_OTHER;
|
||
|
|
char outf[INOUTF_SZ];
|
||
|
|
int out_type = FT_OTHER;
|
||
|
|
int dio = 0;
|
||
|
|
int dio_incomplete = 0;
|
||
|
|
int do_time = 1;
|
||
|
|
int do_odir = 1;
|
||
|
|
int scsi_cdbsz = DEF_SCSI_CDBSZ;
|
||
|
|
int fua_mode = 0;
|
||
|
|
int do_sync = 1;
|
||
|
|
int do_blk_sgio = 1;
|
||
|
|
int do_append = 1;
|
||
|
|
int res, t, buf_sz, dio_tmp;
|
||
|
|
int infd, outfd, blocks;
|
||
|
|
unsigned char *wrkBuff;
|
||
|
|
unsigned char *wrkPos;
|
||
|
|
int in_num_sect = 0;
|
||
|
|
int out_num_sect = 0;
|
||
|
|
int in_sect_sz, out_sect_sz;
|
||
|
|
char ebuff[EBUFF_SZ];
|
||
|
|
int blocks_per;
|
||
|
|
int req_count;
|
||
|
|
struct timeval start_tm, end_tm;
|
||
|
|
|
||
|
|
print_msg(TEST_BREAK, __FUNCTION__);
|
||
|
|
strcpy(inf, "/dev/zero");
|
||
|
|
strcpy(outf, device);
|
||
|
|
|
||
|
|
if (bs <= 0) {
|
||
|
|
bs = DEF_BLOCK_SIZE;
|
||
|
|
fprintf(stderr,
|
||
|
|
"Assume default 'bs' (block size) of %d bytes\n", bs);
|
||
|
|
}
|
||
|
|
if ((ibs && (ibs != bs)) || (obs && (obs != bs))) {
|
||
|
|
fprintf(stderr,
|
||
|
|
"If 'ibs' or 'obs' given must be same as 'bs'\n");
|
||
|
|
usage();
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
if ((skip < 0) || (seek < 0)) {
|
||
|
|
fprintf(stderr, "skip and seek cannot be negative\n");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
if ((do_append > 0) && (seek > 0)) {
|
||
|
|
fprintf(stderr, "Can't use both append and seek switches\n");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
#ifdef SG_DEBUG
|
||
|
|
fprintf(stderr, ME "if=%s skip=%d of=%s seek=%d count=%d\n",
|
||
|
|
inf, skip, outf, seek, dd_count);
|
||
|
|
#endif
|
||
|
|
install_handler(SIGINT, interrupt_handler);
|
||
|
|
install_handler(SIGQUIT, interrupt_handler);
|
||
|
|
install_handler(SIGPIPE, interrupt_handler);
|
||
|
|
install_handler(SIGUSR1, siginfo_handler);
|
||
|
|
|
||
|
|
infd = STDIN_FILENO;
|
||
|
|
outfd = STDOUT_FILENO;
|
||
|
|
if (inf[0] && ('-' != inf[0])) {
|
||
|
|
in_type = dd_filetype(inf);
|
||
|
|
|
||
|
|
if ((FT_BLOCK & in_type) && do_blk_sgio)
|
||
|
|
in_type |= FT_SG;
|
||
|
|
|
||
|
|
if (FT_ST == in_type) {
|
||
|
|
fprintf(stderr,
|
||
|
|
ME "unable to use scsi tape device %s\n", inf);
|
||
|
|
return 1;
|
||
|
|
} else if (FT_SG & in_type) {
|
||
|
|
if ((infd = open(inf, O_RDWR)) < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ,
|
||
|
|
ME "could not open %s for sg reading",
|
||
|
|
inf);
|
||
|
|
perror(ebuff);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
t = bs * bpt;
|
||
|
|
res = ioctl(infd, SG_SET_RESERVED_SIZE, &t);
|
||
|
|
if (res < 0)
|
||
|
|
perror(ME "SG_SET_RESERVED_SIZE error");
|
||
|
|
res = ioctl(infd, SG_GET_VERSION_NUM, &t);
|
||
|
|
if ((res < 0) || (t < 30000)) {
|
||
|
|
if (FT_BLOCK & in_type)
|
||
|
|
fprintf(stderr,
|
||
|
|
ME
|
||
|
|
"SG_IO unsupported on this block"
|
||
|
|
" device\n");
|
||
|
|
else
|
||
|
|
fprintf(stderr,
|
||
|
|
ME
|
||
|
|
"sg driver prior to 3.x.y\n");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
if (do_odir && (FT_BLOCK == in_type))
|
||
|
|
infd = open(inf, O_RDONLY | O_DIRECT);
|
||
|
|
else
|
||
|
|
infd = open(inf, O_RDONLY);
|
||
|
|
if (infd < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ,
|
||
|
|
ME "could not open %s for reading",
|
||
|
|
inf);
|
||
|
|
perror(ebuff);
|
||
|
|
return 1;
|
||
|
|
} else if (skip > 0) {
|
||
|
|
llse_loff_t offset = skip;
|
||
|
|
|
||
|
|
offset *= bs; /* could exceed 32 bits here! */
|
||
|
|
if (llse_llseek(infd, offset, SEEK_SET) < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ,
|
||
|
|
ME
|
||
|
|
"couldn't skip to required position on %s",
|
||
|
|
inf);
|
||
|
|
perror(ebuff);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (outf[0] && ('-' != outf[0])) {
|
||
|
|
out_type = dd_filetype(outf);
|
||
|
|
|
||
|
|
if ((FT_BLOCK & out_type) && do_blk_sgio)
|
||
|
|
out_type |= FT_SG;
|
||
|
|
|
||
|
|
if (FT_ST == out_type) {
|
||
|
|
fprintf(stderr,
|
||
|
|
ME "unable to use scsi tape device %s\n", outf);
|
||
|
|
return 1;
|
||
|
|
} else if (FT_SG & out_type) {
|
||
|
|
if ((outfd = open(outf, O_RDWR)) < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ,
|
||
|
|
ME "could not open %s for sg writing",
|
||
|
|
outf);
|
||
|
|
perror(ebuff);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
t = bs * bpt;
|
||
|
|
res = ioctl(outfd, SG_SET_RESERVED_SIZE, &t);
|
||
|
|
if (res < 0)
|
||
|
|
perror(ME "SG_SET_RESERVED_SIZE error");
|
||
|
|
res = ioctl(outfd, SG_GET_VERSION_NUM, &t);
|
||
|
|
if ((res < 0) || (t < 30000)) {
|
||
|
|
fprintf(stderr,
|
||
|
|
ME "sg driver prior to 3.x.y\n");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
} else if (FT_DEV_NULL & out_type)
|
||
|
|
outfd = -1; /* don't bother opening */
|
||
|
|
else {
|
||
|
|
if (FT_RAW != out_type) {
|
||
|
|
int flags = O_WRONLY | O_CREAT;
|
||
|
|
|
||
|
|
if (do_odir && (FT_BLOCK == out_type))
|
||
|
|
flags |= O_DIRECT;
|
||
|
|
else if (do_append)
|
||
|
|
flags |= O_APPEND;
|
||
|
|
if ((outfd = open(outf, flags, 0666)) < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ,
|
||
|
|
ME
|
||
|
|
"could not open %s for writing",
|
||
|
|
outf);
|
||
|
|
perror(ebuff);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
if ((outfd = open(outf, O_WRONLY)) < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ,
|
||
|
|
ME
|
||
|
|
"could not open %s for raw writing",
|
||
|
|
outf);
|
||
|
|
perror(ebuff);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (seek > 0) {
|
||
|
|
llse_loff_t offset = seek;
|
||
|
|
|
||
|
|
offset *= bs; /* could exceed 32 bits here! */
|
||
|
|
if (llse_llseek(outfd, offset, SEEK_SET) < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ,
|
||
|
|
ME
|
||
|
|
"couldn't seek to required position on %s",
|
||
|
|
outf);
|
||
|
|
perror(ebuff);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if ((STDIN_FILENO == infd) && (STDOUT_FILENO == outfd)) {
|
||
|
|
fprintf(stderr,
|
||
|
|
"Can't have both 'if' as stdin _and_ 'of' as stdout\n");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (dd_count < 0) {
|
||
|
|
if (FT_SG & in_type) {
|
||
|
|
res = read_capacity(infd, &in_num_sect, &in_sect_sz);
|
||
|
|
if (2 == res) {
|
||
|
|
fprintf(stderr,
|
||
|
|
"Unit attention, media changed(in), continuing\n");
|
||
|
|
res =
|
||
|
|
read_capacity(infd, &in_num_sect,
|
||
|
|
&in_sect_sz);
|
||
|
|
}
|
||
|
|
if (0 != res) {
|
||
|
|
fprintf(stderr,
|
||
|
|
"Unable to read capacity on %s\n", inf);
|
||
|
|
in_num_sect = -1;
|
||
|
|
} else {
|
||
|
|
if (in_num_sect > skip)
|
||
|
|
in_num_sect -= skip;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (FT_SG & out_type) {
|
||
|
|
res = read_capacity(outfd, &out_num_sect, &out_sect_sz);
|
||
|
|
if (2 == res) {
|
||
|
|
fprintf(stderr,
|
||
|
|
"Unit attention, media changed(out), continuing\n");
|
||
|
|
res =
|
||
|
|
read_capacity(outfd, &out_num_sect,
|
||
|
|
&out_sect_sz);
|
||
|
|
}
|
||
|
|
if (0 != res) {
|
||
|
|
fprintf(stderr,
|
||
|
|
"Unable to read capacity on %s\n",
|
||
|
|
outf);
|
||
|
|
out_num_sect = -1;
|
||
|
|
} else {
|
||
|
|
if (out_num_sect > seek)
|
||
|
|
out_num_sect -= seek;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
#ifdef SG_DEBUG
|
||
|
|
fprintf(stderr,
|
||
|
|
"Start of loop, count=%d, in_num_sect=%d, out_num_sect=%d\n",
|
||
|
|
dd_count, in_num_sect, out_num_sect);
|
||
|
|
#endif
|
||
|
|
if (in_num_sect > 0) {
|
||
|
|
if (out_num_sect > 0)
|
||
|
|
dd_count =
|
||
|
|
(in_num_sect >
|
||
|
|
out_num_sect) ? out_num_sect : in_num_sect;
|
||
|
|
else
|
||
|
|
dd_count = in_num_sect;
|
||
|
|
} else
|
||
|
|
dd_count = out_num_sect;
|
||
|
|
}
|
||
|
|
if (dd_count < 0) {
|
||
|
|
fprintf(stderr, "Couldn't calculate count, please give one\n");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (dio || do_odir || (FT_RAW == in_type) || (FT_RAW == out_type)) {
|
||
|
|
size_t psz = getpagesize();
|
||
|
|
wrkBuff = malloc(bs * bpt + psz);
|
||
|
|
if (0 == wrkBuff) {
|
||
|
|
fprintf(stderr, "Not enough user memory for raw\n");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
wrkPos = (unsigned char *)(((unsigned long)wrkBuff + psz - 1) &
|
||
|
|
(~(psz - 1)));
|
||
|
|
} else {
|
||
|
|
wrkBuff = malloc(bs * bpt);
|
||
|
|
if (0 == wrkBuff) {
|
||
|
|
fprintf(stderr, "Not enough user memory\n");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
wrkPos = wrkBuff;
|
||
|
|
}
|
||
|
|
|
||
|
|
blocks_per = bpt;
|
||
|
|
#ifdef SG_DEBUG
|
||
|
|
fprintf(stderr, "Start of loop, count=%d, blocks_per=%d\n",
|
||
|
|
dd_count, blocks_per);
|
||
|
|
#endif
|
||
|
|
if (do_time) {
|
||
|
|
start_tm.tv_sec = 0;
|
||
|
|
start_tm.tv_usec = 0;
|
||
|
|
gettimeofday(&start_tm, NULL);
|
||
|
|
}
|
||
|
|
req_count = dd_count;
|
||
|
|
|
||
|
|
while (dd_count > 0) {
|
||
|
|
blocks = (dd_count > blocks_per) ? blocks_per : dd_count;
|
||
|
|
if (FT_SG & in_type) {
|
||
|
|
int fua = fua_mode & 2;
|
||
|
|
|
||
|
|
dio_tmp = dio;
|
||
|
|
res =
|
||
|
|
sg_read(infd, wrkPos, blocks, skip, bs, scsi_cdbsz,
|
||
|
|
fua, &dio_tmp);
|
||
|
|
if (1 == res) { /* ENOMEM, find what's available+try that */
|
||
|
|
if (ioctl(infd, SG_GET_RESERVED_SIZE, &buf_sz) <
|
||
|
|
0) {
|
||
|
|
perror("RESERVED_SIZE ioctls failed");
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
blocks_per = (buf_sz + bs - 1) / bs;
|
||
|
|
blocks = blocks_per;
|
||
|
|
fprintf(stderr,
|
||
|
|
"Reducing read to %d blocks per loop\n",
|
||
|
|
blocks_per);
|
||
|
|
res =
|
||
|
|
sg_read(infd, wrkPos, blocks, skip, bs,
|
||
|
|
scsi_cdbsz, fua, &dio_tmp);
|
||
|
|
} else if (2 == res) {
|
||
|
|
fprintf(stderr,
|
||
|
|
"Unit attention, media changed, continuing (r)\n");
|
||
|
|
res =
|
||
|
|
sg_read(infd, wrkPos, blocks, skip, bs,
|
||
|
|
scsi_cdbsz, fua, &dio_tmp);
|
||
|
|
}
|
||
|
|
if (0 != res) {
|
||
|
|
fprintf(stderr, "sg_read failed, skip=%d\n",
|
||
|
|
skip);
|
||
|
|
break;
|
||
|
|
} else {
|
||
|
|
in_full += blocks;
|
||
|
|
if (dio && (0 == dio_tmp))
|
||
|
|
dio_incomplete++;
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
while (((res = read(infd, wrkPos, blocks * bs)) < 0) &&
|
||
|
|
(EINTR == errno)) ;
|
||
|
|
if (res < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ,
|
||
|
|
ME "reading, skip=%d ", skip);
|
||
|
|
perror(ebuff);
|
||
|
|
break;
|
||
|
|
} else if (res < blocks * bs) {
|
||
|
|
dd_count = 0;
|
||
|
|
blocks = res / bs;
|
||
|
|
if ((res % bs) > 0) {
|
||
|
|
blocks++;
|
||
|
|
in_partial++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
in_full += blocks;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (FT_SG & out_type) {
|
||
|
|
int fua = fua_mode & 1;
|
||
|
|
|
||
|
|
dio_tmp = dio;
|
||
|
|
res =
|
||
|
|
sg_write(outfd, wrkPos, blocks, seek, bs,
|
||
|
|
scsi_cdbsz, fua, &dio_tmp);
|
||
|
|
if (1 == res) { /* ENOMEM, find what's available+try that */
|
||
|
|
if (ioctl(outfd, SG_GET_RESERVED_SIZE, &buf_sz)
|
||
|
|
< 0) {
|
||
|
|
perror("RESERVED_SIZE ioctls failed");
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
blocks_per = (buf_sz + bs - 1) / bs;
|
||
|
|
blocks = blocks_per;
|
||
|
|
fprintf(stderr,
|
||
|
|
"Reducing write to %d blocks per loop\n",
|
||
|
|
blocks);
|
||
|
|
res =
|
||
|
|
sg_write(outfd, wrkPos, blocks, seek, bs,
|
||
|
|
scsi_cdbsz, fua, &dio_tmp);
|
||
|
|
} else if (2 == res) {
|
||
|
|
fprintf(stderr,
|
||
|
|
"Unit attention, media changed, continuing (w)\n");
|
||
|
|
res =
|
||
|
|
sg_write(outfd, wrkPos, blocks, seek, bs,
|
||
|
|
scsi_cdbsz, fua, &dio_tmp);
|
||
|
|
} else if (0 != res) {
|
||
|
|
fprintf(stderr, "sg_write failed, seek=%d\n",
|
||
|
|
seek);
|
||
|
|
break;
|
||
|
|
} else {
|
||
|
|
out_full += blocks;
|
||
|
|
if (dio && (0 == dio_tmp))
|
||
|
|
dio_incomplete++;
|
||
|
|
}
|
||
|
|
} else if (FT_DEV_NULL & out_type)
|
||
|
|
out_full += blocks; /* act as if written out without error */
|
||
|
|
else {
|
||
|
|
while (((res = write(outfd, wrkPos, blocks * bs)) < 0)
|
||
|
|
&& (EINTR == errno)) ;
|
||
|
|
if (res < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ,
|
||
|
|
ME "writing, seek=%d ", seek);
|
||
|
|
perror(ebuff);
|
||
|
|
break;
|
||
|
|
} else if (res < blocks * bs) {
|
||
|
|
fprintf(stderr,
|
||
|
|
"output file probably full, seek=%d ",
|
||
|
|
seek);
|
||
|
|
blocks = res / bs;
|
||
|
|
out_full += blocks;
|
||
|
|
if ((res % bs) > 0)
|
||
|
|
out_partial++;
|
||
|
|
break;
|
||
|
|
} else
|
||
|
|
out_full += blocks;
|
||
|
|
}
|
||
|
|
if (dd_count > 0)
|
||
|
|
dd_count -= blocks;
|
||
|
|
skip += blocks;
|
||
|
|
seek += blocks;
|
||
|
|
}
|
||
|
|
if ((do_time) && (start_tm.tv_sec || start_tm.tv_usec)) {
|
||
|
|
struct timeval res_tm;
|
||
|
|
double a, b;
|
||
|
|
|
||
|
|
gettimeofday(&end_tm, NULL);
|
||
|
|
res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec;
|
||
|
|
res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec;
|
||
|
|
if (res_tm.tv_usec < 0) {
|
||
|
|
--res_tm.tv_sec;
|
||
|
|
res_tm.tv_usec += 1000000;
|
||
|
|
}
|
||
|
|
a = res_tm.tv_sec;
|
||
|
|
a += (0.000001 * res_tm.tv_usec);
|
||
|
|
b = (double)bs *(req_count - dd_count);
|
||
|
|
printf("time to transfer data was %d.%06d secs",
|
||
|
|
(int)res_tm.tv_sec, (int)res_tm.tv_usec);
|
||
|
|
if ((a > 0.00001) && (b > 511))
|
||
|
|
printf(", %.2f MB/sec\n", b / (a * 1000000.0));
|
||
|
|
else
|
||
|
|
printf("\n");
|
||
|
|
}
|
||
|
|
if (do_sync) {
|
||
|
|
if (FT_SG & out_type) {
|
||
|
|
fprintf(stderr, ">> Synchronizing cache on %s\n", outf);
|
||
|
|
res = sync_cache(outfd);
|
||
|
|
if (2 == res) {
|
||
|
|
fprintf(stderr,
|
||
|
|
"Unit attention, media changed(in), continuing\n");
|
||
|
|
res = sync_cache(outfd);
|
||
|
|
}
|
||
|
|
if (0 != res)
|
||
|
|
fprintf(stderr,
|
||
|
|
"Unable to synchronize cache\n");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
free(wrkBuff);
|
||
|
|
if (STDIN_FILENO != infd)
|
||
|
|
close(infd);
|
||
|
|
if ((STDOUT_FILENO != outfd) && (FT_DEV_NULL != out_type))
|
||
|
|
close(outfd);
|
||
|
|
res = 0;
|
||
|
|
if (0 != dd_count) {
|
||
|
|
fprintf(stderr, "Some error occurred,");
|
||
|
|
res = 2;
|
||
|
|
}
|
||
|
|
print_stats();
|
||
|
|
if (dio_incomplete) {
|
||
|
|
int fd;
|
||
|
|
char c;
|
||
|
|
|
||
|
|
fprintf(stderr,
|
||
|
|
">> Direct IO requested but incomplete %d times\n",
|
||
|
|
dio_incomplete);
|
||
|
|
if ((fd = open(proc_allow_dio, O_RDONLY)) >= 0) {
|
||
|
|
if (1 == read(fd, &c, 1)) {
|
||
|
|
if ('0' == c)
|
||
|
|
fprintf(stderr,
|
||
|
|
">>> %s set to '0' but should be set "
|
||
|
|
"to '1' for direct IO\n",
|
||
|
|
proc_allow_dio);
|
||
|
|
}
|
||
|
|
close(fd);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (sum_of_resids)
|
||
|
|
fprintf(stderr, ">> Non-zero sum of residual counts=%d\n",
|
||
|
|
sum_of_resids);
|
||
|
|
return res;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Returns 0 when successful, else -1 */
|
||
|
|
static int do_scsi_inq(int sg_fd, int cmddt, int evpd, unsigned int pg_op,
|
||
|
|
void *resp, int mx_resp_len, int noisy)
|
||
|
|
{
|
||
|
|
int res;
|
||
|
|
unsigned char inqCmdBlk[INQUIRY_CMDLEN] =
|
||
|
|
{ INQUIRY_CMD, 0, 0, 0, 0, 0 };
|
||
|
|
unsigned char sense_b[SENSE_BUFF_LEN];
|
||
|
|
sg_io_hdr_t io_hdr;
|
||
|
|
|
||
|
|
if (cmddt)
|
||
|
|
inqCmdBlk[1] |= 2;
|
||
|
|
if (evpd)
|
||
|
|
inqCmdBlk[1] |= 1;
|
||
|
|
inqCmdBlk[2] = (unsigned char)pg_op;
|
||
|
|
inqCmdBlk[4] = (unsigned char)mx_resp_len;
|
||
|
|
memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
|
||
|
|
io_hdr.interface_id = 'S';
|
||
|
|
io_hdr.cmd_len = sizeof(inqCmdBlk);
|
||
|
|
io_hdr.mx_sb_len = sizeof(sense_b);
|
||
|
|
io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
|
||
|
|
io_hdr.dxfer_len = mx_resp_len;
|
||
|
|
io_hdr.dxferp = resp;
|
||
|
|
io_hdr.cmdp = inqCmdBlk;
|
||
|
|
io_hdr.sbp = sense_b;
|
||
|
|
io_hdr.timeout = DEF_TIMEOUT;
|
||
|
|
|
||
|
|
if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
|
||
|
|
perror("SG_IO (inquiry) error");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
res = sg_err_category3(&io_hdr);
|
||
|
|
switch (res) {
|
||
|
|
case SG_ERR_CAT_CLEAN:
|
||
|
|
case SG_ERR_CAT_RECOVERED:
|
||
|
|
return 0;
|
||
|
|
default:
|
||
|
|
if (noisy) {
|
||
|
|
char ebuff[EBUFF_SZ];
|
||
|
|
snprintf(ebuff, EBUFF_SZ, "Inquiry error, CmdDt=%d, "
|
||
|
|
"EVPD=%d, page_opcode=%x ", cmddt, evpd,
|
||
|
|
pg_op);
|
||
|
|
sg_chk_n_print3(ebuff, &io_hdr);
|
||
|
|
}
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
int do_scsi_inquiry(char *device, int hex_flag)
|
||
|
|
{
|
||
|
|
int sg_fd, k, j, num, len, act_len;
|
||
|
|
int support_num;
|
||
|
|
char *file_name = 0;
|
||
|
|
char buff[MX_ALLOC_LEN + 1];
|
||
|
|
unsigned char rsp_buff[MX_ALLOC_LEN + 1];
|
||
|
|
unsigned int num_opcode = 0;
|
||
|
|
int do_evpd = 0;
|
||
|
|
int do_cmddt = 0;
|
||
|
|
int do_cmdlst = 0;
|
||
|
|
int do_hex = 0;
|
||
|
|
int do_raw = 0;
|
||
|
|
int do_pci = 0;
|
||
|
|
int do_36 = 0;
|
||
|
|
int oflags = O_RDONLY | O_NONBLOCK;
|
||
|
|
int ansi_version = 0;
|
||
|
|
int ret = 0;
|
||
|
|
|
||
|
|
file_name = device;
|
||
|
|
|
||
|
|
if (hex_flag) {
|
||
|
|
do_hex = TRUE;
|
||
|
|
print_msg(TEST_BREAK, __FUNCTION__);
|
||
|
|
} else {
|
||
|
|
do_pci = TRUE;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (do_pci)
|
||
|
|
oflags = O_RDWR | O_NONBLOCK;
|
||
|
|
if ((sg_fd = open(file_name, oflags)) < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ, "sg_inq: error opening file: %s",
|
||
|
|
file_name);
|
||
|
|
perror(ebuff);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
/* Just to be safe, check we have a new sg device by trying an ioctl */
|
||
|
|
if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) {
|
||
|
|
fprintf(stderr,
|
||
|
|
"sg_inq: %s doesn't seem to be a version 3 sg device\n",
|
||
|
|
file_name);
|
||
|
|
close(sg_fd);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
memset(rsp_buff, 0, MX_ALLOC_LEN + 1);
|
||
|
|
|
||
|
|
if (!(do_cmddt || do_evpd)) {
|
||
|
|
if (!do_raw)
|
||
|
|
printf("standard INQUIRY:\n");
|
||
|
|
if (num_opcode > 0)
|
||
|
|
printf
|
||
|
|
(" <<given opcode or page_code is being ignored>>\n");
|
||
|
|
|
||
|
|
if (0 == do_scsi_inq(sg_fd, 0, 0, 0, rsp_buff, 36, 1)) {
|
||
|
|
len = rsp_buff[4] + 5;
|
||
|
|
ansi_version = rsp_buff[2] & 0x7;
|
||
|
|
if ((len > 36) && (len < 256) && (!do_36)) {
|
||
|
|
if (do_scsi_inq
|
||
|
|
(sg_fd, 0, 0, 0, rsp_buff, len, 1)) {
|
||
|
|
fprintf(stderr,
|
||
|
|
"second INQUIRY (%d byte) failed\n",
|
||
|
|
len);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
if (len != (rsp_buff[4] + 5)) {
|
||
|
|
fprintf(stderr,
|
||
|
|
"strange, twin INQUIRYs yield different "
|
||
|
|
"'additional length'\n");
|
||
|
|
ret = 2;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (do_36) {
|
||
|
|
act_len = len;
|
||
|
|
len = 36;
|
||
|
|
} else
|
||
|
|
act_len = len;
|
||
|
|
if (do_hex)
|
||
|
|
dStrHex((const char *)rsp_buff, len, 0);
|
||
|
|
else {
|
||
|
|
printf
|
||
|
|
(" PQual=%d, Device type=%d, RMB=%d, ANSI version=%d, ",
|
||
|
|
(rsp_buff[0] & 0xe0) >> 5,
|
||
|
|
rsp_buff[0] & 0x1f,
|
||
|
|
! !(rsp_buff[1] & 0x80), ansi_version);
|
||
|
|
printf("[full version=0x%02x]\n",
|
||
|
|
(unsigned int)rsp_buff[2]);
|
||
|
|
printf
|
||
|
|
(" AERC=%d, TrmTsk=%d, NormACA=%d, HiSUP=%d, "
|
||
|
|
"Resp data format=%d, SCCS=%d\n",
|
||
|
|
! !(rsp_buff[3] & 0x80),
|
||
|
|
! !(rsp_buff[3] & 0x40),
|
||
|
|
! !(rsp_buff[3] & 0x20),
|
||
|
|
! !(rsp_buff[3] & 0x10),
|
||
|
|
rsp_buff[3] & 0x0f,
|
||
|
|
! !(rsp_buff[5] & 0x80));
|
||
|
|
printf
|
||
|
|
(" BQue=%d, EncServ=%d, MultiP=%d, MChngr=%d, "
|
||
|
|
"ACKREQQ=%d, ", ! !(rsp_buff[6] & 0x80),
|
||
|
|
! !(rsp_buff[6] & 0x40),
|
||
|
|
! !(rsp_buff[6] & 0x10),
|
||
|
|
! !(rsp_buff[6] & 0x08),
|
||
|
|
! !(rsp_buff[6] & 0x04));
|
||
|
|
printf("Addr16=%d\n RelAdr=%d, ",
|
||
|
|
! !(rsp_buff[6] & 0x01),
|
||
|
|
! !(rsp_buff[7] & 0x80));
|
||
|
|
printf
|
||
|
|
("WBus16=%d, Sync=%d, Linked=%d, TranDis=%d, ",
|
||
|
|
! !(rsp_buff[7] & 0x20),
|
||
|
|
! !(rsp_buff[7] & 0x10),
|
||
|
|
! !(rsp_buff[7] & 0x08),
|
||
|
|
! !(rsp_buff[7] & 0x04));
|
||
|
|
printf("CmdQue=%d\n", ! !(rsp_buff[7] & 0x02));
|
||
|
|
if (len > 56)
|
||
|
|
printf
|
||
|
|
(" Clocking=0x%x, QAS=%d, IUS=%d\n",
|
||
|
|
(rsp_buff[56] & 0x0c) >> 2,
|
||
|
|
! !(rsp_buff[56] & 0x2),
|
||
|
|
! !(rsp_buff[56] & 0x1));
|
||
|
|
if (act_len == len)
|
||
|
|
printf(" length=%d (0x%x)", len,
|
||
|
|
len);
|
||
|
|
else
|
||
|
|
printf
|
||
|
|
(" length=%d (0x%x), but only read 36 bytes",
|
||
|
|
len, len);
|
||
|
|
if ((ansi_version >= 2) && (len < 36))
|
||
|
|
printf
|
||
|
|
(" [for SCSI>=2, len>=36 is expected]\n");
|
||
|
|
else
|
||
|
|
printf("\n");
|
||
|
|
|
||
|
|
if (len <= 8)
|
||
|
|
printf
|
||
|
|
(" Inquiry response length=%d\n, no vendor, "
|
||
|
|
"product or revision data\n", len);
|
||
|
|
else {
|
||
|
|
if (len < 36)
|
||
|
|
rsp_buff[len] = '\0';
|
||
|
|
memcpy(buff, &rsp_buff[8], 8);
|
||
|
|
buff[8] = '\0';
|
||
|
|
printf(" Vendor identification: %s\n",
|
||
|
|
buff);
|
||
|
|
if (len <= 16)
|
||
|
|
printf
|
||
|
|
(" Product identification: <none>\n");
|
||
|
|
else {
|
||
|
|
memcpy(buff, &rsp_buff[16], 16);
|
||
|
|
buff[16] = '\0';
|
||
|
|
printf
|
||
|
|
(" Product identification: %s\n",
|
||
|
|
buff);
|
||
|
|
}
|
||
|
|
if (len <= 32)
|
||
|
|
printf
|
||
|
|
(" Product revision level: <none>\n");
|
||
|
|
else {
|
||
|
|
memcpy(buff, &rsp_buff[32], 4);
|
||
|
|
buff[4] = '\0';
|
||
|
|
printf
|
||
|
|
(" Product revision level: %s\n",
|
||
|
|
buff);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (!do_raw &&
|
||
|
|
(0 ==
|
||
|
|
do_scsi_inq(sg_fd, 0, 1, 0x80, rsp_buff,
|
||
|
|
MX_ALLOC_LEN, 0))) {
|
||
|
|
len = rsp_buff[3];
|
||
|
|
if (len > 0) {
|
||
|
|
memcpy(buff, rsp_buff + 4, len);
|
||
|
|
buff[len] = '\0';
|
||
|
|
printf(" Product serial number: %s\n",
|
||
|
|
buff);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
printf("36 byte INQUIRY failed\n");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
} else if (do_cmddt) {
|
||
|
|
int reserved_cmddt;
|
||
|
|
char op_name[128];
|
||
|
|
|
||
|
|
if (do_cmdlst) {
|
||
|
|
printf("Supported command list:\n");
|
||
|
|
for (k = 0; k < 256; ++k) {
|
||
|
|
if (0 ==
|
||
|
|
do_scsi_inq(sg_fd, 1, 0, k, rsp_buff,
|
||
|
|
MX_ALLOC_LEN, 1)) {
|
||
|
|
support_num = rsp_buff[1] & 7;
|
||
|
|
reserved_cmddt = rsp_buff[4];
|
||
|
|
if ((3 == support_num)
|
||
|
|
|| (5 == support_num)) {
|
||
|
|
num = rsp_buff[5];
|
||
|
|
for (j = 0; j < num; ++j)
|
||
|
|
printf(" %.2x",
|
||
|
|
(int)rsp_buff[6 +
|
||
|
|
j]);
|
||
|
|
if (5 == support_num)
|
||
|
|
printf
|
||
|
|
(" [vendor specific manner (5)]");
|
||
|
|
sg_get_command_name((unsigned
|
||
|
|
char)k,
|
||
|
|
sizeof
|
||
|
|
(op_name) -
|
||
|
|
1, op_name);
|
||
|
|
op_name[sizeof(op_name) - 1] =
|
||
|
|
'\0';
|
||
|
|
printf(" %s\n", op_name);
|
||
|
|
} else if ((4 == support_num)
|
||
|
|
|| (6 == support_num))
|
||
|
|
printf
|
||
|
|
(" opcode=0x%.2x vendor specific (%d)\n",
|
||
|
|
k, support_num);
|
||
|
|
else if ((0 == support_num)
|
||
|
|
&& (reserved_cmddt > 0)) {
|
||
|
|
printf
|
||
|
|
(" opcode=0x%.2x ignored cmddt bit, "
|
||
|
|
"given standard INQUIRY response, stop\n",
|
||
|
|
k);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
fprintf(stderr,
|
||
|
|
"CmdDt INQUIRY on opcode=0x%.2x: failed\n",
|
||
|
|
k);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
if (!do_raw) {
|
||
|
|
printf("CmdDt INQUIRY, opcode=0x%.2x: [",
|
||
|
|
num_opcode);
|
||
|
|
sg_get_command_name((unsigned char)num_opcode,
|
||
|
|
sizeof(op_name) - 1,
|
||
|
|
op_name);
|
||
|
|
op_name[sizeof(op_name) - 1] = '\0';
|
||
|
|
printf("%s]\n", op_name);
|
||
|
|
}
|
||
|
|
if (0 == do_scsi_inq(sg_fd, 1, 0, num_opcode, rsp_buff,
|
||
|
|
MX_ALLOC_LEN, 1)) {
|
||
|
|
len = rsp_buff[5] + 6;
|
||
|
|
reserved_cmddt = rsp_buff[4];
|
||
|
|
if (do_hex)
|
||
|
|
dStrHex((const char *)rsp_buff, len, 0);
|
||
|
|
else {
|
||
|
|
const char *desc_p;
|
||
|
|
int prnt_cmd = 0;
|
||
|
|
|
||
|
|
support_num = rsp_buff[1] & 7;
|
||
|
|
num = rsp_buff[5];
|
||
|
|
switch (support_num) {
|
||
|
|
case 0:
|
||
|
|
if (0 == reserved_cmddt)
|
||
|
|
desc_p =
|
||
|
|
"no data available";
|
||
|
|
else
|
||
|
|
desc_p =
|
||
|
|
"ignored cmddt bit, standard INQUIRY "
|
||
|
|
"response";
|
||
|
|
break;
|
||
|
|
case 1:
|
||
|
|
desc_p = "not supported";
|
||
|
|
break;
|
||
|
|
case 2:
|
||
|
|
desc_p = "reserved (2)";
|
||
|
|
break;
|
||
|
|
case 3:
|
||
|
|
desc_p =
|
||
|
|
"supported as per standard";
|
||
|
|
prnt_cmd = 1;
|
||
|
|
break;
|
||
|
|
case 4:
|
||
|
|
desc_p = "vendor specific (4)";
|
||
|
|
break;
|
||
|
|
case 5:
|
||
|
|
desc_p =
|
||
|
|
"supported in vendor specific way";
|
||
|
|
prnt_cmd = 1;
|
||
|
|
break;
|
||
|
|
case 6:
|
||
|
|
desc_p = "vendor specific (6)";
|
||
|
|
break;
|
||
|
|
case 7:
|
||
|
|
desc_p = "reserved (7)";
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
desc_p = "impossible value > 7";
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
if (prnt_cmd) {
|
||
|
|
printf(" Support field: %s [",
|
||
|
|
desc_p);
|
||
|
|
for (j = 0; j < num; ++j)
|
||
|
|
printf(" %.2x",
|
||
|
|
(int)rsp_buff[6 +
|
||
|
|
j]);
|
||
|
|
printf(" ]\n");
|
||
|
|
} else
|
||
|
|
printf(" Support field: %s\n",
|
||
|
|
desc_p);
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
fprintf(stderr,
|
||
|
|
"CmdDt INQUIRY on opcode=0x%.2x: failed\n",
|
||
|
|
num_opcode);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
} else if (do_evpd) {
|
||
|
|
if (!do_raw)
|
||
|
|
printf("EVPD INQUIRY, page code=0x%.2x:\n", num_opcode);
|
||
|
|
if (0 ==
|
||
|
|
do_scsi_inq(sg_fd, 0, 1, num_opcode, rsp_buff, MX_ALLOC_LEN,
|
||
|
|
1)) {
|
||
|
|
len = rsp_buff[3] + 4;
|
||
|
|
if (num_opcode != rsp_buff[1])
|
||
|
|
printf
|
||
|
|
("non evpd respone; probably a STANDARD INQUIRY "
|
||
|
|
"response\n");
|
||
|
|
else {
|
||
|
|
if (!do_hex)
|
||
|
|
printf(" Only hex output supported\n");
|
||
|
|
dStrHex((const char *)rsp_buff, len, 0);
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
fprintf(stderr,
|
||
|
|
"EVPD INQUIRY, page code=0x%.2x: failed\n",
|
||
|
|
num_opcode);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (do_pci) {
|
||
|
|
unsigned char slot_name[16];
|
||
|
|
|
||
|
|
printf("\n");
|
||
|
|
memset(slot_name, '\0', sizeof(slot_name));
|
||
|
|
if (ioctl(sg_fd, SCSI_IOCTL_GET_PCI, slot_name) < 0) {
|
||
|
|
if (EINVAL == errno)
|
||
|
|
printf
|
||
|
|
("ioctl(SCSI_IOCTL_GET_PCI) not supported by this "
|
||
|
|
"kernel\n");
|
||
|
|
else if (ENXIO == errno)
|
||
|
|
printf
|
||
|
|
("associated adapter not a PCI device?\n");
|
||
|
|
else
|
||
|
|
perror("ioctl(SCSI_IOCTL_GET_PCI) failed");
|
||
|
|
} else
|
||
|
|
printf("PCI:slot_name: %s\n", slot_name);
|
||
|
|
}
|
||
|
|
|
||
|
|
close(sg_fd);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
int show_scsi_maps()
|
||
|
|
{
|
||
|
|
int sg_fd, res, k;
|
||
|
|
int do_numeric = NUMERIC_SCAN_DEF;
|
||
|
|
int do_all_s = 1;
|
||
|
|
int do_sd = 0;
|
||
|
|
int do_st = 0;
|
||
|
|
int do_osst = 0;
|
||
|
|
int do_sr = 0;
|
||
|
|
int do_scd = 0;
|
||
|
|
int do_extra = 1;
|
||
|
|
int do_inquiry = 0;
|
||
|
|
char fname[64];
|
||
|
|
int num_errors = 0;
|
||
|
|
int num_silent = 0;
|
||
|
|
int eacces_err = 0;
|
||
|
|
int last_sg_ind = -1;
|
||
|
|
struct stat stat_buf;
|
||
|
|
|
||
|
|
print_msg(TEST_BREAK, __FUNCTION__);
|
||
|
|
|
||
|
|
if (stat(devfs_id, &stat_buf) == 0)
|
||
|
|
printf("# Note: the devfs pseudo file system is present\n");
|
||
|
|
|
||
|
|
for (k = 0, res = 0; (k < MAX_SG_DEVS) && (num_errors < MAX_ERRORS);
|
||
|
|
++k, res = (sg_fd >= 0) ? close(sg_fd) : 0) {
|
||
|
|
if (res < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ, "Error closing %s ", fname);
|
||
|
|
perror("sg_map: close error");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
make_dev_name(fname, "/dev/sg", k, do_numeric);
|
||
|
|
|
||
|
|
sg_fd = open(fname, O_RDONLY | O_NONBLOCK);
|
||
|
|
if (sg_fd < 0) {
|
||
|
|
if (EBUSY == errno) {
|
||
|
|
map_arr[k].active = -2;
|
||
|
|
continue;
|
||
|
|
} else if ((ENODEV == errno) || (ENOENT == errno) ||
|
||
|
|
(ENXIO == errno)) {
|
||
|
|
++num_errors;
|
||
|
|
++num_silent;
|
||
|
|
map_arr[k].active = -1;
|
||
|
|
continue;
|
||
|
|
} else {
|
||
|
|
if (EACCES == errno)
|
||
|
|
eacces_err = 1;
|
||
|
|
snprintf(ebuff, EBUFF_SZ, "Error opening %s ",
|
||
|
|
fname);
|
||
|
|
perror(ebuff);
|
||
|
|
++num_errors;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
res = ioctl(sg_fd, SG_GET_SCSI_ID, &map_arr[k].sg_dat);
|
||
|
|
if (res < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ,
|
||
|
|
"device %s failed on sg ioctl, skip", fname);
|
||
|
|
perror(ebuff);
|
||
|
|
++num_errors;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
if (do_inquiry) {
|
||
|
|
char buff[36];
|
||
|
|
|
||
|
|
if (0 ==
|
||
|
|
do_scsi_inq(sg_fd, 0, 0, 0, buff, sizeof(buff),
|
||
|
|
1)) {
|
||
|
|
memcpy(map_arr[k].vendor, &buff[8], 8);
|
||
|
|
memcpy(map_arr[k].product, &buff[16], 16);
|
||
|
|
memcpy(map_arr[k].revision, &buff[32], 4);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
map_arr[k].active = 1;
|
||
|
|
map_arr[k].oth_dev_num = -1;
|
||
|
|
last_sg_ind = k;
|
||
|
|
}
|
||
|
|
if ((num_errors >= MAX_ERRORS) && (num_silent < num_errors)) {
|
||
|
|
printf("Stopping because there are too many error\n");
|
||
|
|
if (eacces_err)
|
||
|
|
printf(" root access may be required\n");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
if (last_sg_ind < 0) {
|
||
|
|
printf("Stopping because no sg devices found\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
if (do_all_s || do_sd)
|
||
|
|
scan_dev_type("/dev/sd", MAX_SD_DEVS, 0, LIN_DEV_TYPE_SD,
|
||
|
|
last_sg_ind);
|
||
|
|
if (do_all_s || do_sr)
|
||
|
|
scan_dev_type("/dev/sr", MAX_SR_DEVS, 1, LIN_DEV_TYPE_SR,
|
||
|
|
last_sg_ind);
|
||
|
|
if (do_all_s || do_scd)
|
||
|
|
scan_dev_type("/dev/scd", MAX_SR_DEVS, 1, LIN_DEV_TYPE_SCD,
|
||
|
|
last_sg_ind);
|
||
|
|
if (do_all_s || do_st)
|
||
|
|
scan_dev_type("/dev/st", MAX_ST_DEVS, 1, LIN_DEV_TYPE_ST,
|
||
|
|
last_sg_ind);
|
||
|
|
if (do_all_s || do_osst)
|
||
|
|
scan_dev_type("/dev/osst", MAX_OSST_DEVS, 1, LIN_DEV_TYPE_OSST,
|
||
|
|
last_sg_ind);
|
||
|
|
|
||
|
|
for (k = 0; k <= last_sg_ind; ++k) {
|
||
|
|
make_dev_name(fname, "/dev/sg", k, do_numeric);
|
||
|
|
printf("%s", fname);
|
||
|
|
switch (map_arr[k].active) {
|
||
|
|
case -2:
|
||
|
|
printf(do_extra ? " -2 -2 -2 -2 -2" : " busy");
|
||
|
|
break;
|
||
|
|
case -1:
|
||
|
|
printf(do_extra ? " -1 -1 -1 -1 -1" :
|
||
|
|
" not present");
|
||
|
|
break;
|
||
|
|
case 0:
|
||
|
|
printf(do_extra ? " -3 -3 -3 -3 -3" :
|
||
|
|
" some error\n");
|
||
|
|
break;
|
||
|
|
case 1:
|
||
|
|
if (do_extra)
|
||
|
|
printf(" %d %d %d %d %d",
|
||
|
|
map_arr[k].sg_dat.host_no,
|
||
|
|
map_arr[k].sg_dat.channel,
|
||
|
|
map_arr[k].sg_dat.scsi_id,
|
||
|
|
map_arr[k].sg_dat.lun,
|
||
|
|
map_arr[k].sg_dat.scsi_type);
|
||
|
|
switch (map_arr[k].lin_dev_type) {
|
||
|
|
case LIN_DEV_TYPE_SD:
|
||
|
|
make_dev_name(fname, "/dev/sd",
|
||
|
|
map_arr[k].oth_dev_num, 0);
|
||
|
|
printf(" %s", fname);
|
||
|
|
break;
|
||
|
|
case LIN_DEV_TYPE_ST:
|
||
|
|
make_dev_name(fname, "/dev/st",
|
||
|
|
map_arr[k].oth_dev_num, 1);
|
||
|
|
printf(" %s", fname);
|
||
|
|
break;
|
||
|
|
case LIN_DEV_TYPE_OSST:
|
||
|
|
make_dev_name(fname, "/dev/osst",
|
||
|
|
map_arr[k].oth_dev_num, 1);
|
||
|
|
printf(" %s", fname);
|
||
|
|
break;
|
||
|
|
case LIN_DEV_TYPE_SR:
|
||
|
|
make_dev_name(fname, "/dev/sr",
|
||
|
|
map_arr[k].oth_dev_num, 1);
|
||
|
|
printf(" %s", fname);
|
||
|
|
break;
|
||
|
|
case LIN_DEV_TYPE_SCD:
|
||
|
|
make_dev_name(fname, "/dev/scd",
|
||
|
|
map_arr[k].oth_dev_num, 1);
|
||
|
|
printf(" %s", fname);
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
if (do_inquiry)
|
||
|
|
printf(" %.8s %.16s %.4s", map_arr[k].vendor,
|
||
|
|
map_arr[k].product, map_arr[k].revision);
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
printf(" bad logic\n");
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
printf("\n");
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int find_dev_in_sg_arr(My_scsi_idlun * my_idlun, int host_no,
|
||
|
|
int last_sg_ind)
|
||
|
|
{
|
||
|
|
int k;
|
||
|
|
struct sg_scsi_id *sidp;
|
||
|
|
|
||
|
|
for (k = 0; k <= last_sg_ind; ++k) {
|
||
|
|
sidp = &(map_arr[k].sg_dat);
|
||
|
|
if ((host_no == sidp->host_no) &&
|
||
|
|
((my_idlun->dev_id & 0xff) == sidp->scsi_id) &&
|
||
|
|
(((my_idlun->dev_id >> 8) & 0xff) == sidp->lun) &&
|
||
|
|
(((my_idlun->dev_id >> 16) & 0xff) == sidp->channel))
|
||
|
|
return k;
|
||
|
|
}
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void scan_dev_type(const char *leadin, int max_dev, int do_numeric,
|
||
|
|
int lin_dev_type, int last_sg_ind)
|
||
|
|
{
|
||
|
|
int k, res, ind, sg_fd = 0;
|
||
|
|
int num_errors = 0;
|
||
|
|
int num_silent = 0;
|
||
|
|
int host_no = -1;
|
||
|
|
int nonMappedDevicesPresent = FALSE;
|
||
|
|
My_scsi_idlun my_idlun;
|
||
|
|
char fname[64];
|
||
|
|
|
||
|
|
for (k = 0, res = 0; (k < max_dev) && (num_errors < MAX_ERRORS);
|
||
|
|
++k, res = (sg_fd >= 0) ? close(sg_fd) : 0) {
|
||
|
|
|
||
|
|
/* ignore close() errors */
|
||
|
|
#if 0
|
||
|
|
if (res < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ, "Error closing %s ", fname);
|
||
|
|
perror("sg_map: close error");
|
||
|
|
#ifndef IGN_CLOSE_ERR
|
||
|
|
return;
|
||
|
|
#else
|
||
|
|
++num_errors;
|
||
|
|
sg_fd = 0;
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
make_dev_name(fname, leadin, k, do_numeric);
|
||
|
|
#ifdef DEBUG
|
||
|
|
printf("Trying %s: ", fname);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
sg_fd = open(fname, O_RDONLY | O_NONBLOCK);
|
||
|
|
if (sg_fd < 0) {
|
||
|
|
#ifdef DEBUG
|
||
|
|
printf("ERROR %i\n", errno);
|
||
|
|
#endif
|
||
|
|
if (EBUSY == errno) {
|
||
|
|
printf("Device %s is busy\n", fname);
|
||
|
|
++num_errors;
|
||
|
|
continue;
|
||
|
|
} else if ((ENODEV == errno) || (ENOENT == errno) ||
|
||
|
|
(ENXIO == errno)) {
|
||
|
|
++num_errors;
|
||
|
|
++num_silent;
|
||
|
|
continue;
|
||
|
|
} else {
|
||
|
|
snprintf(ebuff, EBUFF_SZ, "Error opening %s ",
|
||
|
|
fname);
|
||
|
|
perror(ebuff);
|
||
|
|
++num_errors;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
res = ioctl(sg_fd, SCSI_IOCTL_GET_IDLUN, &my_idlun);
|
||
|
|
if (res < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ,
|
||
|
|
"device %s failed on scsi ioctl(idlun), skip",
|
||
|
|
fname);
|
||
|
|
perror(ebuff);
|
||
|
|
++num_errors;
|
||
|
|
#ifdef DEBUG
|
||
|
|
printf("Couldn't get IDLUN!\n");
|
||
|
|
#endif
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
res = ioctl(sg_fd, SCSI_IOCTL_GET_BUS_NUMBER, &host_no);
|
||
|
|
if (res < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ,
|
||
|
|
"device %s failed on scsi ioctl(bus_number), skip",
|
||
|
|
fname);
|
||
|
|
perror(ebuff);
|
||
|
|
++num_errors;
|
||
|
|
#ifdef DEBUG
|
||
|
|
printf("Couldn't get BUS!\n");
|
||
|
|
#endif
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
#ifdef DEBUG
|
||
|
|
printf("%i(%x) %i %i %i %i\n", host_no, my_idlun.host_unique_id,
|
||
|
|
(my_idlun.dev_id >> 24) & 0xff,
|
||
|
|
(my_idlun.dev_id >> 16) & 0xff,
|
||
|
|
(my_idlun.dev_id >> 8) & 0xff, my_idlun.dev_id & 0xff);
|
||
|
|
#endif
|
||
|
|
ind = find_dev_in_sg_arr(&my_idlun, host_no, last_sg_ind);
|
||
|
|
if (ind >= 0) {
|
||
|
|
map_arr[ind].oth_dev_num = k;
|
||
|
|
map_arr[ind].lin_dev_type = lin_dev_type;
|
||
|
|
} else if (ind != -1) {
|
||
|
|
printf
|
||
|
|
("Strange, could not find device %s mapped to sg device error %d??\n",
|
||
|
|
fname, ind);
|
||
|
|
} else {
|
||
|
|
nonMappedDevicesPresent = TRUE;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (nonMappedDevicesPresent) {
|
||
|
|
printf("Unmapped Devices found...\n\n");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Returns 0 when successful, else -1 */
|
||
|
|
static int do_simple_inq(int sg_fd, void *resp, int mx_resp_len, int noisy)
|
||
|
|
{
|
||
|
|
int res;
|
||
|
|
unsigned char inqCmdBlk[INQUIRY_CMDLEN] =
|
||
|
|
{ INQUIRY_CMD, 0, 0, 0, 0, 0 };
|
||
|
|
unsigned char sense_b[SENSE_BUFF_LEN];
|
||
|
|
sg_io_hdr_t io_hdr;
|
||
|
|
|
||
|
|
inqCmdBlk[4] = (unsigned char)mx_resp_len;
|
||
|
|
memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
|
||
|
|
io_hdr.interface_id = 'S';
|
||
|
|
io_hdr.cmd_len = sizeof(inqCmdBlk);
|
||
|
|
io_hdr.mx_sb_len = sizeof(sense_b);
|
||
|
|
io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
|
||
|
|
io_hdr.dxfer_len = mx_resp_len;
|
||
|
|
io_hdr.dxferp = resp;
|
||
|
|
io_hdr.cmdp = inqCmdBlk;
|
||
|
|
io_hdr.sbp = sense_b;
|
||
|
|
io_hdr.timeout = DEF_TIMEOUT;
|
||
|
|
|
||
|
|
if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
|
||
|
|
perror("SG_IO (inquiry) error");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
res = sg_err_category3(&io_hdr);
|
||
|
|
switch (res) {
|
||
|
|
case SG_ERR_CAT_CLEAN:
|
||
|
|
case SG_ERR_CAT_RECOVERED:
|
||
|
|
return 0;
|
||
|
|
default:
|
||
|
|
if (noisy) {
|
||
|
|
char ebuff[EBUFF_SZ];
|
||
|
|
snprintf(ebuff, EBUFF_SZ, "Inquiry error ");
|
||
|
|
sg_chk_n_print3(ebuff, &io_hdr);
|
||
|
|
}
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static int do_modes(int sg_fd, int dbd, int pc, int pg_code, int sub_pg_code,
|
||
|
|
void *resp, int mx_resp_len, int noisy, int mode6)
|
||
|
|
{
|
||
|
|
int res;
|
||
|
|
unsigned char modesCmdBlk[MODE_SENSE10_CMDLEN] =
|
||
|
|
{ MODE_SENSE10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||
|
|
unsigned char sense_b[SENSE_BUFF_LEN];
|
||
|
|
sg_io_hdr_t io_hdr;
|
||
|
|
|
||
|
|
modesCmdBlk[1] = (unsigned char)(dbd ? 0x8 : 0);
|
||
|
|
modesCmdBlk[2] = (unsigned char)(((pc << 6) & 0xc0) | (pg_code & 0x3f));
|
||
|
|
modesCmdBlk[3] = (unsigned char)(sub_pg_code & 0xff);
|
||
|
|
if (mx_resp_len > (mode6 ? 0xff : 0xffff)) {
|
||
|
|
printf(ME "mx_resp_len too big\n");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
if (mode6) {
|
||
|
|
modesCmdBlk[0] = MODE_SENSE6_CMD;
|
||
|
|
modesCmdBlk[4] = (unsigned char)(mx_resp_len & 0xff);
|
||
|
|
} else {
|
||
|
|
modesCmdBlk[7] = (unsigned char)((mx_resp_len >> 8) & 0xff);
|
||
|
|
modesCmdBlk[8] = (unsigned char)(mx_resp_len & 0xff);
|
||
|
|
}
|
||
|
|
|
||
|
|
memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
|
||
|
|
memset(sense_b, 0, sizeof(sense_b));
|
||
|
|
io_hdr.interface_id = 'S';
|
||
|
|
io_hdr.cmd_len = mode6 ? MODE_SENSE6_CMDLEN : MODE_SENSE10_CMDLEN;
|
||
|
|
io_hdr.mx_sb_len = sizeof(sense_b);
|
||
|
|
io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
|
||
|
|
io_hdr.dxfer_len = mx_resp_len;
|
||
|
|
io_hdr.dxferp = resp;
|
||
|
|
io_hdr.cmdp = modesCmdBlk;
|
||
|
|
io_hdr.sbp = sense_b;
|
||
|
|
io_hdr.timeout = DEF_TIMEOUT;
|
||
|
|
|
||
|
|
if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
|
||
|
|
perror("SG_IO (mode sense) error");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
res = sg_err_category3(&io_hdr);
|
||
|
|
switch (res) {
|
||
|
|
case SG_ERR_CAT_CLEAN:
|
||
|
|
case SG_ERR_CAT_RECOVERED:
|
||
|
|
return 0;
|
||
|
|
default:
|
||
|
|
if (noisy) {
|
||
|
|
char ebuff[EBUFF_SZ];
|
||
|
|
snprintf(ebuff, EBUFF_SZ, "Mode sense error, dbd=%d "
|
||
|
|
"pc=%d page_code=%x sub_page_code=%x\n ",
|
||
|
|
dbd, pc, pg_code, sub_pg_code);
|
||
|
|
sg_chk_n_print3(ebuff, &io_hdr);
|
||
|
|
}
|
||
|
|
if ((0x70 == (0x7f & sense_b[0])) && (0x20 == sense_b[12]) &&
|
||
|
|
(0x0 == sense_b[13])) {
|
||
|
|
if (mode6)
|
||
|
|
fprintf(stderr,
|
||
|
|
">>>>>> drop '-6' switch and try again with "
|
||
|
|
"a 10 byte MODE SENSE\n");
|
||
|
|
else
|
||
|
|
fprintf(stderr,
|
||
|
|
">>>>>> add '-6' switch and try again with "
|
||
|
|
"a 6 byte MODE SENSE\n");
|
||
|
|
}
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
const char *scsi_ptype_strs[] = {
|
||
|
|
"disk",
|
||
|
|
"tape",
|
||
|
|
"printer",
|
||
|
|
"processor",
|
||
|
|
"write once optical disk",
|
||
|
|
"cd/dvd",
|
||
|
|
"scanner",
|
||
|
|
"optical memory device",
|
||
|
|
"medium changer",
|
||
|
|
"communications",
|
||
|
|
"graphics",
|
||
|
|
"graphics",
|
||
|
|
"storage array controller",
|
||
|
|
"enclosure services device",
|
||
|
|
"simplified direct access device",
|
||
|
|
"optical card reader/writer device",
|
||
|
|
};
|
||
|
|
|
||
|
|
const char *get_ptype_str(int scsi_ptype)
|
||
|
|
{
|
||
|
|
int num = sizeof(scsi_ptype_strs) / sizeof(scsi_ptype_strs[0]);
|
||
|
|
|
||
|
|
return (scsi_ptype < num) ? scsi_ptype_strs[scsi_ptype] : "";
|
||
|
|
}
|
||
|
|
|
||
|
|
static struct page_code_desc pc_desc_all[] = {
|
||
|
|
{0x0, "Unit Attention condition [vendor: page format not required]"},
|
||
|
|
{0x2, "Disconnect-Reconnect"},
|
||
|
|
{0xa, "Control"},
|
||
|
|
{0x15, "Extended"},
|
||
|
|
{0x16, "Extended device-type specific"},
|
||
|
|
{0x18, "Protocol specific LUN"},
|
||
|
|
{0x19, "Protocol specific port"},
|
||
|
|
{0x1a, "Power condition"},
|
||
|
|
{0x1c, "Informational exceptions control"},
|
||
|
|
{0x3f, "[yields all supported pages]"},
|
||
|
|
};
|
||
|
|
|
||
|
|
static struct page_code_desc pc_desc_disk[] = {
|
||
|
|
{0x1, "Read-Write error recovery"},
|
||
|
|
{0x3, "Format"},
|
||
|
|
{0x4, "Rigid disk geometry"},
|
||
|
|
{0x5, "Flexible geometry"},
|
||
|
|
{0x7, "Verify error recovery"},
|
||
|
|
{0x8, "Caching"},
|
||
|
|
{0x9, "Peripheral device (spc-2 ?)"},
|
||
|
|
{0xb, "Medium types supported"},
|
||
|
|
{0xc, "Notch and partition"},
|
||
|
|
{0xd, "Power condition (obsolete)"},
|
||
|
|
{0x10, "XOR control"},
|
||
|
|
};
|
||
|
|
|
||
|
|
static struct page_code_desc pc_desc_tape[] = {
|
||
|
|
{0xf, "Data Compression"},
|
||
|
|
{0x10, "Device config"},
|
||
|
|
{0x11, "Medium Partition [1]"},
|
||
|
|
{0x12, "Medium Partition [2]"},
|
||
|
|
{0x13, "Medium Partition [3]"},
|
||
|
|
{0x14, "Medium Partition [4]"},
|
||
|
|
{0x1c, "Informational exceptions control (tape version)"},
|
||
|
|
};
|
||
|
|
|
||
|
|
static struct page_code_desc pc_desc_cddvd[] = {
|
||
|
|
{0x1, "Read-Write error recovery"},
|
||
|
|
{0x3, "MRW"},
|
||
|
|
{0x5, "Write parameters"},
|
||
|
|
{0xd, "CD device parameters (obsolete)"},
|
||
|
|
{0xe, "CD audio"},
|
||
|
|
{0x1a, "Power condition"},
|
||
|
|
{0x1c, "Fault/failure reporting control"},
|
||
|
|
{0x1d, "Timeout and protect"},
|
||
|
|
{0x2a, "MM capabilities and mechanical status (obsolete)"},
|
||
|
|
};
|
||
|
|
|
||
|
|
static struct page_code_desc pc_desc_smc[] = {
|
||
|
|
{0x1d, "Element address assignment"},
|
||
|
|
{0x1e, "Transport geometry parameters"},
|
||
|
|
{0x1f, "Device capabilities"},
|
||
|
|
};
|
||
|
|
|
||
|
|
static struct page_code_desc pc_desc_scc[] = {
|
||
|
|
{0x1b, "LUN mapping"},
|
||
|
|
};
|
||
|
|
|
||
|
|
static struct page_code_desc pc_desc_ses[] = {
|
||
|
|
{0x14, "Enclosure services management"},
|
||
|
|
};
|
||
|
|
|
||
|
|
struct page_code_desc *find_mode_page_table(int scsi_ptype, int *size)
|
||
|
|
{
|
||
|
|
switch (scsi_ptype) {
|
||
|
|
case 0: /* disk (direct access) type devices */
|
||
|
|
case 4:
|
||
|
|
case 7:
|
||
|
|
case 0xe:
|
||
|
|
*size = sizeof(pc_desc_disk) / sizeof(pc_desc_disk[0]);
|
||
|
|
return &pc_desc_disk[0];
|
||
|
|
case 1: /* tape devices */
|
||
|
|
case 2:
|
||
|
|
*size = sizeof(pc_desc_tape) / sizeof(pc_desc_tape[0]);
|
||
|
|
return &pc_desc_tape[0];
|
||
|
|
case 5: /* cd/dvd devices */
|
||
|
|
*size = sizeof(pc_desc_cddvd) / sizeof(pc_desc_cddvd[0]);
|
||
|
|
return &pc_desc_cddvd[0];
|
||
|
|
case 8: /* medium changer devices */
|
||
|
|
*size = sizeof(pc_desc_smc) / sizeof(pc_desc_smc[0]);
|
||
|
|
return &pc_desc_smc[0];
|
||
|
|
case 0xc: /* storage array devices */
|
||
|
|
*size = sizeof(pc_desc_scc) / sizeof(pc_desc_scc[0]);
|
||
|
|
return &pc_desc_scc[0];
|
||
|
|
case 0xd: /* enclosure services devices */
|
||
|
|
*size = sizeof(pc_desc_ses) / sizeof(pc_desc_ses[0]);
|
||
|
|
return &pc_desc_ses[0];
|
||
|
|
}
|
||
|
|
*size = 0;
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
const char *find_page_code_desc(int page_num, int scsi_ptype)
|
||
|
|
{
|
||
|
|
int k;
|
||
|
|
int num;
|
||
|
|
const struct page_code_desc *pcdp;
|
||
|
|
|
||
|
|
pcdp = find_mode_page_table(scsi_ptype, &num);
|
||
|
|
if (pcdp) {
|
||
|
|
for (k = 0; k < num; ++k, ++pcdp) {
|
||
|
|
if (page_num == pcdp->page_code)
|
||
|
|
return pcdp->desc;
|
||
|
|
else if (page_num < pcdp->page_code)
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
pcdp = &pc_desc_all[0];
|
||
|
|
num = sizeof(pc_desc_all) / sizeof(pc_desc_all[0]);
|
||
|
|
for (k = 0; k < num; ++k, ++pcdp) {
|
||
|
|
if (page_num == pcdp->page_code)
|
||
|
|
return pcdp->desc;
|
||
|
|
else if (page_num < pcdp->page_code)
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void list_page_codes(int scsi_ptype)
|
||
|
|
{
|
||
|
|
int k;
|
||
|
|
int num = sizeof(pc_desc_all) / sizeof(pc_desc_all[0]);
|
||
|
|
const struct page_code_desc *pcdp = &pc_desc_all[0];
|
||
|
|
int num_ptype;
|
||
|
|
const struct page_code_desc *pcd_ptypep;
|
||
|
|
|
||
|
|
pcd_ptypep = find_mode_page_table(scsi_ptype, &num_ptype);
|
||
|
|
printf("Page_Code Description\n");
|
||
|
|
for (k = 0; k < 0x3f; ++k) {
|
||
|
|
if (pcd_ptypep && (num_ptype > 0)) {
|
||
|
|
if (k == pcd_ptypep->page_code) {
|
||
|
|
printf(" 0x%02x %s\n",
|
||
|
|
pcd_ptypep->page_code, pcd_ptypep->desc);
|
||
|
|
++pcd_ptypep;
|
||
|
|
--num_ptype;
|
||
|
|
continue;
|
||
|
|
} else if (k > pcd_ptypep->page_code) {
|
||
|
|
pcd_ptypep++;
|
||
|
|
--num_ptype;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (pcdp && (num > 0)) {
|
||
|
|
if (k == pcdp->page_code) {
|
||
|
|
printf(" 0x%02x %s\n", pcdp->page_code,
|
||
|
|
pcdp->desc);
|
||
|
|
++pcdp;
|
||
|
|
--num;
|
||
|
|
continue;
|
||
|
|
} else if (k > pcdp->page_code) {
|
||
|
|
pcdp++;
|
||
|
|
--num;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
int show_scsi_modes(char *device)
|
||
|
|
{
|
||
|
|
int sg_fd, k, num, len, md_len, bd_len, longlba, page_num;
|
||
|
|
char *file_name = 0;
|
||
|
|
char ebuff[EBUFF_SZ];
|
||
|
|
const char *descp;
|
||
|
|
unsigned char rsp_buff[MODE_ALLOC_LEN];
|
||
|
|
int rsp_buff_size = MODE_ALLOC_LEN;
|
||
|
|
int pg_code = 0;
|
||
|
|
int sub_pg_code = 0;
|
||
|
|
int pc = 0;
|
||
|
|
int do_all = 1;
|
||
|
|
int do_dbd = 0;
|
||
|
|
int do_hex = 0;
|
||
|
|
int do_mode6 = 0; /* Use MODE SENSE(6) instead of MODE SENSE(10) */
|
||
|
|
int oflags = O_RDONLY | O_NONBLOCK;
|
||
|
|
struct sg_scsi_id a_sid;
|
||
|
|
int scsi_ptype, density_code_off;
|
||
|
|
unsigned char *ucp;
|
||
|
|
unsigned char uc;
|
||
|
|
|
||
|
|
print_msg(TEST_BREAK, __FUNCTION__);
|
||
|
|
|
||
|
|
file_name = device;
|
||
|
|
|
||
|
|
list_page_codes(0);
|
||
|
|
|
||
|
|
/* The 6 bytes command only allows up to 255 bytes of response data */
|
||
|
|
if (do_mode6)
|
||
|
|
rsp_buff_size = 255;
|
||
|
|
|
||
|
|
if ((sg_fd = open(file_name, oflags)) < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ, ME "error opening file: %s",
|
||
|
|
file_name);
|
||
|
|
perror(ebuff);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
/* Just to be safe, check we have a new sg device by trying an ioctl */
|
||
|
|
if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) {
|
||
|
|
printf(ME "%s doesn't seem to be a version 3 sg device\n",
|
||
|
|
file_name);
|
||
|
|
close(sg_fd);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
if (ioctl(sg_fd, SG_GET_SCSI_ID, &a_sid) < 0) {
|
||
|
|
unsigned char inqBuff[36];
|
||
|
|
|
||
|
|
if (do_simple_inq(sg_fd, inqBuff, sizeof(inqBuff), 1)) {
|
||
|
|
printf(ME "%s doesn't respond to a SCSI INQUIRY\n",
|
||
|
|
file_name);
|
||
|
|
close(sg_fd);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
scsi_ptype = inqBuff[0] & 0x1f; /* fetch peripheral device type */
|
||
|
|
} else
|
||
|
|
scsi_ptype = a_sid.scsi_type;
|
||
|
|
printf(" SCSI peripheral type: %s [0x%x] (from INQUIRY)\n",
|
||
|
|
get_ptype_str(scsi_ptype), scsi_ptype);
|
||
|
|
|
||
|
|
if (do_all)
|
||
|
|
pg_code = MODE_CODE_ALL;
|
||
|
|
|
||
|
|
if (0 == do_modes(sg_fd, do_dbd, pc, pg_code, sub_pg_code,
|
||
|
|
rsp_buff, rsp_buff_size, 1, do_mode6)) {
|
||
|
|
int medium_type, specific, headerlen;
|
||
|
|
|
||
|
|
printf("Mode parameter header from %s byte MODE SENSE:\n",
|
||
|
|
(do_mode6 ? "6" : "10"));
|
||
|
|
if (do_mode6) {
|
||
|
|
headerlen = 4;
|
||
|
|
if (do_hex)
|
||
|
|
dStrHex((const char *)rsp_buff, headerlen, 1);
|
||
|
|
md_len = rsp_buff[0] + 1;
|
||
|
|
bd_len = rsp_buff[3];
|
||
|
|
medium_type = rsp_buff[1];
|
||
|
|
specific = rsp_buff[2];
|
||
|
|
longlba = 0; /* what is this field? */
|
||
|
|
} else {
|
||
|
|
headerlen = 8;
|
||
|
|
md_len = (rsp_buff[0] << 8) + rsp_buff[1] + 2;
|
||
|
|
bd_len = (rsp_buff[6] << 8) + rsp_buff[7];
|
||
|
|
medium_type = rsp_buff[2];
|
||
|
|
specific = rsp_buff[3];
|
||
|
|
longlba = rsp_buff[4] & 1;
|
||
|
|
}
|
||
|
|
if (do_hex)
|
||
|
|
dStrHex((const char *)rsp_buff, headerlen, 1);
|
||
|
|
printf(" Mode data length=%d, medium type=0x%.2x, specific"
|
||
|
|
" param=0x%.2x, longlba=%d\n", md_len, medium_type,
|
||
|
|
specific, longlba);
|
||
|
|
if (md_len > rsp_buff_size) {
|
||
|
|
printf
|
||
|
|
("Only fetched %d bytes of response, truncate output\n",
|
||
|
|
rsp_buff_size);
|
||
|
|
md_len = rsp_buff_size;
|
||
|
|
if (bd_len + headerlen > rsp_buff_size)
|
||
|
|
bd_len = rsp_buff_size - headerlen;
|
||
|
|
}
|
||
|
|
printf(" Block descriptor length=%d\n", bd_len);
|
||
|
|
if (bd_len > 0) {
|
||
|
|
len = 8;
|
||
|
|
density_code_off = 0;
|
||
|
|
num = bd_len;
|
||
|
|
if (longlba) {
|
||
|
|
printf("> longlba block descriptors:\n");
|
||
|
|
len = 16;
|
||
|
|
density_code_off = 8;
|
||
|
|
} else if (0 == scsi_ptype) {
|
||
|
|
printf
|
||
|
|
("> Direct access device block descriptors:\n");
|
||
|
|
density_code_off = 4;
|
||
|
|
} else
|
||
|
|
printf
|
||
|
|
("> General mode parameter block descriptors:\n");
|
||
|
|
|
||
|
|
ucp = rsp_buff + headerlen;
|
||
|
|
while (num > 0) {
|
||
|
|
printf(" Density code=0x%x\n",
|
||
|
|
*(ucp + density_code_off));
|
||
|
|
dStrHex((const char *)ucp, len, 1);
|
||
|
|
ucp += len;
|
||
|
|
num -= len;
|
||
|
|
}
|
||
|
|
printf("\n");
|
||
|
|
}
|
||
|
|
ucp = rsp_buff + bd_len + headerlen; /* start of mode page(s) */
|
||
|
|
md_len -= bd_len + headerlen; /* length of mode page(s) */
|
||
|
|
while (md_len > 0) { /* got mode page(s) */
|
||
|
|
uc = *ucp;
|
||
|
|
page_num = ucp[0] & 0x3f;
|
||
|
|
if (do_hex)
|
||
|
|
descp = NULL;
|
||
|
|
else {
|
||
|
|
descp =
|
||
|
|
find_page_code_desc(page_num, scsi_ptype);
|
||
|
|
if (NULL == descp)
|
||
|
|
snprintf(ebuff, EBUFF_SZ,
|
||
|
|
"vendor[0x%x]", page_num);
|
||
|
|
}
|
||
|
|
if (uc & 0x40) {
|
||
|
|
len = (ucp[2] << 8) + ucp[3] + 4;
|
||
|
|
if (do_hex)
|
||
|
|
printf
|
||
|
|
(">> page_code=0x%x, subpage_code=0x%x, "
|
||
|
|
"page_control=%d\n", page_num,
|
||
|
|
ucp[1], pc);
|
||
|
|
else
|
||
|
|
printf
|
||
|
|
(">> page_code: %s, subpage_code=0x%x, "
|
||
|
|
"page_control: %s\n",
|
||
|
|
(descp ? descp : ebuff), ucp[1],
|
||
|
|
pg_control_str_arr[pc]);
|
||
|
|
} else {
|
||
|
|
len = ucp[1] + 2;
|
||
|
|
if (do_hex)
|
||
|
|
printf
|
||
|
|
(">> page_code=0x%x, page_control=%d\n",
|
||
|
|
page_num, pc);
|
||
|
|
else
|
||
|
|
printf
|
||
|
|
(">> page_code: %s, page_control: %s\n",
|
||
|
|
(descp ? descp : ebuff),
|
||
|
|
pg_control_str_arr[pc]);
|
||
|
|
}
|
||
|
|
dStrHex((const char *)ucp, len, 1);
|
||
|
|
ucp += len;
|
||
|
|
md_len -= len;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
close(sg_fd);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
int do_scsi_read_buffer(char *device)
|
||
|
|
{
|
||
|
|
int sg_fd, res;
|
||
|
|
unsigned int k, num;
|
||
|
|
unsigned char rbCmdBlk[RB_CMD_LEN];
|
||
|
|
unsigned char *rbBuff = NULL;
|
||
|
|
void *rawp = NULL;
|
||
|
|
unsigned char sense_buffer[32];
|
||
|
|
int buf_capacity = 0;
|
||
|
|
int do_quick = 0;
|
||
|
|
int do_dio = 0;
|
||
|
|
int do_mmap = 1;
|
||
|
|
int do_time = 0;
|
||
|
|
int buf_size = 0;
|
||
|
|
unsigned int total_size_mb = RB_MB_TO_READ;
|
||
|
|
char *file_name = 0;
|
||
|
|
size_t psz = getpagesize();
|
||
|
|
int dio_incomplete = 0;
|
||
|
|
sg_io_hdr_t io_hdr;
|
||
|
|
struct timeval start_tm, end_tm;
|
||
|
|
#ifdef SG_DEBUG
|
||
|
|
int clear = 1;
|
||
|
|
#endif
|
||
|
|
|
||
|
|
print_msg(TEST_BREAK, __FUNCTION__);
|
||
|
|
|
||
|
|
file_name = device;
|
||
|
|
|
||
|
|
sg_fd = open(file_name, O_RDONLY);
|
||
|
|
if (sg_fd < 0) {
|
||
|
|
perror(ME "open error");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
/* Don't worry, being very careful not to write to a none-sg file ... */
|
||
|
|
res = ioctl(sg_fd, SG_GET_VERSION_NUM, &k);
|
||
|
|
if ((res < 0) || (k < 30000)) {
|
||
|
|
printf(ME "not a sg device, or driver prior to 3.x\n");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
if (do_mmap) {
|
||
|
|
do_dio = 0;
|
||
|
|
do_quick = 0;
|
||
|
|
}
|
||
|
|
if (NULL == (rawp = malloc(512))) {
|
||
|
|
printf(ME "out of memory (query)\n");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
rbBuff = rawp;
|
||
|
|
|
||
|
|
memset(rbCmdBlk, 0, RB_CMD_LEN);
|
||
|
|
rbCmdBlk[0] = RB_OPCODE;
|
||
|
|
rbCmdBlk[1] = RB_MODE_DESC;
|
||
|
|
rbCmdBlk[8] = RB_DESC_LEN;
|
||
|
|
memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
|
||
|
|
io_hdr.interface_id = 'S';
|
||
|
|
io_hdr.cmd_len = sizeof(rbCmdBlk);
|
||
|
|
io_hdr.mx_sb_len = sizeof(sense_buffer);
|
||
|
|
io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
|
||
|
|
io_hdr.dxfer_len = RB_DESC_LEN;
|
||
|
|
io_hdr.dxferp = rbBuff;
|
||
|
|
io_hdr.cmdp = rbCmdBlk;
|
||
|
|
io_hdr.sbp = sense_buffer;
|
||
|
|
io_hdr.timeout = 60000; /* 60000 millisecs == 60 seconds */
|
||
|
|
/* do normal IO to find RB size (not dio or mmap-ed at this stage) */
|
||
|
|
|
||
|
|
if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
|
||
|
|
perror(ME "SG_IO READ BUFFER descriptor error");
|
||
|
|
if (rawp)
|
||
|
|
free(rawp);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* now for the error processing */
|
||
|
|
switch (sg_err_category3(&io_hdr)) {
|
||
|
|
case SG_ERR_CAT_CLEAN:
|
||
|
|
break;
|
||
|
|
case SG_ERR_CAT_RECOVERED:
|
||
|
|
printf
|
||
|
|
("Recovered error on READ BUFFER descriptor, continuing\n");
|
||
|
|
break;
|
||
|
|
default: /* won't bother decoding other categories */
|
||
|
|
sg_chk_n_print3("READ BUFFER descriptor error", &io_hdr);
|
||
|
|
if (rawp)
|
||
|
|
free(rawp);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
buf_capacity = ((rbBuff[1] << 16) | (rbBuff[2] << 8) | rbBuff[3]);
|
||
|
|
printf("READ BUFFER reports: buffer capacity=%d, offset boundary=%d\n",
|
||
|
|
buf_capacity, (int)rbBuff[0]);
|
||
|
|
|
||
|
|
if (0 == buf_size)
|
||
|
|
buf_size = buf_capacity;
|
||
|
|
else if (buf_size > buf_capacity) {
|
||
|
|
printf
|
||
|
|
("Requested buffer size=%d exceeds reported capacity=%d\n",
|
||
|
|
buf_size, buf_capacity);
|
||
|
|
if (rawp)
|
||
|
|
free(rawp);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
if (rawp) {
|
||
|
|
free(rawp);
|
||
|
|
rawp = NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!do_dio) {
|
||
|
|
k = buf_size;
|
||
|
|
if (do_mmap && (0 != (k % psz)))
|
||
|
|
k = ((k / psz) + 1) * psz; /* round up to page size */
|
||
|
|
res = ioctl(sg_fd, SG_SET_RESERVED_SIZE, &k);
|
||
|
|
if (res < 0)
|
||
|
|
perror(ME "SG_SET_RESERVED_SIZE error");
|
||
|
|
}
|
||
|
|
|
||
|
|
if (do_mmap) {
|
||
|
|
rbBuff = mmap(NULL, buf_size, PROT_READ, MAP_SHARED, sg_fd, 0);
|
||
|
|
if (MAP_FAILED == rbBuff) {
|
||
|
|
if (ENOMEM == errno)
|
||
|
|
printf(ME "mmap() out of memory, try a smaller "
|
||
|
|
"buffer size than %d KB\n",
|
||
|
|
buf_size / 1024);
|
||
|
|
else
|
||
|
|
perror(ME "error using mmap()");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
} else { /* non mmap-ed IO */
|
||
|
|
rawp = malloc(buf_size + (do_dio ? psz : 0));
|
||
|
|
if (NULL == rawp) {
|
||
|
|
printf(ME "out of memory (data)\n");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
if (do_dio) /* align to page boundary */
|
||
|
|
rbBuff =
|
||
|
|
(unsigned char *)(((unsigned long)rawp + psz - 1) &
|
||
|
|
(~(psz - 1)));
|
||
|
|
else
|
||
|
|
rbBuff = rawp;
|
||
|
|
}
|
||
|
|
|
||
|
|
num = (total_size_mb * 1024U * 1024U) / (unsigned int)buf_size;
|
||
|
|
if (do_time) {
|
||
|
|
start_tm.tv_sec = 0;
|
||
|
|
start_tm.tv_usec = 0;
|
||
|
|
gettimeofday(&start_tm, NULL);
|
||
|
|
}
|
||
|
|
/* main data reading loop */
|
||
|
|
for (k = 0; k < num; ++k) {
|
||
|
|
memset(rbCmdBlk, 0, RB_CMD_LEN);
|
||
|
|
rbCmdBlk[0] = RB_OPCODE;
|
||
|
|
rbCmdBlk[1] = RB_MODE_DATA;
|
||
|
|
rbCmdBlk[6] = 0xff & (buf_size >> 16);
|
||
|
|
rbCmdBlk[7] = 0xff & (buf_size >> 8);
|
||
|
|
rbCmdBlk[8] = 0xff & buf_size;
|
||
|
|
#ifdef SG_DEBUG
|
||
|
|
memset(rbBuff, 0, buf_size);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
|
||
|
|
io_hdr.interface_id = 'S';
|
||
|
|
io_hdr.cmd_len = sizeof(rbCmdBlk);
|
||
|
|
io_hdr.mx_sb_len = sizeof(sense_buffer);
|
||
|
|
io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
|
||
|
|
io_hdr.dxfer_len = buf_size;
|
||
|
|
if (!do_mmap)
|
||
|
|
io_hdr.dxferp = rbBuff;
|
||
|
|
io_hdr.cmdp = rbCmdBlk;
|
||
|
|
io_hdr.sbp = sense_buffer;
|
||
|
|
io_hdr.timeout = 20000; /* 20000 millisecs == 20 seconds */
|
||
|
|
io_hdr.pack_id = k;
|
||
|
|
if (do_mmap)
|
||
|
|
io_hdr.flags |= SG_FLAG_MMAP_IO;
|
||
|
|
else if (do_dio)
|
||
|
|
io_hdr.flags |= SG_FLAG_DIRECT_IO;
|
||
|
|
else if (do_quick)
|
||
|
|
io_hdr.flags |= SG_FLAG_NO_DXFER;
|
||
|
|
|
||
|
|
if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
|
||
|
|
if (ENOMEM == errno)
|
||
|
|
printf(ME
|
||
|
|
"SG_IO data; out of memory, try a smaller "
|
||
|
|
"buffer size than %d KB\n",
|
||
|
|
buf_size / 1024);
|
||
|
|
else
|
||
|
|
perror(ME "SG_IO READ BUFFER data error");
|
||
|
|
if (rawp)
|
||
|
|
free(rawp);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* now for the error processing */
|
||
|
|
switch (sg_err_category3(&io_hdr)) {
|
||
|
|
case SG_ERR_CAT_CLEAN:
|
||
|
|
break;
|
||
|
|
case SG_ERR_CAT_RECOVERED:
|
||
|
|
printf
|
||
|
|
("Recovered error on READ BUFFER data, continuing\n");
|
||
|
|
break;
|
||
|
|
default: /* won't bother decoding other categories */
|
||
|
|
sg_chk_n_print3("READ BUFFER data error", &io_hdr);
|
||
|
|
if (rawp)
|
||
|
|
free(rawp);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
if (do_dio &&
|
||
|
|
((io_hdr.info & SG_INFO_DIRECT_IO_MASK) !=
|
||
|
|
SG_INFO_DIRECT_IO))
|
||
|
|
dio_incomplete = 1; /* flag that dio not done (completely) */
|
||
|
|
|
||
|
|
#ifdef SG_DEBUG
|
||
|
|
if (clear) {
|
||
|
|
for (j = 0; j < buf_size; ++j) {
|
||
|
|
if (rbBuff[j] != 0) {
|
||
|
|
clear = 0;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
if ((do_time) && (start_tm.tv_sec || start_tm.tv_usec)) {
|
||
|
|
struct timeval res_tm;
|
||
|
|
double a, b;
|
||
|
|
|
||
|
|
gettimeofday(&end_tm, NULL);
|
||
|
|
res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec;
|
||
|
|
res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec;
|
||
|
|
if (res_tm.tv_usec < 0) {
|
||
|
|
--res_tm.tv_sec;
|
||
|
|
res_tm.tv_usec += 1000000;
|
||
|
|
}
|
||
|
|
a = res_tm.tv_sec;
|
||
|
|
a += (0.000001 * res_tm.tv_usec);
|
||
|
|
b = (double)buf_size *num;
|
||
|
|
printf("time to read data from buffer was %d.%06d secs",
|
||
|
|
(int)res_tm.tv_sec, (int)res_tm.tv_usec);
|
||
|
|
if ((a > 0.00001) && (b > 511))
|
||
|
|
printf(", %.2f MB/sec\n", b / (a * 1000000.0));
|
||
|
|
else
|
||
|
|
printf("\n");
|
||
|
|
}
|
||
|
|
if (dio_incomplete)
|
||
|
|
printf(">> direct IO requested but not done\n");
|
||
|
|
printf
|
||
|
|
("Read %u MBytes (actual %u MB, %u bytes), buffer size=%d KBytes\n",
|
||
|
|
total_size_mb, (num * buf_size) / 1048576, num * buf_size,
|
||
|
|
buf_size / 1024);
|
||
|
|
|
||
|
|
if (rawp)
|
||
|
|
free(rawp);
|
||
|
|
res = close(sg_fd);
|
||
|
|
if (res < 0) {
|
||
|
|
perror(ME "close error");
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
#ifdef SG_DEBUG
|
||
|
|
if (clear)
|
||
|
|
printf("read buffer always zero\n");
|
||
|
|
else
|
||
|
|
printf("read buffer non-zero\n");
|
||
|
|
#endif
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Performs a 10 byte READ CAPACITY command and fetches response. There is
|
||
|
|
* evidently a 16 byte READ CAPACITY command coming.
|
||
|
|
* Return of 0 -> success, -1 -> failure */
|
||
|
|
int do_readcap_10(int sg_fd, int pmi, unsigned int lba,
|
||
|
|
unsigned int *last_sect, unsigned int *sect_sz)
|
||
|
|
{
|
||
|
|
int res;
|
||
|
|
unsigned char rcCmdBlk[10] = { 0x25, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||
|
|
unsigned char rcBuff[RCAP_REPLY_LEN];
|
||
|
|
unsigned char sense_b[SENSE_BUFF_SZ];
|
||
|
|
sg_io_hdr_t io_hdr;
|
||
|
|
|
||
|
|
if (pmi) { /* lbs only valid when pmi set */
|
||
|
|
rcCmdBlk[8] |= 1;
|
||
|
|
rcCmdBlk[2] = (lba >> 24) & 0xff;
|
||
|
|
rcCmdBlk[3] = (lba >> 16) & 0xff;
|
||
|
|
rcCmdBlk[4] = (lba >> 8) & 0xff;
|
||
|
|
rcCmdBlk[5] = lba & 0xff;
|
||
|
|
}
|
||
|
|
memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
|
||
|
|
io_hdr.interface_id = 'S';
|
||
|
|
io_hdr.cmd_len = sizeof(rcCmdBlk);
|
||
|
|
io_hdr.mx_sb_len = sizeof(sense_b);
|
||
|
|
io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
|
||
|
|
io_hdr.dxfer_len = sizeof(rcBuff);
|
||
|
|
io_hdr.dxferp = rcBuff;
|
||
|
|
io_hdr.cmdp = rcCmdBlk;
|
||
|
|
io_hdr.sbp = sense_b;
|
||
|
|
io_hdr.timeout = 60000;
|
||
|
|
|
||
|
|
while (1) {
|
||
|
|
if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
|
||
|
|
perror("read_capacity (SG_IO) error");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
res = sg_err_category3(&io_hdr);
|
||
|
|
if (SG_ERR_CAT_MEDIA_CHANGED == res)
|
||
|
|
continue;
|
||
|
|
else if (SG_ERR_CAT_CLEAN != res) {
|
||
|
|
sg_chk_n_print3("READ CAPACITY command error", &io_hdr);
|
||
|
|
return -1;
|
||
|
|
} else
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
*last_sect = ((rcBuff[0] << 24) | (rcBuff[1] << 16) |
|
||
|
|
(rcBuff[2] << 8) | rcBuff[3]);
|
||
|
|
*sect_sz = (rcBuff[4] << 24) | (rcBuff[5] << 16) |
|
||
|
|
(rcBuff[6] << 8) | rcBuff[7];
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
int show_scsi_read_capacity(char *device)
|
||
|
|
{
|
||
|
|
int sg_fd, k, res;
|
||
|
|
unsigned int lba = 0;
|
||
|
|
int pmi = 1;
|
||
|
|
unsigned int last_blk_addr, block_size;
|
||
|
|
char ebuff[EBUFF_SZ];
|
||
|
|
const char *file_name = 0;
|
||
|
|
|
||
|
|
print_msg(TEST_BREAK, __FUNCTION__);
|
||
|
|
|
||
|
|
file_name = device;
|
||
|
|
|
||
|
|
if ((0 == pmi) && (lba > 0)) {
|
||
|
|
fprintf(stderr,
|
||
|
|
ME "lba can only be non-zero when pmi is set\n");
|
||
|
|
usage();
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
if ((sg_fd = open(file_name, O_RDONLY)) < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ, ME "error opening file: %s",
|
||
|
|
file_name);
|
||
|
|
perror(ebuff);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
/* Just to be safe, check we have a new sg device by trying an ioctl */
|
||
|
|
if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) {
|
||
|
|
printf(ME "%s doesn't seem to be a version 3 sg device\n",
|
||
|
|
file_name);
|
||
|
|
close(sg_fd);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
res = do_readcap_10(sg_fd, pmi, lba, &last_blk_addr, &block_size);
|
||
|
|
|
||
|
|
if (0 == res) {
|
||
|
|
printf("Read Capacity results:\n");
|
||
|
|
if (pmi)
|
||
|
|
printf(" PMI mode: given lba=0x%x, last block before "
|
||
|
|
"delay=0x%x\n", lba, last_blk_addr);
|
||
|
|
else
|
||
|
|
printf
|
||
|
|
(" Last block address=%u (0x%x), Number of blocks=%u\n",
|
||
|
|
last_blk_addr, last_blk_addr, last_blk_addr + 1);
|
||
|
|
printf(" Block size = %u bytes\n", block_size);
|
||
|
|
}
|
||
|
|
close(sg_fd);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
int do_scsi_reset_devices(char *device, int reset_opt)
|
||
|
|
{
|
||
|
|
int sg_fd, res, k;
|
||
|
|
int do_device_reset = 0;
|
||
|
|
int do_bus_reset = 0;
|
||
|
|
int do_host_reset = 0;
|
||
|
|
char *file_name = 0;
|
||
|
|
|
||
|
|
switch (reset_opt) {
|
||
|
|
case DEVICE_RESET:
|
||
|
|
print_msg(TEST_BREAK, __FUNCTION__);
|
||
|
|
do_device_reset = 1;
|
||
|
|
break;
|
||
|
|
case HOST_RESET:
|
||
|
|
do_host_reset = 1;
|
||
|
|
break;
|
||
|
|
case BUS_RESET:
|
||
|
|
do_bus_reset = 1;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
file_name = device;
|
||
|
|
|
||
|
|
sg_fd = open(file_name, O_RDWR | O_NONBLOCK);
|
||
|
|
if (sg_fd < 0) {
|
||
|
|
perror("sg_reset: open error");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
k = SG_SCSI_RESET_NOTHING;
|
||
|
|
if (do_device_reset) {
|
||
|
|
printf("sg_reset: starting device reset\n");
|
||
|
|
k = SG_SCSI_RESET_DEVICE;
|
||
|
|
} else if (do_bus_reset) {
|
||
|
|
printf("sg_reset: starting bus reset\n");
|
||
|
|
k = SG_SCSI_RESET_BUS;
|
||
|
|
} else if (do_host_reset) {
|
||
|
|
printf("sg_reset: starting host reset\n");
|
||
|
|
k = SG_SCSI_RESET_HOST;
|
||
|
|
}
|
||
|
|
|
||
|
|
res = ioctl(sg_fd, SG_SCSI_RESET, &k);
|
||
|
|
if (res < 0) {
|
||
|
|
if (EBUSY == errno)
|
||
|
|
printf("sg_reset: BUSY, may be resetting now\n");
|
||
|
|
else if (EIO == errno)
|
||
|
|
printf
|
||
|
|
("sg_reset: requested type of reset may not be available\n");
|
||
|
|
else if (EACCES == errno)
|
||
|
|
printf("sg_reset: reset requires CAP_SYS_ADMIN (root) "
|
||
|
|
"permission\n");
|
||
|
|
else if (EINVAL == errno)
|
||
|
|
printf("sg_reset: SG_SCSI_RESET not supported\n");
|
||
|
|
else if (EIO == errno)
|
||
|
|
printf("sg_reset: scsi_reset_provider() call failed\n");
|
||
|
|
else
|
||
|
|
perror("sg_reset: SG_SCSI_RESET failed");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
if (SG_SCSI_RESET_NOTHING == k)
|
||
|
|
printf("sg_reset: did nothing, device is normal mode\n");
|
||
|
|
else if (SG_SCSI_RESET_DEVICE == k)
|
||
|
|
printf("sg_reset: completed device reset\n");
|
||
|
|
else if (SG_SCSI_RESET_BUS == k)
|
||
|
|
printf("sg_reset: completed bus reset\n");
|
||
|
|
else if (SG_SCSI_RESET_HOST == k)
|
||
|
|
printf("sg_reset: completed host reset\n");
|
||
|
|
|
||
|
|
if (close(sg_fd) < 0) {
|
||
|
|
perror("sg_reset: close error");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int do_senddiag(int sg_fd, int sf_code, int pf_bit, int sf_bit,
|
||
|
|
int devofl_bit, int unitofl_bit, void *outgoing_pg,
|
||
|
|
int outgoing_len, int noisy)
|
||
|
|
{
|
||
|
|
int res;
|
||
|
|
unsigned char senddiagCmdBlk[SEND_DIAGNOSTIC_CMDLEN] =
|
||
|
|
{ SEND_DIAGNOSTIC_CMD, 0, 0, 0, 0, 0 };
|
||
|
|
unsigned char sense_b[SENSE_BUFF_LEN];
|
||
|
|
sg_io_hdr_t io_hdr;
|
||
|
|
|
||
|
|
senddiagCmdBlk[1] = (unsigned char)((sf_code << 5) | (pf_bit << 4) |
|
||
|
|
(sf_bit << 2) | (devofl_bit << 1) |
|
||
|
|
unitofl_bit);
|
||
|
|
senddiagCmdBlk[3] = (unsigned char)((outgoing_len >> 8) & 0xff);
|
||
|
|
senddiagCmdBlk[4] = (unsigned char)(outgoing_len & 0xff);
|
||
|
|
|
||
|
|
memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
|
||
|
|
io_hdr.interface_id = 'S';
|
||
|
|
io_hdr.cmd_len = SEND_DIAGNOSTIC_CMDLEN;
|
||
|
|
io_hdr.mx_sb_len = sizeof(sense_b);
|
||
|
|
io_hdr.dxfer_direction = outgoing_len ? SG_DXFER_TO_DEV : SG_DXFER_NONE;
|
||
|
|
io_hdr.dxfer_len = outgoing_len;
|
||
|
|
io_hdr.dxferp = outgoing_pg;
|
||
|
|
io_hdr.cmdp = senddiagCmdBlk;
|
||
|
|
io_hdr.sbp = sense_b;
|
||
|
|
io_hdr.timeout = LONG_TIMEOUT;
|
||
|
|
|
||
|
|
if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
|
||
|
|
perror("SG_IO (send diagnostic) error");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
res = sg_err_category3(&io_hdr);
|
||
|
|
switch (res) {
|
||
|
|
case SG_ERR_CAT_CLEAN:
|
||
|
|
case SG_ERR_CAT_RECOVERED:
|
||
|
|
return 0;
|
||
|
|
default:
|
||
|
|
if (noisy) {
|
||
|
|
char ebuff[EBUFF_SZ];
|
||
|
|
snprintf(ebuff, EBUFF_SZ,
|
||
|
|
"Send diagnostic error, sf_code=0x%x, "
|
||
|
|
"pf_bit=%d, sf_bit=%d ", sf_code, pf_bit,
|
||
|
|
sf_bit);
|
||
|
|
sg_chk_n_print3(ebuff, &io_hdr);
|
||
|
|
}
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static int do_rcvdiag(int sg_fd, int pcv, int pg_code, void *resp,
|
||
|
|
int mx_resp_len, int noisy)
|
||
|
|
{
|
||
|
|
int res;
|
||
|
|
unsigned char rcvdiagCmdBlk[RECEIVE_DIAGNOSTIC_CMDLEN] =
|
||
|
|
{ RECEIVE_DIAGNOSTIC_CMD, 0, 0, 0, 0, 0 };
|
||
|
|
unsigned char sense_b[SENSE_BUFF_LEN];
|
||
|
|
sg_io_hdr_t io_hdr;
|
||
|
|
|
||
|
|
rcvdiagCmdBlk[1] = (unsigned char)(pcv ? 0x1 : 0);
|
||
|
|
rcvdiagCmdBlk[2] = (unsigned char)(pg_code);
|
||
|
|
rcvdiagCmdBlk[3] = (unsigned char)((mx_resp_len >> 8) & 0xff);
|
||
|
|
rcvdiagCmdBlk[4] = (unsigned char)(mx_resp_len & 0xff);
|
||
|
|
|
||
|
|
memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
|
||
|
|
io_hdr.interface_id = 'S';
|
||
|
|
io_hdr.cmd_len = RECEIVE_DIAGNOSTIC_CMDLEN;
|
||
|
|
io_hdr.mx_sb_len = sizeof(sense_b);
|
||
|
|
io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
|
||
|
|
io_hdr.dxfer_len = mx_resp_len;
|
||
|
|
io_hdr.dxferp = resp;
|
||
|
|
io_hdr.cmdp = rcvdiagCmdBlk;
|
||
|
|
io_hdr.sbp = sense_b;
|
||
|
|
io_hdr.timeout = DEF_TIMEOUT;
|
||
|
|
|
||
|
|
if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
|
||
|
|
perror("SG_IO (receive diagnostic) error");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
res = sg_err_category3(&io_hdr);
|
||
|
|
switch (res) {
|
||
|
|
case SG_ERR_CAT_CLEAN:
|
||
|
|
case SG_ERR_CAT_RECOVERED:
|
||
|
|
return 0;
|
||
|
|
default:
|
||
|
|
if (noisy) {
|
||
|
|
char ebuff[EBUFF_SZ];
|
||
|
|
snprintf(ebuff, EBUFF_SZ,
|
||
|
|
"Receive diagnostic error, pcv=%d, "
|
||
|
|
"page_code=%x ", pcv, pg_code);
|
||
|
|
sg_chk_n_print3(ebuff, &io_hdr);
|
||
|
|
}
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Get last extended self-test time from mode page 0xa (for '-e' option) */
|
||
|
|
static int do_modes_0a(int sg_fd, void *resp, int mx_resp_len, int noisy,
|
||
|
|
int mode6)
|
||
|
|
{
|
||
|
|
int res;
|
||
|
|
unsigned char modesCmdBlk[MODE_SENSE10_CMDLEN] =
|
||
|
|
{ MODE_SENSE10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||
|
|
unsigned char sense_b[SENSE_BUFF_LEN];
|
||
|
|
sg_io_hdr_t io_hdr;
|
||
|
|
int dbd = 1;
|
||
|
|
int pc = 0;
|
||
|
|
int pg_code = 0xa;
|
||
|
|
|
||
|
|
modesCmdBlk[1] = (unsigned char)(dbd ? 0x8 : 0);
|
||
|
|
modesCmdBlk[2] = (unsigned char)(((pc << 6) & 0xc0) | (pg_code & 0x3f));
|
||
|
|
if (mx_resp_len > (mode6 ? 0xff : 0xffff)) {
|
||
|
|
printf(ME "mx_resp_len too big\n");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
if (mode6) {
|
||
|
|
modesCmdBlk[0] = MODE_SENSE6_CMD;
|
||
|
|
modesCmdBlk[4] = (unsigned char)(mx_resp_len & 0xff);
|
||
|
|
} else {
|
||
|
|
modesCmdBlk[7] = (unsigned char)((mx_resp_len >> 8) & 0xff);
|
||
|
|
modesCmdBlk[8] = (unsigned char)(mx_resp_len & 0xff);
|
||
|
|
}
|
||
|
|
|
||
|
|
memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
|
||
|
|
io_hdr.interface_id = 'S';
|
||
|
|
io_hdr.cmd_len = mode6 ? MODE_SENSE6_CMDLEN : MODE_SENSE10_CMDLEN;
|
||
|
|
io_hdr.mx_sb_len = sizeof(sense_b);
|
||
|
|
io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
|
||
|
|
io_hdr.dxfer_len = mx_resp_len;
|
||
|
|
io_hdr.dxferp = resp;
|
||
|
|
io_hdr.cmdp = modesCmdBlk;
|
||
|
|
io_hdr.sbp = sense_b;
|
||
|
|
io_hdr.timeout = DEF_TIMEOUT;
|
||
|
|
|
||
|
|
if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
|
||
|
|
perror("SG_IO (mode sense) error");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
res = sg_err_category3(&io_hdr);
|
||
|
|
switch (res) {
|
||
|
|
case SG_ERR_CAT_CLEAN:
|
||
|
|
case SG_ERR_CAT_RECOVERED:
|
||
|
|
return 0;
|
||
|
|
default:
|
||
|
|
if (noisy) {
|
||
|
|
char ebuff[EBUFF_SZ];
|
||
|
|
snprintf(ebuff, EBUFF_SZ, "Mode sense error, dbd=%d, "
|
||
|
|
"pc=%d, page_code=%x ", dbd, pc, pg_code);
|
||
|
|
sg_chk_n_print3(ebuff, &io_hdr);
|
||
|
|
}
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
int do_scsi_send_diagnostics(char *device)
|
||
|
|
{
|
||
|
|
int sg_fd, k, num, rsp_len;
|
||
|
|
char *file_name = 0;
|
||
|
|
unsigned char rsp_buff[MODE_ALLOC_LEN];
|
||
|
|
int rsp_buff_size = MODE_ALLOC_LEN;
|
||
|
|
int self_test_code = 6;
|
||
|
|
int do_pf = 0;
|
||
|
|
int do_doff = 0;
|
||
|
|
int do_def_test = 0;
|
||
|
|
int do_uoff = 0;
|
||
|
|
int oflags = O_RDWR;
|
||
|
|
|
||
|
|
print_msg(TEST_BREAK, __FUNCTION__);
|
||
|
|
|
||
|
|
file_name = device;
|
||
|
|
|
||
|
|
if ((sg_fd = open(file_name, oflags)) < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ, ME "error opening file: %s",
|
||
|
|
file_name);
|
||
|
|
perror(ebuff);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
/* Just to be safe, check we have a new sg device by trying an ioctl */
|
||
|
|
if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) {
|
||
|
|
printf(ME "%s doesn't seem to be a version 3 sg device\n",
|
||
|
|
file_name);
|
||
|
|
close(sg_fd);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (0 == do_modes_0a(sg_fd, rsp_buff, 32, 1, 0)) {
|
||
|
|
/* Assume mode sense(10) response without block descriptors */
|
||
|
|
num = (rsp_buff[0] << 8) + rsp_buff[1] - 6;
|
||
|
|
if (num >= 0xc) {
|
||
|
|
int secs;
|
||
|
|
|
||
|
|
secs = (rsp_buff[18] << 8) + rsp_buff[19];
|
||
|
|
printf
|
||
|
|
("Previous extended self-test duration=%d seconds "
|
||
|
|
"(%.2f minutes)\n", secs, secs / 60.0);
|
||
|
|
} else
|
||
|
|
printf("Extended self-test duration not available\n");
|
||
|
|
} else
|
||
|
|
printf("Extended self-test duration (mode page 0xa) failed\n");
|
||
|
|
|
||
|
|
memset(rsp_buff, 0, sizeof(rsp_buff));
|
||
|
|
if (0 == do_senddiag(sg_fd, 0, do_pf, 0, 0, 0, rsp_buff, 4, 1)) {
|
||
|
|
if (0 == do_rcvdiag(sg_fd, 0, 0, rsp_buff, rsp_buff_size, 1)) {
|
||
|
|
printf("Supported diagnostic pages response:\n");
|
||
|
|
rsp_len = (rsp_buff[2] << 8) + rsp_buff[3] + 4;
|
||
|
|
for (k = 0; k < (rsp_len - 4); ++k)
|
||
|
|
printf(" %s\n",
|
||
|
|
find_page_code_desc(rsp_buff[k + 4], 0));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (0 == do_senddiag(sg_fd, self_test_code, do_pf, do_def_test,
|
||
|
|
do_doff, do_uoff, NULL, 0, 1)) {
|
||
|
|
if ((5 == self_test_code) || (6 == self_test_code))
|
||
|
|
printf("Foreground self test returned GOOD status\n");
|
||
|
|
else if (do_def_test && (!do_doff) && (!do_uoff))
|
||
|
|
printf("Default self test returned GOOD status\n");
|
||
|
|
}
|
||
|
|
close(sg_fd);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void do_start_stop(int fd, int start, int immed, int loej,
|
||
|
|
int power_conditions)
|
||
|
|
{
|
||
|
|
unsigned char cmdblk[6] = {
|
||
|
|
START_STOP, /* Command */
|
||
|
|
0, /* Resvd/Immed */
|
||
|
|
0, /* Reserved */
|
||
|
|
0, /* Reserved */
|
||
|
|
0, /* PowCond/Resvd/LoEj/Start */
|
||
|
|
0
|
||
|
|
}; /* Reserved/Flag/Link */
|
||
|
|
unsigned char sense_b[32];
|
||
|
|
sg_io_hdr_t io_hdr;
|
||
|
|
int k, res, debug = 1;
|
||
|
|
|
||
|
|
memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
|
||
|
|
cmdblk[1] = immed & 1;
|
||
|
|
cmdblk[4] = ((power_conditions & 0xf) << 4) |
|
||
|
|
((loej & 1) << 1) | (start & 1);
|
||
|
|
io_hdr.interface_id = 'S';
|
||
|
|
io_hdr.cmd_len = sizeof(cmdblk);
|
||
|
|
io_hdr.mx_sb_len = sizeof(sense_b);
|
||
|
|
io_hdr.dxfer_direction = SG_DXFER_NONE;
|
||
|
|
io_hdr.dxfer_len = 0;
|
||
|
|
io_hdr.dxferp = NULL;
|
||
|
|
io_hdr.cmdp = cmdblk;
|
||
|
|
io_hdr.sbp = sense_b;
|
||
|
|
io_hdr.timeout = DEF_START_TIMEOUT;
|
||
|
|
|
||
|
|
if (debug) {
|
||
|
|
printf(" Start/Stop command:");
|
||
|
|
for (k = 0; k < 6; ++k)
|
||
|
|
printf(" %02x", cmdblk[k]);
|
||
|
|
printf("\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
if (ioctl(fd, SG_IO, &io_hdr) < 0) {
|
||
|
|
perror("start_stop (SG_IO) error");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
res = sg_err_category3(&io_hdr);
|
||
|
|
if (SG_ERR_CAT_MEDIA_CHANGED == res) {
|
||
|
|
fprintf(stderr, "media change report, try start_stop again\n");
|
||
|
|
if (ioctl(fd, SG_IO, &io_hdr) < 0) {
|
||
|
|
perror("start_stop (SG_IO) error");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (SG_ERR_CAT_CLEAN != res) {
|
||
|
|
sg_chk_n_print3("start_stop", &io_hdr);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (debug)
|
||
|
|
fprintf(stderr, "start_stop [%s] successful\n",
|
||
|
|
start ? "start" : "stop");
|
||
|
|
}
|
||
|
|
|
||
|
|
static void do_sync_cache(int fd)
|
||
|
|
{
|
||
|
|
unsigned char cmdblk[10] = {
|
||
|
|
SYNCHRONIZE_CACHE, /* Command */
|
||
|
|
0, /* Immed (2) */
|
||
|
|
0, 0, 0, 0, /* LBA */
|
||
|
|
0, /* Reserved */
|
||
|
|
0, 0, /* No of blocks */
|
||
|
|
0
|
||
|
|
}; /* Reserved/Flag/Link */
|
||
|
|
unsigned char sense_b[32];
|
||
|
|
sg_io_hdr_t io_hdr;
|
||
|
|
int res, debug = 1;
|
||
|
|
|
||
|
|
memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
|
||
|
|
io_hdr.interface_id = 'S';
|
||
|
|
io_hdr.cmd_len = sizeof(cmdblk);
|
||
|
|
io_hdr.mx_sb_len = sizeof(sense_b);
|
||
|
|
io_hdr.dxfer_direction = SG_DXFER_NONE;
|
||
|
|
io_hdr.dxfer_len = 0;
|
||
|
|
io_hdr.dxferp = NULL;
|
||
|
|
io_hdr.cmdp = cmdblk;
|
||
|
|
io_hdr.sbp = sense_b;
|
||
|
|
io_hdr.timeout = DEF_START_TIMEOUT;
|
||
|
|
|
||
|
|
if (ioctl(fd, SG_IO, &io_hdr) < 0) {
|
||
|
|
perror("sync_cache (SG_IO) error");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
res = sg_err_category3(&io_hdr);
|
||
|
|
if (SG_ERR_CAT_MEDIA_CHANGED == res) {
|
||
|
|
fprintf(stderr, "media change report, try sync_cache again\n");
|
||
|
|
if (ioctl(fd, SG_IO, &io_hdr) < 0) {
|
||
|
|
perror("sync_cache (SG_IO) error");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (SG_ERR_CAT_CLEAN != res) {
|
||
|
|
sg_chk_n_print3("sync_cache", &io_hdr);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (debug)
|
||
|
|
fprintf(stderr, "synchronize cache successful\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
int do_scsi_start_stop(char *device, int startstop)
|
||
|
|
{
|
||
|
|
int synccache = 1;
|
||
|
|
char *file_name = 0;
|
||
|
|
int fd;
|
||
|
|
int immed = 1;
|
||
|
|
int loej = 0;
|
||
|
|
int power_conds = 0;
|
||
|
|
|
||
|
|
print_msg(TEST_BREAK, __FUNCTION__);
|
||
|
|
|
||
|
|
file_name = device;
|
||
|
|
|
||
|
|
fd = open(file_name, O_RDWR | O_NONBLOCK);
|
||
|
|
if (fd < 0) {
|
||
|
|
fprintf(stderr, "Error trying to open %s\n", file_name);
|
||
|
|
perror("");
|
||
|
|
usage();
|
||
|
|
return 2;
|
||
|
|
}
|
||
|
|
if (ioctl(fd, SG_GET_TIMEOUT, 0) < 0) {
|
||
|
|
fprintf(stderr, "Given file not block or SCSI "
|
||
|
|
"generic device\n");
|
||
|
|
close(fd);
|
||
|
|
return 3;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (synccache)
|
||
|
|
do_sync_cache(fd);
|
||
|
|
|
||
|
|
if (power_conds > 0)
|
||
|
|
do_start_stop(fd, 0, immed, 0, power_conds);
|
||
|
|
else if (startstop != -1)
|
||
|
|
do_start_stop(fd, startstop, immed, loej, 0);
|
||
|
|
|
||
|
|
close(fd);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
int find_out_about_buffer(int sg_fd, int *buf_capacity, char *file_name)
|
||
|
|
{
|
||
|
|
int res, buf_granul = 255;
|
||
|
|
unsigned char *rbBuff = malloc(OFF + sizeof(rbCmdBlk) + 512);
|
||
|
|
struct sg_header *rsghp = (struct sg_header *)rbBuff;
|
||
|
|
int rbInLen = OFF + RB_DESC_LEN;
|
||
|
|
int rbOutLen = OFF + sizeof(rbCmdBlk);
|
||
|
|
unsigned char *buffp = rbBuff + OFF;
|
||
|
|
rsghp->pack_len = 0; /* don't care */
|
||
|
|
rsghp->pack_id = 0;
|
||
|
|
rsghp->reply_len = rbInLen;
|
||
|
|
rsghp->twelve_byte = 0;
|
||
|
|
rsghp->result = 0;
|
||
|
|
#ifndef SG_GET_RESERVED_SIZE
|
||
|
|
rsghp->sense_buffer[0] = 0;
|
||
|
|
#endif
|
||
|
|
memcpy(rbBuff + OFF, rbCmdBlk, sizeof(rbCmdBlk));
|
||
|
|
rbBuff[OFF + 1] = RB_MODE_DESC;
|
||
|
|
rbBuff[OFF + 8] = RB_DESC_LEN;
|
||
|
|
|
||
|
|
res = write(sg_fd, rbBuff, rbOutLen);
|
||
|
|
if (res < 0) {
|
||
|
|
perror("sg_test_rwbuf: write (desc) error");
|
||
|
|
if (rbBuff)
|
||
|
|
free(rbBuff);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
if (res < rbOutLen) {
|
||
|
|
printf("sg_test_rwbuf: wrote less (desc), ask=%d, got=%d\n",
|
||
|
|
rbOutLen, res);
|
||
|
|
if (rbBuff)
|
||
|
|
free(rbBuff);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
memset(rbBuff + OFF, 0, RB_DESC_LEN);
|
||
|
|
res = read(sg_fd, rbBuff, rbInLen);
|
||
|
|
if (res < 0) {
|
||
|
|
perror("sg_test_rwbuf: read (desc) error");
|
||
|
|
if (rbBuff)
|
||
|
|
free(rbBuff);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
if (res < rbInLen) {
|
||
|
|
printf("sg_test_rwbuf: read less (desc), ask=%d, got=%d\n",
|
||
|
|
rbInLen, res);
|
||
|
|
if (rbBuff)
|
||
|
|
free(rbBuff);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
#ifdef SG_GET_RESERVED_SIZE
|
||
|
|
if (!sg_chk_n_print("sg_test_rwbuf: desc", rsghp->target_status,
|
||
|
|
rsghp->host_status, rsghp->driver_status,
|
||
|
|
rsghp->sense_buffer, SG_MAX_SENSE)) {
|
||
|
|
printf
|
||
|
|
("sg_test_rwbuf: perhaps %s doesn't support READ BUFFER\n",
|
||
|
|
file_name);
|
||
|
|
if (rbBuff)
|
||
|
|
free(rbBuff);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
#else
|
||
|
|
if ((rsghp->result != 0) || (0 != rsghp->sense_buffer[0])) {
|
||
|
|
printf("sg_test_rwbuf: read(desc) result=%d\n", rsghp->result);
|
||
|
|
if (0 != rsghp->sense_buffer[0])
|
||
|
|
sg_print_sense("sg_test_rwbuf: desc",
|
||
|
|
rsghp->sense_buffer, SG_MAX_SENSE);
|
||
|
|
printf
|
||
|
|
("sg_test_rwbuf: perhaps %s doesn't support READ BUFFER\n",
|
||
|
|
file_name);
|
||
|
|
if (rbBuff)
|
||
|
|
free(rbBuff);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
*(buf_capacity) = ((buffp[1] << 16) | (buffp[2] << 8) | buffp[3]);
|
||
|
|
buf_granul = (unsigned char)buffp[0];
|
||
|
|
|
||
|
|
printf("READ BUFFER reports: %02x %02x %02x %02x %02x %02x %02x %02x\n",
|
||
|
|
buffp[0], buffp[1], buffp[2], buffp[3],
|
||
|
|
buffp[4], buffp[5], buffp[6], buffp[7]);
|
||
|
|
|
||
|
|
printf("READ BUFFER reports: buffer capacity=%d, offset boundary=%d\n",
|
||
|
|
*(buf_capacity), buf_granul);
|
||
|
|
#ifdef SG_DEF_RESERVED_SIZE
|
||
|
|
res = ioctl(sg_fd, SG_SET_RESERVED_SIZE, buf_capacity);
|
||
|
|
if (res < 0)
|
||
|
|
perror("sg_test_rwbuf: SG_SET_RESERVED_SIZE error");
|
||
|
|
#endif
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
int mymemcmp(unsigned char *bf1, unsigned char *bf2, int len)
|
||
|
|
{
|
||
|
|
int df;
|
||
|
|
for (df = 0; df < len; df++)
|
||
|
|
if (bf1[df] != bf2[df])
|
||
|
|
return df;
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
int do_checksum(int *buf, int len, int quiet)
|
||
|
|
{
|
||
|
|
int sum = base;
|
||
|
|
int i;
|
||
|
|
int rln = len;
|
||
|
|
for (i = 0; i < len / BPI; i++)
|
||
|
|
sum += buf[i];
|
||
|
|
while (rln % BPI)
|
||
|
|
sum += ((char *)buf)[--rln];
|
||
|
|
if (sum != READWRITE_BASE_NUM) {
|
||
|
|
if (!quiet)
|
||
|
|
printf("sg_test_rwbuf: Checksum error (sz=%i): %08x\n",
|
||
|
|
len, sum);
|
||
|
|
if (cmpbuf && !quiet) {
|
||
|
|
int diff = mymemcmp(cmpbuf, (unsigned char *)buf, len);
|
||
|
|
printf("Differ at pos %i/%i:\n", diff, len);
|
||
|
|
for (i = 0; i < 24 && i + diff < len; i++)
|
||
|
|
printf(" %02x", cmpbuf[i + diff]);
|
||
|
|
printf("\n");
|
||
|
|
for (i = 0; i < 24 && i + diff < len; i++)
|
||
|
|
printf(" %02x",
|
||
|
|
((unsigned char *)buf)[i + diff]);
|
||
|
|
printf("\n");
|
||
|
|
}
|
||
|
|
return 2;
|
||
|
|
} else
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
void do_fill_buffer(int *buf, int len)
|
||
|
|
{
|
||
|
|
int sum;
|
||
|
|
int i;
|
||
|
|
int rln = len;
|
||
|
|
srand(time(0));
|
||
|
|
retry:
|
||
|
|
if (len >= BPI)
|
||
|
|
base = READWRITE_BASE_NUM + rand();
|
||
|
|
else
|
||
|
|
base = READWRITE_BASE_NUM + (char)rand();
|
||
|
|
sum = base;
|
||
|
|
for (i = 0; i < len / BPI - 1; i++) {
|
||
|
|
/* we rely on rand() giving full range of int */
|
||
|
|
buf[i] = rand();
|
||
|
|
sum += buf[i];
|
||
|
|
}
|
||
|
|
while (rln % BPI) {
|
||
|
|
((char *)buf)[--rln] = rand();
|
||
|
|
sum += ((char *)buf)[rln];
|
||
|
|
}
|
||
|
|
if (len >= BPI)
|
||
|
|
buf[len / BPI - 1] = READWRITE_BASE_NUM - sum;
|
||
|
|
else
|
||
|
|
((char *)buf)[0] = READWRITE_BASE_NUM + ((char *)buf)[0] - sum;
|
||
|
|
if (do_checksum(buf, len, 1)) {
|
||
|
|
if (len < BPI)
|
||
|
|
goto retry;
|
||
|
|
printf("sg_test_rwbuf: Memory corruption?\n");
|
||
|
|
exit(1);
|
||
|
|
}
|
||
|
|
if (cmpbuf)
|
||
|
|
memcpy(cmpbuf, (char *)buf, len);
|
||
|
|
}
|
||
|
|
|
||
|
|
int read_buffer(int sg_fd, unsigned size)
|
||
|
|
{
|
||
|
|
int res;
|
||
|
|
unsigned char *rbBuff = malloc(OFF + sizeof(rbCmdBlk) + size);
|
||
|
|
struct sg_header *rsghp = (struct sg_header *)rbBuff;
|
||
|
|
|
||
|
|
int rbInLen = OFF + size;
|
||
|
|
int rbOutLen = OFF + sizeof(rbCmdBlk);
|
||
|
|
memset(rbBuff, 0, OFF + sizeof(rbCmdBlk) + size);
|
||
|
|
rsghp->pack_len = 0; /* don't care */
|
||
|
|
rsghp->reply_len = rbInLen;
|
||
|
|
rsghp->twelve_byte = 0;
|
||
|
|
rsghp->result = 0;
|
||
|
|
memcpy(rbBuff + OFF, rbCmdBlk, sizeof(rbCmdBlk));
|
||
|
|
rbBuff[OFF + 1] = RB_MODE_DATA;
|
||
|
|
rbBuff[OFF + 6] = 0xff & ((size) >> 16);
|
||
|
|
rbBuff[OFF + 7] = 0xff & ((size) >> 8);
|
||
|
|
rbBuff[OFF + 8] = 0xff & (size);
|
||
|
|
|
||
|
|
rsghp->pack_id = 2;
|
||
|
|
res = write(sg_fd, rbBuff, rbOutLen);
|
||
|
|
if (res < 0) {
|
||
|
|
perror("sg_test_rwbuf: write (data) error");
|
||
|
|
if (rbBuff)
|
||
|
|
free(rbBuff);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
if (res < rbOutLen) {
|
||
|
|
printf("sg_test_rwbuf: wrote less (data), ask=%d, got=%d\n",
|
||
|
|
rbOutLen, res);
|
||
|
|
if (rbBuff)
|
||
|
|
free(rbBuff);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
res = read(sg_fd, rbBuff, rbInLen);
|
||
|
|
if (res < 0) {
|
||
|
|
perror("sg_test_rwbuf: read (data) error");
|
||
|
|
if (rbBuff)
|
||
|
|
free(rbBuff);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
if (res < rbInLen) {
|
||
|
|
printf("sg_test_rwbuf: read less (data), ask=%d, got=%d\n",
|
||
|
|
rbInLen, res);
|
||
|
|
if (rbBuff)
|
||
|
|
free(rbBuff);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
res = do_checksum((int *)(rbBuff + OFF), size, 0);
|
||
|
|
if (rbBuff)
|
||
|
|
free(rbBuff);
|
||
|
|
return res;
|
||
|
|
}
|
||
|
|
|
||
|
|
int write_buffer(int sg_fd, unsigned size)
|
||
|
|
{
|
||
|
|
int res;
|
||
|
|
unsigned char *rbBuff = malloc(OFF + sizeof(rbCmdBlk) + size);
|
||
|
|
struct sg_header *rsghp = (struct sg_header *)rbBuff;
|
||
|
|
//unsigned char * buffp = rbBuff + OFF;
|
||
|
|
|
||
|
|
int rbInLen = OFF;
|
||
|
|
int rbOutLen = OFF + sizeof(rbCmdBlk) + size;
|
||
|
|
|
||
|
|
do_fill_buffer((int *)(rbBuff + OFF + sizeof(rbCmdBlk)), size);
|
||
|
|
rsghp->pack_len = 0; /* don't care */
|
||
|
|
rsghp->reply_len = rbInLen;
|
||
|
|
rsghp->twelve_byte = 0;
|
||
|
|
rsghp->result = 0;
|
||
|
|
memcpy(rbBuff + OFF, rbCmdBlk, sizeof(rbCmdBlk));
|
||
|
|
rbBuff[OFF + 0] = WRITE_BUFFER;
|
||
|
|
rbBuff[OFF + 1] = RB_MODE_DATA;
|
||
|
|
rbBuff[OFF + 6] = 0xff & ((size) >> 16);
|
||
|
|
rbBuff[OFF + 7] = 0xff & ((size) >> 8);
|
||
|
|
rbBuff[OFF + 8] = 0xff & (size);
|
||
|
|
|
||
|
|
rsghp->pack_id = 1;
|
||
|
|
res = write(sg_fd, rbBuff, rbOutLen);
|
||
|
|
if (res < 0) {
|
||
|
|
perror("sg_test_rwbuf: write (data) error");
|
||
|
|
if (rbBuff)
|
||
|
|
free(rbBuff);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
if (res < rbOutLen) {
|
||
|
|
printf("sg_test_rwbuf: wrote less (data), ask=%d, got=%d\n",
|
||
|
|
rbOutLen, res);
|
||
|
|
if (rbBuff)
|
||
|
|
free(rbBuff);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
res = read(sg_fd, rbBuff, rbInLen);
|
||
|
|
if (res < 0) {
|
||
|
|
perror("sg_test_rwbuf: read (status) error");
|
||
|
|
if (rbBuff)
|
||
|
|
free(rbBuff);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
if (rbBuff)
|
||
|
|
free(rbBuff);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
int do_scsi_read_write_buffer(char *device)
|
||
|
|
{
|
||
|
|
int sg_fd;
|
||
|
|
int res, buf_capacity;
|
||
|
|
char *file_name = device;
|
||
|
|
struct stat a_st;
|
||
|
|
int block_dev = 0;
|
||
|
|
|
||
|
|
print_msg(TEST_BREAK, __FUNCTION__);
|
||
|
|
|
||
|
|
sg_fd = open(file_name, O_RDWR);
|
||
|
|
if (sg_fd < 0) {
|
||
|
|
perror("sg_test_rwbuf: open error");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
if (fstat(sg_fd, &a_st) < 0) {
|
||
|
|
fprintf(stderr, "could do fstat() on fd ??\n");
|
||
|
|
close(sg_fd);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
if (S_ISBLK(a_st.st_mode))
|
||
|
|
block_dev = 1;
|
||
|
|
/* Don't worry, being very careful not to write to a none-sg file ... */
|
||
|
|
if (block_dev || (ioctl(sg_fd, SG_GET_TIMEOUT, 0) < 0)) {
|
||
|
|
/* perror("ioctl on generic device, error"); */
|
||
|
|
printf("sg_test_rwbuf: not a sg device, or wrong driver\n");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
if (find_out_about_buffer(sg_fd, &buf_capacity, file_name))
|
||
|
|
return 1;
|
||
|
|
|
||
|
|
cmpbuf = malloc(buf_capacity);
|
||
|
|
if (write_buffer(sg_fd, buf_capacity))
|
||
|
|
return 3;
|
||
|
|
res = read_buffer(sg_fd, buf_capacity);
|
||
|
|
if (res)
|
||
|
|
return (res + 4);
|
||
|
|
|
||
|
|
res = close(sg_fd);
|
||
|
|
if (res < 0) {
|
||
|
|
perror("sg_test_rwbuf: close error");
|
||
|
|
return 6;
|
||
|
|
}
|
||
|
|
printf("Success\n");
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
int do_scsi_test_unit_ready(char *device)
|
||
|
|
{
|
||
|
|
int sg_fd, k;
|
||
|
|
unsigned char turCmdBlk[TUR_CMD_LEN] = { 0x00, 0, 0, 0, 0, 0 };
|
||
|
|
sg_io_hdr_t io_hdr;
|
||
|
|
char *file_name = device;
|
||
|
|
char ebuff[EBUFF_SZ];
|
||
|
|
unsigned char sense_buffer[32];
|
||
|
|
int num_turs = 10240;
|
||
|
|
int num_errs = 0;
|
||
|
|
int do_time = 1;
|
||
|
|
struct timeval start_tm, end_tm;
|
||
|
|
|
||
|
|
print_msg(TEST_BREAK, __FUNCTION__);
|
||
|
|
|
||
|
|
if ((sg_fd = open(file_name, O_RDONLY)) < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ,
|
||
|
|
"sg_turs: error opening file: %s", file_name);
|
||
|
|
perror(ebuff);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
/* Just to be safe, check we have a new sg driver by trying an ioctl */
|
||
|
|
if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) {
|
||
|
|
printf
|
||
|
|
("sg_turs: %s isn't an sg device (or the sg driver is old)\n",
|
||
|
|
file_name);
|
||
|
|
close(sg_fd);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
/* Prepare TEST UNIT READY command */
|
||
|
|
memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
|
||
|
|
io_hdr.interface_id = 'S';
|
||
|
|
io_hdr.cmd_len = sizeof(turCmdBlk);
|
||
|
|
io_hdr.mx_sb_len = sizeof(sense_buffer);
|
||
|
|
io_hdr.dxfer_direction = SG_DXFER_NONE;
|
||
|
|
io_hdr.cmdp = turCmdBlk;
|
||
|
|
io_hdr.sbp = sense_buffer;
|
||
|
|
io_hdr.timeout = 20000; /* 20000 millisecs == 20 seconds */
|
||
|
|
if (do_time) {
|
||
|
|
start_tm.tv_sec = 0;
|
||
|
|
start_tm.tv_usec = 0;
|
||
|
|
gettimeofday(&start_tm, NULL);
|
||
|
|
}
|
||
|
|
for (k = 0; k < num_turs; ++k) {
|
||
|
|
io_hdr.pack_id = k;
|
||
|
|
if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
|
||
|
|
perror("sg_turs: Test Unit Ready SG_IO ioctl error");
|
||
|
|
close(sg_fd);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
if (io_hdr.info & SG_INFO_OK_MASK) {
|
||
|
|
++num_errs;
|
||
|
|
if (1 == num_turs) { /* then print out the error message */
|
||
|
|
if (SG_ERR_CAT_CLEAN !=
|
||
|
|
sg_err_category3(&io_hdr))
|
||
|
|
sg_chk_n_print3("tur", &io_hdr);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if ((do_time) && (start_tm.tv_sec || start_tm.tv_usec)) {
|
||
|
|
struct timeval res_tm;
|
||
|
|
double a, b;
|
||
|
|
|
||
|
|
gettimeofday(&end_tm, NULL);
|
||
|
|
res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec;
|
||
|
|
res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec;
|
||
|
|
if (res_tm.tv_usec < 0) {
|
||
|
|
--res_tm.tv_sec;
|
||
|
|
res_tm.tv_usec += 1000000;
|
||
|
|
}
|
||
|
|
a = res_tm.tv_sec;
|
||
|
|
a += (0.000001 * res_tm.tv_usec);
|
||
|
|
b = (double)num_turs;
|
||
|
|
printf("time to perform commands was %d.%06d secs",
|
||
|
|
(int)res_tm.tv_sec, (int)res_tm.tv_usec);
|
||
|
|
if (a > 0.00001)
|
||
|
|
printf("; %.2f operations/sec\n", b / a);
|
||
|
|
else
|
||
|
|
printf("\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
printf("Completed %d Test Unit Ready commands with %d errors\n",
|
||
|
|
num_turs, num_errs);
|
||
|
|
close(sg_fd);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Returns 0 -> ok, 1 -> err, 2 -> recovered error */
|
||
|
|
static int do_sg_io(int sg_fd, unsigned char *buff)
|
||
|
|
{
|
||
|
|
/* N.B. Assuming buff contains pointer 'buffer' or 'buffer1' */
|
||
|
|
struct sg_header *sghp = (struct sg_header *)(buff - OFF);
|
||
|
|
int res;
|
||
|
|
|
||
|
|
sghp->pack_len = 0;
|
||
|
|
sghp->reply_len = SG_HSZ + *(((int *)buff) + 1);
|
||
|
|
sghp->pack_id = 0;
|
||
|
|
sghp->twelve_byte = 0;
|
||
|
|
sghp->other_flags = 0;
|
||
|
|
#ifndef SG_GET_RESERVED_SIZE
|
||
|
|
sghp->sense_buffer[0] = 0;
|
||
|
|
#endif
|
||
|
|
#if 0
|
||
|
|
sg_print_command(buff + 8);
|
||
|
|
printf(" write_len=%d, read_len=%d\n",
|
||
|
|
SG_HSZ + sg_get_command_size(buff[8]) + *((int *)buff),
|
||
|
|
sghp->reply_len);
|
||
|
|
#endif
|
||
|
|
res = write(sg_fd, (const void *)sghp,
|
||
|
|
SG_HSZ + sg_get_command_size(buff[8]) + *((int *)buff));
|
||
|
|
if (res < 0) {
|
||
|
|
#ifdef SG_IO_DEBUG
|
||
|
|
perror("write to sg failed");
|
||
|
|
#endif
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
res = read(sg_fd, (void *)sghp, sghp->reply_len);
|
||
|
|
if (res < 0) {
|
||
|
|
#ifdef SG_IO_DEBUG
|
||
|
|
perror("read from sg failed");
|
||
|
|
#endif
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
#ifdef SG_GET_RESERVED_SIZE
|
||
|
|
res = sg_err_category(sghp->target_status, sghp->host_status,
|
||
|
|
sghp->driver_status, sghp->sense_buffer,
|
||
|
|
SG_MAX_SENSE);
|
||
|
|
switch (res) {
|
||
|
|
case SG_ERR_CAT_CLEAN:
|
||
|
|
return 0;
|
||
|
|
case SG_ERR_CAT_RECOVERED:
|
||
|
|
return 2;
|
||
|
|
default:
|
||
|
|
#ifdef SG_IO_DEBUG
|
||
|
|
sg_chk_n_print("read from sg", sghp->target_status,
|
||
|
|
sghp->host_status, sghp->driver_status,
|
||
|
|
sghp->sense_buffer, SG_MAX_SENSE);
|
||
|
|
#endif
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
#else
|
||
|
|
if (0 != sghp->sense_buffer[0]) {
|
||
|
|
#ifdef SG_IO_DEBUG
|
||
|
|
int k;
|
||
|
|
printf("read from sg, sense buffer (in hex):\n ");
|
||
|
|
for (k = 0; k < 16; ++k)
|
||
|
|
printf("%02x ", (int)sghp->sense_buffer[k]);
|
||
|
|
printf("\n");
|
||
|
|
#endif
|
||
|
|
return 1;
|
||
|
|
} else if (0 != sghp->result) {
|
||
|
|
#ifdef SG_IO_DEBUG
|
||
|
|
printf("read from sg, bad result=%d\n", sghp->result);
|
||
|
|
#endif
|
||
|
|
return 1;
|
||
|
|
} else
|
||
|
|
return 0;
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
static char *get_page_name(int pageno)
|
||
|
|
{
|
||
|
|
if ((pageno <= 0) || (pageno >= MAX_PAGENO) || (!page_names[pageno]))
|
||
|
|
return "Mode";
|
||
|
|
return page_names[pageno];
|
||
|
|
}
|
||
|
|
|
||
|
|
static int getnbyte(unsigned char *pnt, int nbyte)
|
||
|
|
{
|
||
|
|
unsigned int result;
|
||
|
|
int i;
|
||
|
|
result = 0;
|
||
|
|
for (i = 0; i < nbyte; i++)
|
||
|
|
result = (result << 8) | (pnt[i] & 0xff);
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void bitfield(unsigned char *pageaddr, char *text, int mask, int shift)
|
||
|
|
{
|
||
|
|
printf("%-35s%d\n", text, (*pageaddr >> shift) & mask);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void notbitfield(unsigned char *pageaddr, char *text, int mask,
|
||
|
|
int shift)
|
||
|
|
{
|
||
|
|
printf("%-35s%d\n", text, !((*pageaddr >> shift) & mask));
|
||
|
|
}
|
||
|
|
|
||
|
|
static void intfield(unsigned char *pageaddr, int nbytes, char *text)
|
||
|
|
{
|
||
|
|
printf("%-35s%d\n", text, getnbyte(pageaddr, nbytes));
|
||
|
|
}
|
||
|
|
|
||
|
|
static void hexfield(unsigned char *pageaddr, int nbytes, char *text)
|
||
|
|
{
|
||
|
|
printf("%-35s0x%x\n", text, getnbyte(pageaddr, nbytes));
|
||
|
|
}
|
||
|
|
|
||
|
|
static void hexdatafield(unsigned char *pageaddr, int nbytes, char *text)
|
||
|
|
{
|
||
|
|
printf("%-35s0x", text);
|
||
|
|
while (nbytes-- > 0)
|
||
|
|
printf("%02x", *pageaddr++);
|
||
|
|
putchar('\n');
|
||
|
|
}
|
||
|
|
|
||
|
|
static int get_mode_page(int page, int page_code)
|
||
|
|
{
|
||
|
|
int status, quiet;
|
||
|
|
unsigned char *cmd;
|
||
|
|
|
||
|
|
memset(buffer, 0, SIZEOF_BUFFER);
|
||
|
|
|
||
|
|
quiet = page_code & ~3;
|
||
|
|
page_code &= 3;
|
||
|
|
|
||
|
|
*((int *)buffer) = 0; /* length of input data */
|
||
|
|
*(((int *)buffer) + 1) = 0xff; /* length of output data */
|
||
|
|
|
||
|
|
cmd = (unsigned char *)(((int *)buffer) + 2);
|
||
|
|
|
||
|
|
cmd[0] = MODE_SENSE; /* MODE SENSE (6) */
|
||
|
|
cmd[1] = 0x00; /* lun = 0, inhibitting BD makes this fail
|
||
|
|
for me */
|
||
|
|
cmd[2] = (page_code << 6) | page;
|
||
|
|
cmd[3] = 0x00; /* (reserved) */
|
||
|
|
cmd[4] = (unsigned char)0xff; /* allocation length */
|
||
|
|
cmd[5] = 0x00; /* control */
|
||
|
|
|
||
|
|
status = do_sg_io(glob_fd, buffer);
|
||
|
|
if (status && (!quiet))
|
||
|
|
fprintf(stdout, ">>> Unable to read %s Page %02xh\n",
|
||
|
|
get_page_name(page), page);
|
||
|
|
//dump (buffer+2, 46);
|
||
|
|
return status;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Same as above, but this time with MODE_SENSE_10 */
|
||
|
|
static int get_mode_page10(int page, int page_code)
|
||
|
|
{
|
||
|
|
int status, quiet;
|
||
|
|
unsigned char *cmd;
|
||
|
|
|
||
|
|
memset(buffer, 0, SIZEOF_BUFFER);
|
||
|
|
|
||
|
|
quiet = page_code & ~3;
|
||
|
|
page_code &= 3;
|
||
|
|
|
||
|
|
*((int *)buffer) = 0; /* length of input data */
|
||
|
|
*(((int *)buffer) + 1) = 0xffff; /* length of output buffer */
|
||
|
|
|
||
|
|
cmd = (unsigned char *)(((int *)buffer) + 2);
|
||
|
|
|
||
|
|
cmd[0] = MODE_SENSE_10; /* MODE SENSE (10) */
|
||
|
|
cmd[1] = 0x00; /* lun = 0, inhibitting BD makes this fail
|
||
|
|
for me */
|
||
|
|
cmd[2] = (page_code << 6) | page;
|
||
|
|
cmd[3] = 0x00; /* (reserved) */
|
||
|
|
cmd[4] = 0x00; /* (reserved) */
|
||
|
|
cmd[5] = 0x00; /* (reserved) */
|
||
|
|
cmd[6] = 0x00; /* (reserved) */
|
||
|
|
cmd[7] = 0xff; /* allocation length hi */
|
||
|
|
cmd[8] = 0xff; /* allocation length lo */
|
||
|
|
cmd[9] = 0x00; /* control */
|
||
|
|
|
||
|
|
status = do_sg_io(glob_fd, buffer);
|
||
|
|
if (status && (!quiet))
|
||
|
|
fprintf(stdout,
|
||
|
|
">>> Unable to read %s Page %02xh with MODESENSE(10)\n",
|
||
|
|
get_page_name(page), page);
|
||
|
|
return status;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Contents should point to the mode parameter header that we obtained
|
||
|
|
in a prior read operation. This way we do not have to work out the
|
||
|
|
format of the beast */
|
||
|
|
|
||
|
|
static int read_geometry(int page_code)
|
||
|
|
{
|
||
|
|
int status;
|
||
|
|
int bdlen;
|
||
|
|
unsigned char *pagestart;
|
||
|
|
|
||
|
|
SETUP_MODE_PAGE(4, 9);
|
||
|
|
|
||
|
|
printf("Data from Rigid Disk Drive Geometry Page\n");
|
||
|
|
printf("----------------------------------------\n");
|
||
|
|
intfield(pagestart + 2, 3, "Number of cylinders");
|
||
|
|
intfield(pagestart + 5, 1, "Number of heads");
|
||
|
|
intfield(pagestart + 6, 3, "Starting write precomp");
|
||
|
|
intfield(pagestart + 9, 3, "Starting reduced current");
|
||
|
|
intfield(pagestart + 12, 2, "Drive step rate");
|
||
|
|
intfield(pagestart + 14, 3, "Landing Zone Cylinder");
|
||
|
|
bitfield(pagestart + 17, "RPL", 3, 0);
|
||
|
|
intfield(pagestart + 18, 1, "Rotational Offset");
|
||
|
|
intfield(pagestart + 20, 2, "Rotational Rate");
|
||
|
|
printf("\n");
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
static int read_disconnect_reconnect_data(int page_code)
|
||
|
|
{
|
||
|
|
int status;
|
||
|
|
int bdlen;
|
||
|
|
unsigned char *pagestart;
|
||
|
|
|
||
|
|
SETUP_MODE_PAGE(2, 7);
|
||
|
|
|
||
|
|
printf("Data from Disconnect-Reconnect Page\n");
|
||
|
|
printf("-----------------------------------\n");
|
||
|
|
intfield(pagestart + 2, 1, "Buffer full ratio");
|
||
|
|
intfield(pagestart + 3, 1, "Buffer empty ratio");
|
||
|
|
intfield(pagestart + 4, 2, "Bus Inactivity Limit");
|
||
|
|
intfield(pagestart + 6, 2, "Disconnect Time Limit");
|
||
|
|
intfield(pagestart + 8, 2, "Connect Time Limit");
|
||
|
|
intfield(pagestart + 10, 2, "Maximum Burst Size");
|
||
|
|
hexfield(pagestart + 12, 1, "DTDC");
|
||
|
|
printf("\n");
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
static int read_control_page(int page_code)
|
||
|
|
{
|
||
|
|
int status;
|
||
|
|
int bdlen;
|
||
|
|
unsigned char *pagestart;
|
||
|
|
|
||
|
|
SETUP_MODE_PAGE(10, 9);
|
||
|
|
|
||
|
|
printf("Data from Control Page\n");
|
||
|
|
printf("----------------------\n");
|
||
|
|
bitfield(pagestart + 2, "RLEC", 1, 0);
|
||
|
|
bitfield(pagestart + 3, "QErr", 1, 1);
|
||
|
|
bitfield(pagestart + 3, "DQue", 1, 0);
|
||
|
|
bitfield(pagestart + 4, "EECA", 1, 7);
|
||
|
|
bitfield(pagestart + 4, "RAENP", 1, 2);
|
||
|
|
bitfield(pagestart + 4, "UUAENP", 1, 1);
|
||
|
|
bitfield(pagestart + 4, "EAENP", 1, 0);
|
||
|
|
bitfield(pagestart + 3, "Queue Algorithm Modifier", 0xf, 4);
|
||
|
|
intfield(pagestart + 6, 2, "Ready AEN Holdoff Period");
|
||
|
|
printf("\n");
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
static int error_recovery_page(int page_code)
|
||
|
|
{
|
||
|
|
int status;
|
||
|
|
int bdlen;
|
||
|
|
unsigned char *pagestart;
|
||
|
|
|
||
|
|
SETUP_MODE_PAGE(1, 14);
|
||
|
|
printf("Data from Error Recovery Page\n");
|
||
|
|
printf("-----------------------------\n");
|
||
|
|
bitfield(pagestart + 2, "AWRE", 1, 7);
|
||
|
|
bitfield(pagestart + 2, "ARRE", 1, 6);
|
||
|
|
bitfield(pagestart + 2, "TB", 1, 5);
|
||
|
|
bitfield(pagestart + 2, "RC", 1, 4);
|
||
|
|
bitfield(pagestart + 2, "EER", 1, 3);
|
||
|
|
bitfield(pagestart + 2, "PER", 1, 2);
|
||
|
|
bitfield(pagestart + 2, "DTE", 1, 1);
|
||
|
|
bitfield(pagestart + 2, "DCR", 1, 0);
|
||
|
|
intfield(pagestart + 3, 1, "Read Retry Count");
|
||
|
|
intfield(pagestart + 4, 1, "Correction Span");
|
||
|
|
intfield(pagestart + 5, 1, "Head Offset Count");
|
||
|
|
intfield(pagestart + 6, 1, "Data Strobe Offset Count");
|
||
|
|
intfield(pagestart + 8, 1, "Write Retry Count");
|
||
|
|
intfield(pagestart + 10, 2, "Recovery Time Limit");
|
||
|
|
printf("\n");
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int notch_parameters_page(int page_code)
|
||
|
|
{
|
||
|
|
int status;
|
||
|
|
int bdlen;
|
||
|
|
unsigned char *pagestart;
|
||
|
|
|
||
|
|
SETUP_MODE_PAGE(0xc, 7);
|
||
|
|
|
||
|
|
printf("Data from Notch Parameters Page\n");
|
||
|
|
printf("-------------------------------\n");
|
||
|
|
bitfield(pagestart + 2, "Notched Drive", 1, 7);
|
||
|
|
bitfield(pagestart + 2, "Logical or Physical Notch", 1, 6);
|
||
|
|
intfield(pagestart + 4, 2, "Max # of notches");
|
||
|
|
intfield(pagestart + 6, 2, "Active Notch");
|
||
|
|
if (pagestart[2] & 0x40) {
|
||
|
|
intfield(pagestart + 8, 4, "Starting Boundary");
|
||
|
|
intfield(pagestart + 12, 4, "Ending Boundary");
|
||
|
|
} else { /* Hex is more meaningful for physical notches */
|
||
|
|
hexfield(pagestart + 8, 4, "Starting Boundary");
|
||
|
|
hexfield(pagestart + 12, 4, "Ending Boundary");
|
||
|
|
}
|
||
|
|
|
||
|
|
printf("0x%8.8x%8.8x", getnbyte(pagestart + 16, 4),
|
||
|
|
getnbyte(pagestart + 20, 4));
|
||
|
|
|
||
|
|
printf("\n");
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static char *formatname(int format)
|
||
|
|
{
|
||
|
|
switch (format) {
|
||
|
|
case 0x0:
|
||
|
|
return "logical blocks";
|
||
|
|
case 0x4:
|
||
|
|
return "bytes from index [Cyl:Head:Off]\n"
|
||
|
|
"Offset -1 marks whole track as bad.\n";
|
||
|
|
case 0x5:
|
||
|
|
return "physical blocks [Cyl:Head:Sect]\n"
|
||
|
|
"Sector -1 marks whole track as bad.\n";
|
||
|
|
}
|
||
|
|
return "Weird, unknown format";
|
||
|
|
}
|
||
|
|
|
||
|
|
static int read_defect_list(int page_code)
|
||
|
|
{
|
||
|
|
int status = 0, i, len, reallen, table, k;
|
||
|
|
unsigned char *cmd, *df = 0;
|
||
|
|
int trunc;
|
||
|
|
|
||
|
|
printf("Data from Defect Lists\n" "----------------------\n");
|
||
|
|
for (table = 0; table < 2; table++) {
|
||
|
|
memset(buffer, 0, SIZEOF_BUFFER);
|
||
|
|
trunc = 0;
|
||
|
|
|
||
|
|
*((int *)buffer) = 0; /* length of input data */
|
||
|
|
*(((int *)buffer) + 1) = 4; /* length of output buffer */
|
||
|
|
|
||
|
|
cmd = (unsigned char *)(((int *)buffer) + 2);
|
||
|
|
|
||
|
|
cmd[0] = 0x37; /* READ DEFECT DATA */
|
||
|
|
cmd[1] = 0x00; /* lun=0 */
|
||
|
|
cmd[2] = (table ? 0x08 : 0x10) | defectformat; /* List, Format */
|
||
|
|
cmd[3] = 0x00; /* (reserved) */
|
||
|
|
cmd[4] = 0x00; /* (reserved) */
|
||
|
|
cmd[5] = 0x00; /* (reserved) */
|
||
|
|
cmd[6] = 0x00; /* (reserved) */
|
||
|
|
cmd[7] = 0x00; /* Alloc len */
|
||
|
|
cmd[8] = 0x04; /* Alloc len */
|
||
|
|
cmd[9] = 0x00; /* control */
|
||
|
|
|
||
|
|
i = do_sg_io(glob_fd, buffer);
|
||
|
|
if (2 == i)
|
||
|
|
i = 0; /* Recovered error, probably returned a different
|
||
|
|
format */
|
||
|
|
if (i) {
|
||
|
|
fprintf(stdout, ">>> Unable to read %s defect data.\n",
|
||
|
|
(table ? "grown" : "manufacturer"));
|
||
|
|
status |= i;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
len = (buffer[10] << 8) | buffer[11];
|
||
|
|
reallen = len;
|
||
|
|
if (len > 0) {
|
||
|
|
if (len >= 0xfff8) {
|
||
|
|
len = SIZEOF_BUFFER - 8;
|
||
|
|
k = len + 8; /* length of defect list */
|
||
|
|
*((int *)buffer) = 0; /* length of input data */
|
||
|
|
*(((int *)buffer) + 1) = k; /* length of output buffer */
|
||
|
|
((struct sg_header *)buffer)->twelve_byte = 1;
|
||
|
|
cmd[0] = 0xB7; /* READ DEFECT DATA */
|
||
|
|
cmd[1] = (table ? 0x08 : 0x10) | defectformat; /* List, Format */
|
||
|
|
cmd[2] = 0x00; /* (reserved) */
|
||
|
|
cmd[3] = 0x00; /* (reserved) */
|
||
|
|
cmd[4] = 0x00; /* (reserved) */
|
||
|
|
cmd[5] = 0x00; /* (reserved) */
|
||
|
|
cmd[6] = 0x00; /* Alloc len */
|
||
|
|
cmd[7] = (k >> 16); /* Alloc len */
|
||
|
|
cmd[8] = (k >> 8); /* Alloc len */
|
||
|
|
cmd[9] = (k & 0xff); /* Alloc len */
|
||
|
|
cmd[10] = 0x00; /* reserved */
|
||
|
|
cmd[11] = 0x00; /* control */
|
||
|
|
i = do_sg_io(glob_fd, buffer);
|
||
|
|
if (i == 2)
|
||
|
|
i = 0;
|
||
|
|
if (i)
|
||
|
|
goto trytenbyte;
|
||
|
|
reallen =
|
||
|
|
(buffer[12] << 24 | buffer[13] << 16 |
|
||
|
|
buffer[14] << 8 | buffer[15]);
|
||
|
|
len = reallen;
|
||
|
|
if (len > SIZEOF_BUFFER - 8) {
|
||
|
|
len = SIZEOF_BUFFER - 8;
|
||
|
|
trunc = 1;
|
||
|
|
}
|
||
|
|
df = (unsigned char *)(buffer + 16);
|
||
|
|
} else {
|
||
|
|
trytenbyte:
|
||
|
|
if (len > 0xfff8) {
|
||
|
|
len = 0xfff8;
|
||
|
|
trunc = 1;
|
||
|
|
}
|
||
|
|
k = len + 4; /* length of defect list */
|
||
|
|
*((int *)buffer) = 0; /* length of input data */
|
||
|
|
*(((int *)buffer) + 1) = k; /* length of output buffer */
|
||
|
|
cmd[0] = 0x37; /* READ DEFECT DATA */
|
||
|
|
cmd[1] = 0x00; /* lun=0 */
|
||
|
|
cmd[2] = (table ? 0x08 : 0x10) | defectformat; /* List, Format */
|
||
|
|
cmd[3] = 0x00; /* (reserved) */
|
||
|
|
cmd[4] = 0x00; /* (reserved) */
|
||
|
|
cmd[5] = 0x00; /* (reserved) */
|
||
|
|
cmd[6] = 0x00; /* (reserved) */
|
||
|
|
cmd[7] = (k >> 8); /* Alloc len */
|
||
|
|
cmd[8] = (k & 0xff); /* Alloc len */
|
||
|
|
cmd[9] = 0x00; /* control */
|
||
|
|
i = do_sg_io(glob_fd, buffer);
|
||
|
|
df = (unsigned char *)(buffer + 12);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (2 == i)
|
||
|
|
i = 0; /* Recovered error, probably returned a different
|
||
|
|
format */
|
||
|
|
if (i) {
|
||
|
|
fprintf(stdout, ">>> Unable to read %s defect data.\n",
|
||
|
|
(table ? "grown" : "manufacturer"));
|
||
|
|
status |= i;
|
||
|
|
continue;
|
||
|
|
} else {
|
||
|
|
if (table && !status)
|
||
|
|
printf("\n");
|
||
|
|
printf("%d entries (%d bytes) in %s table.\n"
|
||
|
|
"Format (%x) is: %s\n",
|
||
|
|
reallen / ((buffer[9] & 7) ? 8 : 4), reallen,
|
||
|
|
(table ? "grown" : "manufacturer"),
|
||
|
|
buffer[9] & 7, formatname(buffer[9] & 7));
|
||
|
|
i = 0;
|
||
|
|
if ((buffer[9] & 7) == 4) {
|
||
|
|
while (len > 0) {
|
||
|
|
snprintf((char *)buffer, 40,
|
||
|
|
"%6d:%3u:%8d", getnbyte(df, 3),
|
||
|
|
df[3], getnbyte(df + 4, 4));
|
||
|
|
printf("%19s", (char *)buffer);
|
||
|
|
len -= 8;
|
||
|
|
df += 8;
|
||
|
|
i++;
|
||
|
|
if (i >= 4) {
|
||
|
|
printf("\n");
|
||
|
|
i = 0;
|
||
|
|
} else
|
||
|
|
printf("|");
|
||
|
|
}
|
||
|
|
} else if ((buffer[9] & 7) == 5) {
|
||
|
|
while (len > 0) {
|
||
|
|
snprintf((char *)buffer, 40,
|
||
|
|
"%6d:%2u:%5d", getnbyte(df, 3),
|
||
|
|
df[3], getnbyte(df + 4, 4));
|
||
|
|
printf("%15s", (char *)buffer);
|
||
|
|
len -= 8;
|
||
|
|
df += 8;
|
||
|
|
i++;
|
||
|
|
if (i >= 5) {
|
||
|
|
printf("\n");
|
||
|
|
i = 0;
|
||
|
|
} else
|
||
|
|
printf("|");
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
while (len > 0) {
|
||
|
|
printf("%10d", getnbyte(df, 4));
|
||
|
|
len -= 4;
|
||
|
|
df += 4;
|
||
|
|
i++;
|
||
|
|
if (i >= 7) {
|
||
|
|
printf("\n");
|
||
|
|
i = 0;
|
||
|
|
} else
|
||
|
|
printf("|");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (i)
|
||
|
|
printf("\n");
|
||
|
|
}
|
||
|
|
if (trunc)
|
||
|
|
printf("[truncated]\n");
|
||
|
|
}
|
||
|
|
printf("\n");
|
||
|
|
return status;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int read_cache(int page_code)
|
||
|
|
{
|
||
|
|
int status;
|
||
|
|
int bdlen;
|
||
|
|
unsigned char *pagestart;
|
||
|
|
|
||
|
|
SETUP_MODE_PAGE(8, 9);
|
||
|
|
|
||
|
|
printf("Data from Caching Page\n");
|
||
|
|
printf("----------------------\n");
|
||
|
|
bitfield(pagestart + 2, "Write Cache", 1, 2);
|
||
|
|
notbitfield(pagestart + 2, "Read Cache", 1, 0);
|
||
|
|
bitfield(pagestart + 2, "Prefetch units", 1, 1);
|
||
|
|
bitfield(pagestart + 3, "Demand Read Retention Priority", 0xf, 4);
|
||
|
|
bitfield(pagestart + 3, "Demand Write Retention Priority", 0xf, 0);
|
||
|
|
intfield(pagestart + 4, 2, "Disable Pre-fetch Transfer Length");
|
||
|
|
intfield(pagestart + 6, 2, "Minimum Pre-fetch");
|
||
|
|
intfield(pagestart + 8, 2, "Maximum Pre-fetch");
|
||
|
|
intfield(pagestart + 10, 2, "Maximum Pre-fetch Ceiling");
|
||
|
|
printf("\n");
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int read_format_info(int page_code)
|
||
|
|
{
|
||
|
|
int status;
|
||
|
|
int bdlen;
|
||
|
|
unsigned char *pagestart;
|
||
|
|
|
||
|
|
SETUP_MODE_PAGE(3, 13);
|
||
|
|
|
||
|
|
printf("Data from Format Device Page\n");
|
||
|
|
printf("----------------------------\n");
|
||
|
|
bitfield(pagestart + 20, "Removable Medium", 1, 5);
|
||
|
|
bitfield(pagestart + 20, "Supports Hard Sectoring", 1, 6);
|
||
|
|
bitfield(pagestart + 20, "Supports Soft Sectoring", 1, 7);
|
||
|
|
bitfield(pagestart + 20, "Addresses assigned by surface", 1, 4);
|
||
|
|
intfield(pagestart + 2, 2, "Tracks per Zone");
|
||
|
|
intfield(pagestart + 4, 2, "Alternate sectors per zone");
|
||
|
|
intfield(pagestart + 6, 2, "Alternate tracks per zone");
|
||
|
|
intfield(pagestart + 8, 2, "Alternate tracks per lun");
|
||
|
|
intfield(pagestart + 10, 2, "Sectors per track");
|
||
|
|
intfield(pagestart + 12, 2, "Bytes per sector");
|
||
|
|
intfield(pagestart + 14, 2, "Interleave");
|
||
|
|
intfield(pagestart + 16, 2, "Track skew factor");
|
||
|
|
intfield(pagestart + 18, 2, "Cylinder skew factor");
|
||
|
|
printf("\n");
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
static int verify_error_recovery(int page_code)
|
||
|
|
{
|
||
|
|
int status;
|
||
|
|
int bdlen;
|
||
|
|
unsigned char *pagestart;
|
||
|
|
|
||
|
|
SETUP_MODE_PAGE(7, 7);
|
||
|
|
|
||
|
|
printf("Data from Verify Error Recovery Page\n");
|
||
|
|
printf("------------------------------------\n");
|
||
|
|
bitfield(pagestart + 2, "EER", 1, 3);
|
||
|
|
bitfield(pagestart + 2, "PER", 1, 2);
|
||
|
|
bitfield(pagestart + 2, "DTE", 1, 1);
|
||
|
|
bitfield(pagestart + 2, "DCR", 1, 0);
|
||
|
|
intfield(pagestart + 3, 1, "Verify Retry Count");
|
||
|
|
intfield(pagestart + 4, 1, "Verify Correction Span (bits)");
|
||
|
|
intfield(pagestart + 10, 2, "Verify Recovery Time Limit (ms)");
|
||
|
|
|
||
|
|
printf("\n");
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int peripheral_device_page(int page_code)
|
||
|
|
{
|
||
|
|
static char *idents[] = {
|
||
|
|
"X3.131: Small Computer System Interface",
|
||
|
|
"X3.91M-1987: Storage Module Interface",
|
||
|
|
"X3.170: Enhanced Small Device Interface",
|
||
|
|
"X3.130-1986; X3T9.3/87-002: IPI-2",
|
||
|
|
"X3.132-1987; X3.147-1988: IPI-3"
|
||
|
|
};
|
||
|
|
int status;
|
||
|
|
int bdlen;
|
||
|
|
unsigned ident;
|
||
|
|
unsigned char *pagestart;
|
||
|
|
char *name;
|
||
|
|
|
||
|
|
SETUP_MODE_PAGE(9, 2);
|
||
|
|
|
||
|
|
printf("Data from Peripheral Device Page\n");
|
||
|
|
printf("--------------------------------\n");
|
||
|
|
|
||
|
|
ident = getnbyte(pagestart + 2, 2);
|
||
|
|
if (ident < (sizeof(idents) / sizeof(char *)))
|
||
|
|
name = idents[ident];
|
||
|
|
else if (ident < 0x8000)
|
||
|
|
name = "Reserved";
|
||
|
|
else
|
||
|
|
name = "Vendor Specific";
|
||
|
|
|
||
|
|
bdlen = pagestart[1] - 6;
|
||
|
|
if (bdlen < 0)
|
||
|
|
bdlen = 0;
|
||
|
|
else
|
||
|
|
SETUP_MODE_PAGE(9, 2);
|
||
|
|
|
||
|
|
hexfield(pagestart + 2, 2, "Interface Identifier");
|
||
|
|
for (ident = 0; ident < 35; ident++)
|
||
|
|
putchar(' ');
|
||
|
|
puts(name);
|
||
|
|
|
||
|
|
hexdatafield(pagestart + 8, bdlen, "Vendor Specific Data");
|
||
|
|
|
||
|
|
printf("\n");
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* end */
|
||
|
|
|
||
|
|
static int do_user_page(int page_code, int page_no)
|
||
|
|
{
|
||
|
|
int status;
|
||
|
|
int bdlen;
|
||
|
|
int i;
|
||
|
|
//unsigned ident;
|
||
|
|
unsigned char *pagestart;
|
||
|
|
char *name;
|
||
|
|
|
||
|
|
SETUP_MODE_PAGE(page_no, 0);
|
||
|
|
//printf ("Page 0x%02x len: %i\n", page_code, pagestart[1]);
|
||
|
|
|
||
|
|
name = "Vendor specific";
|
||
|
|
for (i = 2; i < pagestart[1] + 2; i++) {
|
||
|
|
char nm[8];
|
||
|
|
snprintf(nm, 8, "%02x", i);
|
||
|
|
hexdatafield(pagestart + i, 1, nm);
|
||
|
|
}
|
||
|
|
|
||
|
|
printf("\n");
|
||
|
|
puts(name);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* end */
|
||
|
|
|
||
|
|
static int do_scsi_info_inquiry(int page_code)
|
||
|
|
{
|
||
|
|
int status, i, x_interface = 0;
|
||
|
|
unsigned char *cmd;
|
||
|
|
unsigned char *pagestart;
|
||
|
|
unsigned char tmp;
|
||
|
|
|
||
|
|
for (i = 0; i < 1024; i++) {
|
||
|
|
buffer[i] = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
*((int *)buffer) = 0; /* length of input data */
|
||
|
|
*(((int *)buffer) + 1) = 36; /* length of output buffer */
|
||
|
|
|
||
|
|
cmd = (unsigned char *)(((int *)buffer) + 2);
|
||
|
|
|
||
|
|
cmd[0] = 0x12; /* INQUIRY */
|
||
|
|
cmd[1] = 0x00; /* lun=0, evpd=0 */
|
||
|
|
cmd[2] = 0x00; /* page code = 0 */
|
||
|
|
cmd[3] = 0x00; /* (reserved) */
|
||
|
|
cmd[4] = 0x24; /* allocation length */
|
||
|
|
cmd[5] = 0x00; /* control */
|
||
|
|
|
||
|
|
status = do_sg_io(glob_fd, buffer);
|
||
|
|
if (status) {
|
||
|
|
printf("Error doing INQUIRY (1)");
|
||
|
|
return status;
|
||
|
|
}
|
||
|
|
|
||
|
|
pagestart = buffer + 8;
|
||
|
|
|
||
|
|
printf("Inquiry command\n");
|
||
|
|
printf("---------------\n");
|
||
|
|
bitfield(pagestart + 7, "Relative Address", 1, 7);
|
||
|
|
bitfield(pagestart + 7, "Wide bus 32", 1, 6);
|
||
|
|
bitfield(pagestart + 7, "Wide bus 16", 1, 5);
|
||
|
|
bitfield(pagestart + 7, "Synchronous neg.", 1, 4);
|
||
|
|
bitfield(pagestart + 7, "Linked Commands", 1, 3);
|
||
|
|
bitfield(pagestart + 7, "Command Queueing", 1, 1);
|
||
|
|
bitfield(pagestart + 7, "SftRe", 1, 0);
|
||
|
|
bitfield(pagestart + 0, "Device Type", 0x1f, 0);
|
||
|
|
bitfield(pagestart + 0, "Peripheral Qualifier", 0x7, 5);
|
||
|
|
bitfield(pagestart + 1, "Removable?", 1, 7);
|
||
|
|
bitfield(pagestart + 1, "Device Type Modifier", 0x7f, 0);
|
||
|
|
bitfield(pagestart + 2, "ISO Version", 3, 6);
|
||
|
|
bitfield(pagestart + 2, "ECMA Version", 7, 3);
|
||
|
|
bitfield(pagestart + 2, "ANSI Version", 7, 0);
|
||
|
|
bitfield(pagestart + 3, "AENC", 1, 7);
|
||
|
|
bitfield(pagestart + 3, "TrmIOP", 1, 6);
|
||
|
|
bitfield(pagestart + 3, "Response Data Format", 0xf, 0);
|
||
|
|
tmp = pagestart[16];
|
||
|
|
pagestart[16] = 0;
|
||
|
|
printf("%s%s\n", (!x_interface ? "Vendor: " : ""),
|
||
|
|
pagestart + 8);
|
||
|
|
pagestart[16] = tmp;
|
||
|
|
|
||
|
|
tmp = pagestart[32];
|
||
|
|
pagestart[32] = 0;
|
||
|
|
printf("%s%s\n", (!x_interface ? "Product: " : ""),
|
||
|
|
pagestart + 16);
|
||
|
|
pagestart[32] = tmp;
|
||
|
|
|
||
|
|
printf("%s%s\n", (!x_interface ? "Revision level: " : ""),
|
||
|
|
pagestart + 32);
|
||
|
|
|
||
|
|
printf("\n");
|
||
|
|
return status;
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
static int do_serial_number(int page_code)
|
||
|
|
{
|
||
|
|
int status, i, pagelen;
|
||
|
|
unsigned char *cmd;
|
||
|
|
unsigned char *pagestart;
|
||
|
|
|
||
|
|
for (i = 0; i < 1024; i++) {
|
||
|
|
buffer[i] = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
*((int *)buffer) = 0; /* length of input data */
|
||
|
|
*(((int *)buffer) + 1) = 4; /* length of output buffer */
|
||
|
|
|
||
|
|
cmd = (unsigned char *)(((int *)buffer) + 2);
|
||
|
|
|
||
|
|
cmd[0] = 0x12; /* INQUIRY */
|
||
|
|
cmd[1] = 0x01; /* lun=0, evpd=1 */
|
||
|
|
cmd[2] = 0x80; /* page code = 0x80, serial number */
|
||
|
|
cmd[3] = 0x00; /* (reserved) */
|
||
|
|
cmd[4] = 0x04; /* allocation length */
|
||
|
|
cmd[5] = 0x00; /* control */
|
||
|
|
|
||
|
|
status = do_sg_io(glob_fd, buffer);
|
||
|
|
if (status) {
|
||
|
|
printf("Error doing INQUIRY (evpd=1, serial number)\n");
|
||
|
|
return status;
|
||
|
|
}
|
||
|
|
|
||
|
|
pagestart = buffer + 8;
|
||
|
|
|
||
|
|
pagelen = 4 + pagestart[3];
|
||
|
|
*((int *)buffer) = 0; /* length of input data */
|
||
|
|
*(((int *)buffer) + 1) = pagelen; /* length of output buffer */
|
||
|
|
|
||
|
|
cmd[0] = 0x12; /* INQUIRY */
|
||
|
|
cmd[1] = 0x01; /* lun=0, evpd=1 */
|
||
|
|
cmd[2] = 0x80; /* page code = 0x80, serial number */
|
||
|
|
cmd[3] = 0x00; /* (reserved) */
|
||
|
|
cmd[4] = (unsigned char)pagelen; /* allocation length */
|
||
|
|
cmd[5] = 0x00; /* control */
|
||
|
|
|
||
|
|
status = do_sg_io(glob_fd, buffer);
|
||
|
|
if (status) {
|
||
|
|
printf("Error doing INQUIRY (evpd=1, serial number, len)\n");
|
||
|
|
return status;
|
||
|
|
}
|
||
|
|
|
||
|
|
printf("Serial Number '");
|
||
|
|
for (i = 0; i < pagestart[3]; i++)
|
||
|
|
printf("%c", pagestart[4 + i]);
|
||
|
|
printf("'\n");
|
||
|
|
printf("\n");
|
||
|
|
|
||
|
|
return status;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Print out a list of the known devices on the system */
|
||
|
|
static void show_devices()
|
||
|
|
{
|
||
|
|
int k, j, fd, err, bus;
|
||
|
|
My_scsi_idlun m_idlun;
|
||
|
|
char name[MDEV_NAME_SZ];
|
||
|
|
char ebuff[EBUFF_SZ];
|
||
|
|
int do_numeric = 1;
|
||
|
|
int max_holes = MAX_HOLES;
|
||
|
|
|
||
|
|
for (k = 0, j = 0; k < sizeof(devices) / sizeof(char *); k++) {
|
||
|
|
fd = open(devices[k], O_RDONLY | O_NONBLOCK);
|
||
|
|
if (fd < 0)
|
||
|
|
continue;
|
||
|
|
err =
|
||
|
|
ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, &(sg_map_arr[j].bus));
|
||
|
|
if (err < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ,
|
||
|
|
"SCSI(1) ioctl on %s failed", devices[k]);
|
||
|
|
perror(ebuff);
|
||
|
|
close(fd);
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
err = ioctl(fd, SCSI_IOCTL_GET_IDLUN, &m_idlun);
|
||
|
|
if (err < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ,
|
||
|
|
"SCSI(2) ioctl on %s failed", devices[k]);
|
||
|
|
perror(ebuff);
|
||
|
|
close(fd);
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
sg_map_arr[j].channel = (m_idlun.dev_id >> 16) & 0xff;
|
||
|
|
sg_map_arr[j].lun = (m_idlun.dev_id >> 8) & 0xff;
|
||
|
|
sg_map_arr[j].target_id = m_idlun.dev_id & 0xff;
|
||
|
|
sg_map_arr[j].dev_name = devices[k];
|
||
|
|
|
||
|
|
printf("[scsi%d ch=%d id=%d lun=%d %s] ", sg_map_arr[j].bus,
|
||
|
|
sg_map_arr[j].channel, sg_map_arr[j].target_id,
|
||
|
|
sg_map_arr[j].lun, sg_map_arr[j].dev_name);
|
||
|
|
|
||
|
|
++j;
|
||
|
|
printf("%s ", devices[k]);
|
||
|
|
close(fd);
|
||
|
|
};
|
||
|
|
printf("\n");
|
||
|
|
for (k = 0; k < MAX_SG_DEVS; k++) {
|
||
|
|
make_dev_name(name, NULL, k, do_numeric);
|
||
|
|
fd = open(name, O_RDWR | O_NONBLOCK);
|
||
|
|
if (fd < 0) {
|
||
|
|
if ((ENOENT == errno) && (0 == k)) {
|
||
|
|
do_numeric = 0;
|
||
|
|
make_dev_name(name, NULL, k, do_numeric);
|
||
|
|
fd = open(name, O_RDWR | O_NONBLOCK);
|
||
|
|
}
|
||
|
|
if (fd < 0) {
|
||
|
|
if (EBUSY == errno)
|
||
|
|
continue; /* step over if O_EXCL already on it */
|
||
|
|
else {
|
||
|
|
#if 0
|
||
|
|
snprintf(ebuff, EBUFF_SZ,
|
||
|
|
"open on %s failed (%d)", name,
|
||
|
|
errno);
|
||
|
|
perror(ebuff);
|
||
|
|
#endif
|
||
|
|
if (max_holes-- > 0)
|
||
|
|
continue;
|
||
|
|
else
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
max_holes = MAX_HOLES;
|
||
|
|
err = ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, &bus);
|
||
|
|
if (err < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ, "SCSI(3) ioctl on %s failed",
|
||
|
|
name);
|
||
|
|
perror(ebuff);
|
||
|
|
close(fd);
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
err = ioctl(fd, SCSI_IOCTL_GET_IDLUN, &m_idlun);
|
||
|
|
if (err < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ, "SCSI(3) ioctl on %s failed",
|
||
|
|
name);
|
||
|
|
perror(ebuff);
|
||
|
|
close(fd);
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
printf("[scsi%d ch=%d id=%d lun=%d %s]", bus,
|
||
|
|
(m_idlun.dev_id >> 16) & 0xff, m_idlun.dev_id & 0xff,
|
||
|
|
(m_idlun.dev_id >> 8) & 0xff, name);
|
||
|
|
|
||
|
|
for (j = 0; sg_map_arr[j].dev_name; ++j) {
|
||
|
|
if ((bus == sg_map_arr[j].bus) &&
|
||
|
|
((m_idlun.dev_id & 0xff) == sg_map_arr[j].target_id)
|
||
|
|
&& (((m_idlun.dev_id >> 16) & 0xff) ==
|
||
|
|
sg_map_arr[j].channel)
|
||
|
|
&& (((m_idlun.dev_id >> 8) & 0xff) ==
|
||
|
|
sg_map_arr[j].lun)) {
|
||
|
|
printf("%s [=%s scsi%d ch=%d id=%d lun=%d]\n",
|
||
|
|
name, sg_map_arr[j].dev_name, bus,
|
||
|
|
((m_idlun.dev_id >> 16) & 0xff),
|
||
|
|
m_idlun.dev_id & 0xff,
|
||
|
|
((m_idlun.dev_id >> 8) & 0xff));
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (NULL == sg_map_arr[j].dev_name)
|
||
|
|
printf("%s [scsi%d ch=%d id=%d lun=%d]\n", name, bus,
|
||
|
|
((m_idlun.dev_id >> 16) & 0xff),
|
||
|
|
m_idlun.dev_id & 0xff,
|
||
|
|
((m_idlun.dev_id >> 8) & 0xff));
|
||
|
|
close(fd);
|
||
|
|
}
|
||
|
|
printf("\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
static int show_pages(int page_code)
|
||
|
|
{
|
||
|
|
int offset;
|
||
|
|
int length;
|
||
|
|
int i;
|
||
|
|
unsigned long long pages_sup = 0;
|
||
|
|
unsigned long long pages_mask = 0;
|
||
|
|
|
||
|
|
if (!get_mode_page10(0x3f, page_code | 0x10)) {
|
||
|
|
length = 9 + getnbyte(buffer + 8, 2);
|
||
|
|
offset = 16 + getnbyte(buffer + 14, 2);
|
||
|
|
} else if (!get_mode_page(0x3f, page_code | 0x10)) {
|
||
|
|
length = 9 + buffer[8];
|
||
|
|
offset = 12 + buffer[11];
|
||
|
|
} else { /* Assume SCSI-1 and fake settings to report NO pages */
|
||
|
|
offset = 10;
|
||
|
|
length = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Get mask of pages supported by prog: */
|
||
|
|
for (i = 0; i < MAX_PAGENO; i++)
|
||
|
|
if (page_names[i])
|
||
|
|
pages_mask |= (1LL << i);
|
||
|
|
|
||
|
|
/* Get pages listed in mode_pages */
|
||
|
|
while (offset < length) {
|
||
|
|
pages_sup |= (1LL << (buffer[offset] & 0x3f));
|
||
|
|
offset += 2 + buffer[offset + 1];
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Mask out pages unsupported by this binary */
|
||
|
|
pages_sup &= pages_mask;
|
||
|
|
|
||
|
|
/* Notch page supported? */
|
||
|
|
if (pages_sup & (1LL << 12)) {
|
||
|
|
if (get_mode_page(12, 0))
|
||
|
|
return 2;
|
||
|
|
offset = 12 + buffer[11];
|
||
|
|
} else { /* Fake empty notch page */
|
||
|
|
memset(buffer, 0, SIZEOF_BUFFER);
|
||
|
|
offset = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
pages_mask = getnbyte(buffer + offset + 16, 4);
|
||
|
|
pages_mask <<= 32;
|
||
|
|
pages_mask += getnbyte(buffer + offset + 20, 4);
|
||
|
|
|
||
|
|
puts("Mode Pages supported by this binary and target:");
|
||
|
|
puts("-----------------------------------------------");
|
||
|
|
for (i = 0; i < MAX_PAGENO; i++)
|
||
|
|
if (pages_sup & (1LL << i))
|
||
|
|
printf("%02xh: %s Page%s\n", i, get_page_name(i),
|
||
|
|
(pages_mask & (1LL << i)) ? " (notched)" : "");
|
||
|
|
if (pages_sup & (1LL << 12)) {
|
||
|
|
printf("\nCurrent notch is %d.\n",
|
||
|
|
getnbyte(buffer + offset + 6, 2));
|
||
|
|
}
|
||
|
|
if (!pages_sup)
|
||
|
|
puts("No mode pages supported (SCSI-1?).");
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int open_sg_dev(char *devname)
|
||
|
|
{
|
||
|
|
int fd, err, bus, bbus, k;
|
||
|
|
My_scsi_idlun m_idlun, mm_idlun;
|
||
|
|
int do_numeric = 1;
|
||
|
|
char name[DEVNAME_SZ];
|
||
|
|
struct stat a_st;
|
||
|
|
int block_dev = 0;
|
||
|
|
|
||
|
|
strncpy(name, devname, DEVNAME_SZ);
|
||
|
|
name[DEVNAME_SZ - 1] = '\0';
|
||
|
|
fd = open(name, O_RDONLY);
|
||
|
|
if (fd < 0)
|
||
|
|
return fd;
|
||
|
|
if (fstat(fd, &a_st) < 0) {
|
||
|
|
fprintf(stderr, "could do fstat() on fd ??\n");
|
||
|
|
close(fd);
|
||
|
|
return -9999;
|
||
|
|
}
|
||
|
|
if (S_ISBLK(a_st.st_mode))
|
||
|
|
block_dev = 1;
|
||
|
|
if (block_dev || (ioctl(fd, SG_GET_TIMEOUT, 0) < 0)) {
|
||
|
|
err = ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, &bus);
|
||
|
|
if (err < 0) {
|
||
|
|
perror("A SCSI device name is required\n");
|
||
|
|
close(fd);
|
||
|
|
return -9999;
|
||
|
|
}
|
||
|
|
err = ioctl(fd, SCSI_IOCTL_GET_IDLUN, &m_idlun);
|
||
|
|
if (err < 0) {
|
||
|
|
perror("A SCSI device name is required\n");
|
||
|
|
close(fd);
|
||
|
|
return -9999;
|
||
|
|
}
|
||
|
|
close(fd);
|
||
|
|
|
||
|
|
for (k = 0; k < MAX_SG_DEVS; k++) {
|
||
|
|
make_dev_name(name, NULL, k, do_numeric);
|
||
|
|
fd = open(name, O_RDWR | O_NONBLOCK);
|
||
|
|
if (fd < 0) {
|
||
|
|
if ((ENOENT == errno) && (0 == k)) {
|
||
|
|
do_numeric = 0;
|
||
|
|
make_dev_name(name, NULL, k,
|
||
|
|
do_numeric);
|
||
|
|
fd = open(name, O_RDWR | O_NONBLOCK);
|
||
|
|
}
|
||
|
|
if (fd < 0) {
|
||
|
|
if (EBUSY == errno)
|
||
|
|
continue; /* step over if O_EXCL already on it */
|
||
|
|
else
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
err = ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, &bbus);
|
||
|
|
if (err < 0) {
|
||
|
|
perror("sg ioctl failed");
|
||
|
|
close(fd);
|
||
|
|
fd = -9999;
|
||
|
|
}
|
||
|
|
err = ioctl(fd, SCSI_IOCTL_GET_IDLUN, &mm_idlun);
|
||
|
|
if (err < 0) {
|
||
|
|
perror("sg ioctl failed");
|
||
|
|
close(fd);
|
||
|
|
fd = -9999;
|
||
|
|
}
|
||
|
|
if ((bus == bbus) &&
|
||
|
|
((m_idlun.dev_id & 0xff) ==
|
||
|
|
(mm_idlun.dev_id & 0xff))
|
||
|
|
&& (((m_idlun.dev_id >> 8) & 0xff) ==
|
||
|
|
((mm_idlun.dev_id >> 8) & 0xff))
|
||
|
|
&& (((m_idlun.dev_id >> 16) & 0xff) ==
|
||
|
|
((mm_idlun.dev_id >> 16) & 0xff)))
|
||
|
|
break;
|
||
|
|
else {
|
||
|
|
close(fd);
|
||
|
|
fd = -9999;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (fd >= 0) {
|
||
|
|
#ifdef SG_GET_RESERVED_SIZE
|
||
|
|
int size;
|
||
|
|
|
||
|
|
if (ioctl(fd, SG_GET_RESERVED_SIZE, &size) < 0) {
|
||
|
|
fprintf(stderr,
|
||
|
|
"Compiled with new driver, running on old!!\n");
|
||
|
|
close(fd);
|
||
|
|
return -9999;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
close(fd);
|
||
|
|
return open(name, O_RDWR);
|
||
|
|
} else
|
||
|
|
return fd;
|
||
|
|
}
|
||
|
|
|
||
|
|
int show_scsi_info(char *device)
|
||
|
|
{
|
||
|
|
int page_code = 0;
|
||
|
|
int status = 0;
|
||
|
|
|
||
|
|
print_msg(TEST_BREAK, __FUNCTION__);
|
||
|
|
|
||
|
|
show_devices();
|
||
|
|
|
||
|
|
glob_fd = open_sg_dev(device);
|
||
|
|
if (glob_fd < 0) {
|
||
|
|
if (-9999 == glob_fd)
|
||
|
|
fprintf(stderr,
|
||
|
|
"Couldn't find sg device corresponding to %s\n",
|
||
|
|
device);
|
||
|
|
else {
|
||
|
|
perror("sginfo(open)");
|
||
|
|
fprintf(stderr,
|
||
|
|
"file=%s, or no corresponding sg device found\n",
|
||
|
|
device);
|
||
|
|
fprintf(stderr, "Is sg driver loaded?\n");
|
||
|
|
}
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
status |= do_scsi_info_inquiry(page_code);
|
||
|
|
|
||
|
|
status |= do_serial_number(page_code);
|
||
|
|
|
||
|
|
status |= read_geometry(page_code);
|
||
|
|
|
||
|
|
status |= read_cache(page_code);
|
||
|
|
|
||
|
|
status |= read_format_info(page_code);
|
||
|
|
|
||
|
|
status |= error_recovery_page(page_code);
|
||
|
|
|
||
|
|
status |= read_control_page(page_code);
|
||
|
|
|
||
|
|
status |= read_disconnect_reconnect_data(page_code);
|
||
|
|
|
||
|
|
status |= read_defect_list(page_code);
|
||
|
|
|
||
|
|
status |= notch_parameters_page(page_code);
|
||
|
|
|
||
|
|
status |= verify_error_recovery(page_code);
|
||
|
|
|
||
|
|
status |= peripheral_device_page(page_code);
|
||
|
|
|
||
|
|
status |= do_user_page(page_code, 0);
|
||
|
|
|
||
|
|
status |= show_pages(page_code);
|
||
|
|
|
||
|
|
return status;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* -1 -> unrecoverable error, 0 -> successful, 1 -> recoverable (ENOMEM),
|
||
|
|
2 -> try again */
|
||
|
|
int sg_read2(int sg_fd, unsigned char *buff, int blocks, int from_block,
|
||
|
|
int bs, int cdbsz, int fua, int do_mmap)
|
||
|
|
{
|
||
|
|
unsigned char rdCmd[MAX_SCSI_CDBSZ];
|
||
|
|
unsigned char senseBuff[SENSE_BUFF_LEN];
|
||
|
|
sg_io_hdr_t io_hdr;
|
||
|
|
int res;
|
||
|
|
|
||
|
|
if (sg_build_scsi_cdb(rdCmd, cdbsz, blocks, from_block, 0, fua, 0)) {
|
||
|
|
fprintf(stderr,
|
||
|
|
ME "bad rd cdb build, from_block=%d, blocks=%d\n",
|
||
|
|
from_block, blocks);
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
|
||
|
|
io_hdr.interface_id = 'S';
|
||
|
|
io_hdr.cmd_len = cdbsz;
|
||
|
|
io_hdr.cmdp = rdCmd;
|
||
|
|
io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
|
||
|
|
io_hdr.dxfer_len = bs * blocks;
|
||
|
|
if (!do_mmap)
|
||
|
|
io_hdr.dxferp = buff;
|
||
|
|
io_hdr.mx_sb_len = SENSE_BUFF_LEN;
|
||
|
|
io_hdr.sbp = senseBuff;
|
||
|
|
io_hdr.timeout = DEF_TIMEOUT;
|
||
|
|
io_hdr.pack_id = from_block;
|
||
|
|
if (do_mmap)
|
||
|
|
io_hdr.flags |= SG_FLAG_MMAP_IO;
|
||
|
|
|
||
|
|
while (((res = write(sg_fd, &io_hdr, sizeof(io_hdr))) < 0) &&
|
||
|
|
(EINTR == errno)) ;
|
||
|
|
if (res < 0) {
|
||
|
|
if (ENOMEM == errno)
|
||
|
|
return 1;
|
||
|
|
perror("reading (wr) on sg device, error");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
while (((res = read(sg_fd, &io_hdr, sizeof(io_hdr))) < 0) &&
|
||
|
|
(EINTR == errno)) ;
|
||
|
|
if (res < 0) {
|
||
|
|
perror("reading (rd) on sg device, error");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
switch (sg_err_category3(&io_hdr)) {
|
||
|
|
case SG_ERR_CAT_CLEAN:
|
||
|
|
break;
|
||
|
|
case SG_ERR_CAT_RECOVERED:
|
||
|
|
fprintf(stderr,
|
||
|
|
"Recovered error while reading block=%d, num=%d\n",
|
||
|
|
from_block, blocks);
|
||
|
|
break;
|
||
|
|
case SG_ERR_CAT_MEDIA_CHANGED:
|
||
|
|
return 2;
|
||
|
|
default:
|
||
|
|
sg_chk_n_print3("reading", &io_hdr);
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
sum_of_resids += io_hdr.resid;
|
||
|
|
#if SG_DEBUG
|
||
|
|
fprintf(stderr, "duration=%u ms\n", io_hdr.duration);
|
||
|
|
#endif
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* -1 -> unrecoverable error, 0 -> successful, 1 -> recoverable (ENOMEM),
|
||
|
|
2 -> try again */
|
||
|
|
int sg_write2(int sg_fd, unsigned char *buff, int blocks, int to_block,
|
||
|
|
int bs, int cdbsz, int fua, int do_mmap, int *diop)
|
||
|
|
{
|
||
|
|
unsigned char wrCmd[MAX_SCSI_CDBSZ];
|
||
|
|
unsigned char senseBuff[SENSE_BUFF_LEN];
|
||
|
|
sg_io_hdr_t io_hdr;
|
||
|
|
int res;
|
||
|
|
|
||
|
|
if (sg_build_scsi_cdb(wrCmd, cdbsz, blocks, to_block, 1, fua, 0)) {
|
||
|
|
fprintf(stderr, ME "bad wr cdb build, to_block=%d, blocks=%d\n",
|
||
|
|
to_block, blocks);
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
|
||
|
|
io_hdr.interface_id = 'S';
|
||
|
|
io_hdr.cmd_len = cdbsz;
|
||
|
|
io_hdr.cmdp = wrCmd;
|
||
|
|
io_hdr.dxfer_direction = SG_DXFER_TO_DEV;
|
||
|
|
io_hdr.dxfer_len = bs * blocks;
|
||
|
|
if (!do_mmap)
|
||
|
|
io_hdr.dxferp = buff;
|
||
|
|
io_hdr.mx_sb_len = SENSE_BUFF_LEN;
|
||
|
|
io_hdr.sbp = senseBuff;
|
||
|
|
io_hdr.timeout = DEF_TIMEOUT;
|
||
|
|
io_hdr.pack_id = to_block;
|
||
|
|
if (do_mmap)
|
||
|
|
io_hdr.flags |= SG_FLAG_MMAP_IO;
|
||
|
|
if (diop && *diop)
|
||
|
|
io_hdr.flags |= SG_FLAG_DIRECT_IO;
|
||
|
|
|
||
|
|
while (((res = write(sg_fd, &io_hdr, sizeof(io_hdr))) < 0) &&
|
||
|
|
(EINTR == errno)) ;
|
||
|
|
if (res < 0) {
|
||
|
|
if (ENOMEM == errno)
|
||
|
|
return 1;
|
||
|
|
perror("writing (wr) on sg device, error");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
while (((res = read(sg_fd, &io_hdr, sizeof(io_hdr))) < 0) &&
|
||
|
|
(EINTR == errno)) ;
|
||
|
|
if (res < 0) {
|
||
|
|
perror("writing (rd) on sg device, error");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
switch (sg_err_category3(&io_hdr)) {
|
||
|
|
case SG_ERR_CAT_CLEAN:
|
||
|
|
break;
|
||
|
|
case SG_ERR_CAT_RECOVERED:
|
||
|
|
fprintf(stderr,
|
||
|
|
"Recovered error while writing block=%d, num=%d\n",
|
||
|
|
to_block, blocks);
|
||
|
|
break;
|
||
|
|
case SG_ERR_CAT_MEDIA_CHANGED:
|
||
|
|
return 2;
|
||
|
|
default:
|
||
|
|
sg_chk_n_print3("writing", &io_hdr);
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
if (diop && *diop &&
|
||
|
|
((io_hdr.info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO))
|
||
|
|
*diop = 0; /* flag that dio not done (completely) */
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
int do_scsi_sgm_read_write(char *device)
|
||
|
|
{
|
||
|
|
int skip = 0;
|
||
|
|
int seek = 0;
|
||
|
|
int bs = 0;
|
||
|
|
int bpt = DEF_BLOCKS_PER_TRANSFER;
|
||
|
|
char inf[INOUTF_SZ];
|
||
|
|
int in_type = FT_OTHER;
|
||
|
|
char outf[INOUTF_SZ];
|
||
|
|
int out_type = FT_OTHER;
|
||
|
|
int res, t;
|
||
|
|
int infd, outfd, blocks;
|
||
|
|
unsigned char *wrkPos;
|
||
|
|
unsigned char *wrkBuff = NULL;
|
||
|
|
unsigned char *wrkMmap = NULL;
|
||
|
|
int in_num_sect = 0;
|
||
|
|
int in_res_sz = 0;
|
||
|
|
int out_num_sect = 0;
|
||
|
|
int out_res_sz = 0;
|
||
|
|
int do_time = 1;
|
||
|
|
int scsi_cdbsz = DEF_SCSI_CDBSZ;
|
||
|
|
int do_sync = 1;
|
||
|
|
int do_dio = 0;
|
||
|
|
int num_dio_not_done = 0;
|
||
|
|
int fua_mode = 0;
|
||
|
|
int in_sect_sz, out_sect_sz;
|
||
|
|
char ebuff[EBUFF_SZ];
|
||
|
|
int blocks_per;
|
||
|
|
int req_count;
|
||
|
|
size_t psz = getpagesize();
|
||
|
|
struct timeval start_tm, end_tm;
|
||
|
|
|
||
|
|
print_msg(TEST_BREAK, __FUNCTION__);
|
||
|
|
|
||
|
|
strcpy(inf, "/dev/zero");
|
||
|
|
strcpy(outf, device);
|
||
|
|
|
||
|
|
install_handler(SIGINT, interrupt_handler);
|
||
|
|
install_handler(SIGQUIT, interrupt_handler);
|
||
|
|
install_handler(SIGPIPE, interrupt_handler);
|
||
|
|
install_handler(SIGUSR1, siginfo_handler);
|
||
|
|
|
||
|
|
infd = STDIN_FILENO;
|
||
|
|
outfd = STDOUT_FILENO;
|
||
|
|
|
||
|
|
in_type = dd_filetype(inf);
|
||
|
|
|
||
|
|
if (FT_ST == in_type) {
|
||
|
|
fprintf(stderr, ME "unable to use scsi tape device %s\n", inf);
|
||
|
|
return 1;
|
||
|
|
} else if (FT_SG == in_type) {
|
||
|
|
if ((infd = open(inf, O_RDWR)) < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ,
|
||
|
|
ME "could not open %s for sg reading", inf);
|
||
|
|
perror(ebuff);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
res = ioctl(infd, SG_GET_VERSION_NUM, &t);
|
||
|
|
if ((res < 0) || (t < 30122)) {
|
||
|
|
fprintf(stderr, ME "sg driver prior to 3.1.22\n");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
in_res_sz = bs * bpt;
|
||
|
|
if (0 != (in_res_sz % psz)) /* round up to next page */
|
||
|
|
in_res_sz = ((in_res_sz / psz) + 1) * psz;
|
||
|
|
if (ioctl(infd, SG_GET_RESERVED_SIZE, &t) < 0) {
|
||
|
|
perror(ME "SG_GET_RESERVED_SIZE error");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
if (in_res_sz > t) {
|
||
|
|
if (ioctl(infd, SG_SET_RESERVED_SIZE, &in_res_sz) < 0) {
|
||
|
|
perror(ME "SG_SET_RESERVED_SIZE error");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
wrkMmap = mmap(NULL, in_res_sz, PROT_READ | PROT_WRITE,
|
||
|
|
MAP_SHARED, infd, 0);
|
||
|
|
if (MAP_FAILED == wrkMmap) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ,
|
||
|
|
ME "error using mmap() on file: %s", inf);
|
||
|
|
perror(ebuff);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
if ((infd = open(inf, O_RDONLY)) < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ,
|
||
|
|
ME "could not open %s for reading", inf);
|
||
|
|
perror(ebuff);
|
||
|
|
return 1;
|
||
|
|
} else if (skip > 0) {
|
||
|
|
llse_loff_t offset = skip;
|
||
|
|
|
||
|
|
offset *= bs; /* could exceed 32 bits here! */
|
||
|
|
if (llse_llseek(infd, offset, SEEK_SET) < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ, ME "couldn't skip to "
|
||
|
|
"required position on %s", inf);
|
||
|
|
perror(ebuff);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (outf[0] && ('-' != outf[0])) {
|
||
|
|
out_type = dd_filetype(outf);
|
||
|
|
|
||
|
|
if (FT_ST == out_type) {
|
||
|
|
fprintf(stderr,
|
||
|
|
ME "unable to use scsi tape device %s\n", outf);
|
||
|
|
return 1;
|
||
|
|
} else if (FT_SG == out_type) {
|
||
|
|
if ((outfd = open(outf, O_RDWR)) < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ,
|
||
|
|
ME "could not open %s for "
|
||
|
|
"sg writing", outf);
|
||
|
|
perror(ebuff);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
res = ioctl(outfd, SG_GET_VERSION_NUM, &t);
|
||
|
|
if ((res < 0) || (t < 30122)) {
|
||
|
|
fprintf(stderr,
|
||
|
|
ME "sg driver prior to 3.1.22\n");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
if (ioctl(outfd, SG_GET_RESERVED_SIZE, &t) < 0) {
|
||
|
|
perror(ME "SG_GET_RESERVED_SIZE error");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
out_res_sz = bs * bpt;
|
||
|
|
if (out_res_sz > t) {
|
||
|
|
if (ioctl
|
||
|
|
(outfd, SG_SET_RESERVED_SIZE,
|
||
|
|
&out_res_sz) < 0) {
|
||
|
|
perror(ME "SG_SET_RESERVED_SIZE error");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (NULL == wrkMmap) {
|
||
|
|
wrkMmap =
|
||
|
|
mmap(NULL, out_res_sz,
|
||
|
|
PROT_READ | PROT_WRITE, MAP_SHARED,
|
||
|
|
outfd, 0);
|
||
|
|
if (MAP_FAILED == wrkMmap) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ,
|
||
|
|
ME
|
||
|
|
"error using mmap() on file: %s",
|
||
|
|
outf);
|
||
|
|
perror(ebuff);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
} else if (FT_DEV_NULL == out_type)
|
||
|
|
outfd = -1; /* don't bother opening */
|
||
|
|
else {
|
||
|
|
if (FT_RAW != out_type) {
|
||
|
|
if ((outfd =
|
||
|
|
open(outf, O_WRONLY | O_CREAT,
|
||
|
|
0666)) < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ,
|
||
|
|
ME
|
||
|
|
"could not open %s for writing",
|
||
|
|
outf);
|
||
|
|
perror(ebuff);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
if ((outfd = open(outf, O_WRONLY)) < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ,
|
||
|
|
ME "could not open %s "
|
||
|
|
"for raw writing", outf);
|
||
|
|
perror(ebuff);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (seek > 0) {
|
||
|
|
llse_loff_t offset = seek;
|
||
|
|
|
||
|
|
offset *= bs; /* could exceed 32 bits here! */
|
||
|
|
if (llse_llseek(outfd, offset, SEEK_SET) < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ,
|
||
|
|
ME "couldn't seek to "
|
||
|
|
"required position on %s",
|
||
|
|
outf);
|
||
|
|
perror(ebuff);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if ((STDIN_FILENO == infd) && (STDOUT_FILENO == outfd)) {
|
||
|
|
fprintf(stderr,
|
||
|
|
"Can't have both 'if' as stdin _and_ 'of' as stdout\n");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
#if 0
|
||
|
|
if ((FT_OTHER == in_type) && (FT_OTHER == out_type)) {
|
||
|
|
fprintf(stderr, "Both 'if' and 'of' can't be ordinary files\n");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
if (dd_count < 0) {
|
||
|
|
if (FT_SG == in_type) {
|
||
|
|
res = read_capacity(infd, &in_num_sect, &in_sect_sz);
|
||
|
|
if (2 == res) {
|
||
|
|
fprintf(stderr,
|
||
|
|
"Unit attention, media changed(in), continuing\n");
|
||
|
|
res =
|
||
|
|
read_capacity(infd, &in_num_sect,
|
||
|
|
&in_sect_sz);
|
||
|
|
}
|
||
|
|
if (0 != res) {
|
||
|
|
fprintf(stderr,
|
||
|
|
"Unable to read capacity on %s\n", inf);
|
||
|
|
in_num_sect = -1;
|
||
|
|
} else {
|
||
|
|
#if 0
|
||
|
|
if (0 == in_sect_sz)
|
||
|
|
in_sect_sz = bs;
|
||
|
|
else if (in_sect_sz > bs)
|
||
|
|
in_num_sect *= (in_sect_sz / bs);
|
||
|
|
else if (in_sect_sz < bs)
|
||
|
|
in_num_sect /= (bs / in_sect_sz);
|
||
|
|
#endif
|
||
|
|
if (in_num_sect > skip)
|
||
|
|
in_num_sect -= skip;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (FT_SG == out_type) {
|
||
|
|
res = read_capacity(outfd, &out_num_sect, &out_sect_sz);
|
||
|
|
if (2 == res) {
|
||
|
|
fprintf(stderr,
|
||
|
|
"Unit attention, media changed(out), continuing\n");
|
||
|
|
res =
|
||
|
|
read_capacity(outfd, &out_num_sect,
|
||
|
|
&out_sect_sz);
|
||
|
|
}
|
||
|
|
if (0 != res) {
|
||
|
|
fprintf(stderr,
|
||
|
|
"Unable to read capacity on %s\n",
|
||
|
|
outf);
|
||
|
|
out_num_sect = -1;
|
||
|
|
} else {
|
||
|
|
if (out_num_sect > seek)
|
||
|
|
out_num_sect -= seek;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
#ifdef SG_DEBUG
|
||
|
|
fprintf(stderr,
|
||
|
|
"Start of loop, count=%d, in_num_sect=%d, out_num_sect=%d\n",
|
||
|
|
dd_count, in_num_sect, out_num_sect);
|
||
|
|
#endif
|
||
|
|
if (in_num_sect > 0) {
|
||
|
|
if (out_num_sect > 0)
|
||
|
|
dd_count =
|
||
|
|
(in_num_sect >
|
||
|
|
out_num_sect) ? out_num_sect : in_num_sect;
|
||
|
|
else
|
||
|
|
dd_count = in_num_sect;
|
||
|
|
} else
|
||
|
|
dd_count = out_num_sect;
|
||
|
|
}
|
||
|
|
if (dd_count < 0) {
|
||
|
|
fprintf(stderr, "Couldn't calculate count, please give one\n");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
if (do_dio && (FT_SG != in_type)) {
|
||
|
|
do_dio = 0;
|
||
|
|
fprintf(stderr,
|
||
|
|
">>> dio only performed on 'of' side when 'if' is"
|
||
|
|
" an sg device\n");
|
||
|
|
}
|
||
|
|
if (do_dio) {
|
||
|
|
int fd;
|
||
|
|
char c;
|
||
|
|
|
||
|
|
if ((fd = open(proc_allow_dio, O_RDONLY)) >= 0) {
|
||
|
|
if (1 == read(fd, &c, 1)) {
|
||
|
|
if ('0' == c)
|
||
|
|
fprintf(stderr,
|
||
|
|
">>> %s set to '0' but should be set "
|
||
|
|
"to '1' for direct IO\n",
|
||
|
|
proc_allow_dio);
|
||
|
|
}
|
||
|
|
close(fd);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (wrkMmap)
|
||
|
|
wrkPos = wrkMmap;
|
||
|
|
else {
|
||
|
|
if ((FT_RAW == in_type) || (FT_RAW == out_type)) {
|
||
|
|
wrkBuff = malloc(bs * bpt + psz);
|
||
|
|
if (0 == wrkBuff) {
|
||
|
|
fprintf(stderr,
|
||
|
|
"Not enough user memory for raw\n");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
wrkPos =
|
||
|
|
(unsigned char *)(((unsigned long)wrkBuff + psz - 1)
|
||
|
|
& (~(psz - 1)));
|
||
|
|
} else {
|
||
|
|
wrkBuff = malloc(bs * bpt);
|
||
|
|
if (0 == wrkBuff) {
|
||
|
|
fprintf(stderr, "Not enough user memory\n");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
wrkPos = wrkBuff;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
blocks_per = bpt;
|
||
|
|
#ifdef SG_DEBUG
|
||
|
|
fprintf(stderr, "Start of loop, count=%d, blocks_per=%d\n",
|
||
|
|
dd_count, blocks_per);
|
||
|
|
#endif
|
||
|
|
if (do_time) {
|
||
|
|
start_tm.tv_sec = 0;
|
||
|
|
start_tm.tv_usec = 0;
|
||
|
|
gettimeofday(&start_tm, NULL);
|
||
|
|
}
|
||
|
|
req_count = dd_count;
|
||
|
|
|
||
|
|
while (dd_count > 0) {
|
||
|
|
blocks = (dd_count > blocks_per) ? blocks_per : dd_count;
|
||
|
|
if (FT_SG == in_type) {
|
||
|
|
int fua = fua_mode & 2;
|
||
|
|
|
||
|
|
res =
|
||
|
|
sg_read2(infd, wrkPos, blocks, skip, bs, scsi_cdbsz,
|
||
|
|
fua, 1);
|
||
|
|
if (2 == res) {
|
||
|
|
fprintf(stderr,
|
||
|
|
"Unit attention, media changed, continuing (r)\n");
|
||
|
|
res =
|
||
|
|
sg_read2(infd, wrkPos, blocks, skip, bs,
|
||
|
|
scsi_cdbsz, fua, 1);
|
||
|
|
}
|
||
|
|
if (0 != res) {
|
||
|
|
fprintf(stderr, "sg_read2 failed, skip=%d\n",
|
||
|
|
skip);
|
||
|
|
break;
|
||
|
|
} else
|
||
|
|
in_full += blocks;
|
||
|
|
} else {
|
||
|
|
while (((res = read(infd, wrkPos, blocks * bs)) < 0) &&
|
||
|
|
(EINTR == errno)) ;
|
||
|
|
if (res < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ,
|
||
|
|
ME "reading, skip=%d ", skip);
|
||
|
|
perror(ebuff);
|
||
|
|
break;
|
||
|
|
} else if (res < blocks * bs) {
|
||
|
|
dd_count = 0;
|
||
|
|
blocks = res / bs;
|
||
|
|
if ((res % bs) > 0) {
|
||
|
|
blocks++;
|
||
|
|
in_partial++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
in_full += blocks;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (FT_SG == out_type) {
|
||
|
|
int do_mmap = (FT_SG == in_type) ? 0 : 1;
|
||
|
|
int fua = fua_mode & 1;
|
||
|
|
int dio_res = do_dio;
|
||
|
|
|
||
|
|
res =
|
||
|
|
sg_write2(outfd, wrkPos, blocks, seek, bs,
|
||
|
|
scsi_cdbsz, fua, do_mmap, &dio_res);
|
||
|
|
if (2 == res) {
|
||
|
|
fprintf(stderr,
|
||
|
|
"Unit attention, media changed, continuing (w)\n");
|
||
|
|
res =
|
||
|
|
sg_write2(outfd, wrkPos, blocks, seek, bs,
|
||
|
|
scsi_cdbsz, fua, do_mmap,
|
||
|
|
&dio_res);
|
||
|
|
} else if (0 != res) {
|
||
|
|
fprintf(stderr, "sg_write2 failed, seek=%d\n",
|
||
|
|
seek);
|
||
|
|
break;
|
||
|
|
} else {
|
||
|
|
out_full += blocks;
|
||
|
|
if (do_dio && (0 == dio_res))
|
||
|
|
num_dio_not_done++;
|
||
|
|
}
|
||
|
|
} else if (FT_DEV_NULL == out_type)
|
||
|
|
out_full += blocks; /* act as if written out without error */
|
||
|
|
else {
|
||
|
|
while (((res = write(outfd, wrkPos, blocks * bs)) < 0)
|
||
|
|
&& (EINTR == errno)) ;
|
||
|
|
if (res < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ,
|
||
|
|
ME "writing, seek=%d ", seek);
|
||
|
|
perror(ebuff);
|
||
|
|
break;
|
||
|
|
} else if (res < blocks * bs) {
|
||
|
|
fprintf(stderr,
|
||
|
|
"output file probably full, seek=%d ",
|
||
|
|
seek);
|
||
|
|
blocks = res / bs;
|
||
|
|
out_full += blocks;
|
||
|
|
if ((res % bs) > 0)
|
||
|
|
out_partial++;
|
||
|
|
break;
|
||
|
|
} else
|
||
|
|
out_full += blocks;
|
||
|
|
}
|
||
|
|
if (dd_count > 0)
|
||
|
|
dd_count -= blocks;
|
||
|
|
skip += blocks;
|
||
|
|
seek += blocks;
|
||
|
|
}
|
||
|
|
if ((do_time) && (start_tm.tv_sec || start_tm.tv_usec)) {
|
||
|
|
struct timeval res_tm;
|
||
|
|
double a, b;
|
||
|
|
|
||
|
|
gettimeofday(&end_tm, NULL);
|
||
|
|
res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec;
|
||
|
|
res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec;
|
||
|
|
if (res_tm.tv_usec < 0) {
|
||
|
|
--res_tm.tv_sec;
|
||
|
|
res_tm.tv_usec += 1000000;
|
||
|
|
}
|
||
|
|
a = res_tm.tv_sec;
|
||
|
|
a += (0.000001 * res_tm.tv_usec);
|
||
|
|
b = (double)bs *(req_count - dd_count);
|
||
|
|
printf("time to transfer data was %d.%06d secs",
|
||
|
|
(int)res_tm.tv_sec, (int)res_tm.tv_usec);
|
||
|
|
if ((a > 0.00001) && (b > 511))
|
||
|
|
printf(", %.2f MB/sec\n", b / (a * 1000000.0));
|
||
|
|
else
|
||
|
|
printf("\n");
|
||
|
|
}
|
||
|
|
if (do_sync) {
|
||
|
|
if (FT_SG == out_type) {
|
||
|
|
fprintf(stderr, ">> Synchronizing cache on %s\n", outf);
|
||
|
|
res = sync_cache(outfd);
|
||
|
|
if (2 == res) {
|
||
|
|
fprintf(stderr,
|
||
|
|
"Unit attention, media changed(in), continuing\n");
|
||
|
|
res = sync_cache(outfd);
|
||
|
|
}
|
||
|
|
if (0 != res)
|
||
|
|
fprintf(stderr,
|
||
|
|
"Unable to synchronize cache\n");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (wrkBuff)
|
||
|
|
free(wrkBuff);
|
||
|
|
if (STDIN_FILENO != infd)
|
||
|
|
close(infd);
|
||
|
|
if ((STDOUT_FILENO != outfd) && (FT_DEV_NULL != out_type))
|
||
|
|
close(outfd);
|
||
|
|
res = 0;
|
||
|
|
if (0 != dd_count) {
|
||
|
|
fprintf(stderr, "Some error occurred,");
|
||
|
|
res = 2;
|
||
|
|
}
|
||
|
|
print_stats();
|
||
|
|
if (sum_of_resids)
|
||
|
|
fprintf(stderr, ">> Non-zero sum of residual counts=%d\n",
|
||
|
|
sum_of_resids);
|
||
|
|
if (num_dio_not_done)
|
||
|
|
fprintf(stderr, ">> dio requested but _not done %d times\n",
|
||
|
|
num_dio_not_done);
|
||
|
|
return res;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void guarded_stop_in(Rq_coll * clp)
|
||
|
|
{
|
||
|
|
pthread_mutex_lock(&clp->in_mutex);
|
||
|
|
clp->in_stop = 1;
|
||
|
|
pthread_mutex_unlock(&clp->in_mutex);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void guarded_stop_out(Rq_coll * clp)
|
||
|
|
{
|
||
|
|
pthread_mutex_lock(&clp->out_mutex);
|
||
|
|
clp->out_stop = 1;
|
||
|
|
pthread_mutex_unlock(&clp->out_mutex);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void guarded_stop_both(Rq_coll * clp)
|
||
|
|
{
|
||
|
|
guarded_stop_in(clp);
|
||
|
|
guarded_stop_out(clp);
|
||
|
|
}
|
||
|
|
|
||
|
|
void *sig_listen_thread(void *v_clp)
|
||
|
|
{
|
||
|
|
Rq_coll *clp = (Rq_coll *) v_clp;
|
||
|
|
int sig_number;
|
||
|
|
|
||
|
|
while (1) {
|
||
|
|
sigwait(&signal_set, &sig_number);
|
||
|
|
if (SIGINT == sig_number) {
|
||
|
|
fprintf(stderr, ME "interrupted by SIGINT\n");
|
||
|
|
guarded_stop_both(clp);
|
||
|
|
pthread_cond_broadcast(&clp->out_sync_cv);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
void cleanup_in(void *v_clp)
|
||
|
|
{
|
||
|
|
Rq_coll *clp = (Rq_coll *) v_clp;
|
||
|
|
|
||
|
|
fprintf(stderr, "thread cancelled while in mutex held\n");
|
||
|
|
clp->in_stop = 1;
|
||
|
|
pthread_mutex_unlock(&clp->in_mutex);
|
||
|
|
guarded_stop_out(clp);
|
||
|
|
pthread_cond_broadcast(&clp->out_sync_cv);
|
||
|
|
}
|
||
|
|
|
||
|
|
void cleanup_out(void *v_clp)
|
||
|
|
{
|
||
|
|
Rq_coll *clp = (Rq_coll *) v_clp;
|
||
|
|
|
||
|
|
fprintf(stderr, "thread cancelled while out mutex held\n");
|
||
|
|
clp->out_stop = 1;
|
||
|
|
pthread_mutex_unlock(&clp->out_mutex);
|
||
|
|
guarded_stop_in(clp);
|
||
|
|
pthread_cond_broadcast(&clp->out_sync_cv);
|
||
|
|
}
|
||
|
|
|
||
|
|
void *read_write_thread(void *v_clp)
|
||
|
|
{
|
||
|
|
Rq_coll *clp = (Rq_coll *) v_clp;
|
||
|
|
Rq_elem rel;
|
||
|
|
Rq_elem *rep = &rel;
|
||
|
|
size_t psz = 0;
|
||
|
|
int sz = clp->bpt * clp->bs;
|
||
|
|
int stop_after_write = 0;
|
||
|
|
int seek_skip = clp->seek - clp->skip;
|
||
|
|
int blocks, status;
|
||
|
|
|
||
|
|
memset(rep, 0, sizeof(Rq_elem));
|
||
|
|
psz = getpagesize();
|
||
|
|
if (NULL == (rep->alloc_bp = malloc(sz + psz)))
|
||
|
|
err_exit(ENOMEM, "out of memory creating user buffers\n");
|
||
|
|
rep->buffp =
|
||
|
|
(unsigned char *)(((unsigned long)rep->alloc_bp + psz - 1) &
|
||
|
|
(~(psz - 1)));
|
||
|
|
/* Follow clp members are constant during lifetime of thread */
|
||
|
|
rep->bs = clp->bs;
|
||
|
|
rep->fua_mode = clp->fua_mode;
|
||
|
|
rep->dio = clp->dio;
|
||
|
|
rep->infd = clp->infd;
|
||
|
|
rep->outfd = clp->outfd;
|
||
|
|
rep->debug = clp->debug;
|
||
|
|
rep->in_scsi_type = clp->in_scsi_type;
|
||
|
|
rep->out_scsi_type = clp->out_scsi_type;
|
||
|
|
rep->cdbsz = clp->cdbsz;
|
||
|
|
|
||
|
|
while (1) {
|
||
|
|
status = pthread_mutex_lock(&clp->in_mutex);
|
||
|
|
if (0 != status)
|
||
|
|
err_exit(status, "lock in_mutex");
|
||
|
|
if (clp->in_stop || (clp->in_count <= 0)) {
|
||
|
|
/* no more to do, exit loop then thread */
|
||
|
|
status = pthread_mutex_unlock(&clp->in_mutex);
|
||
|
|
if (0 != status)
|
||
|
|
err_exit(status, "unlock in_mutex");
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
blocks = (clp->in_count > clp->bpt) ? clp->bpt : clp->in_count;
|
||
|
|
rep->wr = 0;
|
||
|
|
rep->blk = clp->in_blk;
|
||
|
|
rep->num_blks = blocks;
|
||
|
|
clp->in_blk += blocks;
|
||
|
|
clp->in_count -= blocks;
|
||
|
|
|
||
|
|
pthread_cleanup_push(cleanup_in, (void *)clp);
|
||
|
|
if (FT_SG == clp->in_type)
|
||
|
|
sg_in_operation(clp, rep); /* lets go of in_mutex mid operation */
|
||
|
|
else {
|
||
|
|
stop_after_write =
|
||
|
|
normal_in_operation(clp, rep, blocks);
|
||
|
|
status = pthread_mutex_unlock(&clp->in_mutex);
|
||
|
|
if (0 != status)
|
||
|
|
err_exit(status, "unlock in_mutex");
|
||
|
|
}
|
||
|
|
pthread_cleanup_pop(0);
|
||
|
|
|
||
|
|
status = pthread_mutex_lock(&clp->out_mutex);
|
||
|
|
if (0 != status)
|
||
|
|
err_exit(status, "lock out_mutex");
|
||
|
|
if (FT_DEV_NULL != clp->out_type) {
|
||
|
|
while ((!clp->out_stop) &&
|
||
|
|
((rep->blk + seek_skip) != clp->out_blk)) {
|
||
|
|
/* if write would be out of sequence then wait */
|
||
|
|
pthread_cleanup_push(cleanup_out, (void *)clp);
|
||
|
|
status =
|
||
|
|
pthread_cond_wait(&clp->out_sync_cv,
|
||
|
|
&clp->out_mutex);
|
||
|
|
if (0 != status)
|
||
|
|
err_exit(status, "cond out_sync_cv");
|
||
|
|
pthread_cleanup_pop(0);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (clp->out_stop || (clp->out_count <= 0)) {
|
||
|
|
if (!clp->out_stop)
|
||
|
|
clp->out_stop = 1;
|
||
|
|
status = pthread_mutex_unlock(&clp->out_mutex);
|
||
|
|
if (0 != status)
|
||
|
|
err_exit(status, "unlock out_mutex");
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
if (stop_after_write)
|
||
|
|
clp->out_stop = 1;
|
||
|
|
rep->wr = 1;
|
||
|
|
rep->blk = clp->out_blk;
|
||
|
|
/* rep->num_blks = blocks; */
|
||
|
|
clp->out_blk += blocks;
|
||
|
|
clp->out_count -= blocks;
|
||
|
|
|
||
|
|
pthread_cleanup_push(cleanup_out, (void *)clp);
|
||
|
|
if (FT_SG == clp->out_type)
|
||
|
|
sg_out_operation(clp, rep); /* releases out_mutex mid operation */
|
||
|
|
else if (FT_DEV_NULL == clp->out_type) {
|
||
|
|
/* skip actual write operation */
|
||
|
|
clp->out_done_count -= blocks;
|
||
|
|
status = pthread_mutex_unlock(&clp->out_mutex);
|
||
|
|
if (0 != status)
|
||
|
|
err_exit(status, "unlock out_mutex");
|
||
|
|
} else {
|
||
|
|
normal_out_operation(clp, rep, blocks);
|
||
|
|
status = pthread_mutex_unlock(&clp->out_mutex);
|
||
|
|
if (0 != status)
|
||
|
|
err_exit(status, "unlock out_mutex");
|
||
|
|
}
|
||
|
|
pthread_cleanup_pop(0);
|
||
|
|
|
||
|
|
if (stop_after_write)
|
||
|
|
break;
|
||
|
|
pthread_cond_broadcast(&clp->out_sync_cv);
|
||
|
|
} /* end of while loop */
|
||
|
|
if (rep->alloc_bp)
|
||
|
|
free(rep->alloc_bp);
|
||
|
|
status = pthread_mutex_lock(&clp->in_mutex);
|
||
|
|
if (0 != status)
|
||
|
|
err_exit(status, "lock in_mutex");
|
||
|
|
if (!clp->in_stop)
|
||
|
|
clp->in_stop = 1; /* flag other workers to stop */
|
||
|
|
status = pthread_mutex_unlock(&clp->in_mutex);
|
||
|
|
if (0 != status)
|
||
|
|
err_exit(status, "unlock in_mutex");
|
||
|
|
pthread_cond_broadcast(&clp->out_sync_cv);
|
||
|
|
return stop_after_write ? NULL : v_clp;
|
||
|
|
}
|
||
|
|
|
||
|
|
int normal_in_operation(Rq_coll * clp, Rq_elem * rep, int blocks)
|
||
|
|
{
|
||
|
|
int res;
|
||
|
|
int stop_after_write = 0;
|
||
|
|
|
||
|
|
/* enters holding in_mutex */
|
||
|
|
while (((res = read(clp->infd, rep->buffp,
|
||
|
|
blocks * clp->bs)) < 0) && (EINTR == errno)) ;
|
||
|
|
if (res < 0) {
|
||
|
|
if (clp->coe) {
|
||
|
|
memset(rep->buffp, 0, rep->num_blks * rep->bs);
|
||
|
|
fprintf(stderr,
|
||
|
|
">> substituted zeros for in blk=%d for "
|
||
|
|
"%d bytes, %s\n", rep->blk,
|
||
|
|
rep->num_blks * rep->bs, strerror(errno));
|
||
|
|
res = rep->num_blks * clp->bs;
|
||
|
|
} else {
|
||
|
|
fprintf(stderr, "error in normal read, %s\n",
|
||
|
|
strerror(errno));
|
||
|
|
clp->in_stop = 1;
|
||
|
|
guarded_stop_out(clp);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (res < blocks * clp->bs) {
|
||
|
|
int o_blocks = blocks;
|
||
|
|
stop_after_write = 1;
|
||
|
|
blocks = res / clp->bs;
|
||
|
|
if ((res % clp->bs) > 0) {
|
||
|
|
blocks++;
|
||
|
|
clp->in_partial++;
|
||
|
|
}
|
||
|
|
/* Reverse out + re-apply blocks on clp */
|
||
|
|
clp->in_blk -= o_blocks;
|
||
|
|
clp->in_count += o_blocks;
|
||
|
|
rep->num_blks = blocks;
|
||
|
|
clp->in_blk += blocks;
|
||
|
|
clp->in_count -= blocks;
|
||
|
|
}
|
||
|
|
clp->in_done_count -= blocks;
|
||
|
|
return stop_after_write;
|
||
|
|
}
|
||
|
|
|
||
|
|
void normal_out_operation(Rq_coll * clp, Rq_elem * rep, int blocks)
|
||
|
|
{
|
||
|
|
int res;
|
||
|
|
|
||
|
|
/* enters holding out_mutex */
|
||
|
|
while (((res = write(clp->outfd, rep->buffp,
|
||
|
|
rep->num_blks * clp->bs)) < 0)
|
||
|
|
&& (EINTR == errno)) ;
|
||
|
|
if (res < 0) {
|
||
|
|
if (clp->coe) {
|
||
|
|
fprintf(stderr, ">> ignored error for out blk=%d for "
|
||
|
|
"%d bytes, %s\n", rep->blk,
|
||
|
|
rep->num_blks * rep->bs, strerror(errno));
|
||
|
|
res = rep->num_blks * clp->bs;
|
||
|
|
} else {
|
||
|
|
fprintf(stderr, "error normal write, %s\n",
|
||
|
|
strerror(errno));
|
||
|
|
guarded_stop_in(clp);
|
||
|
|
clp->out_stop = 1;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (res < blocks * clp->bs) {
|
||
|
|
blocks = res / clp->bs;
|
||
|
|
if ((res % clp->bs) > 0) {
|
||
|
|
blocks++;
|
||
|
|
clp->out_partial++;
|
||
|
|
}
|
||
|
|
rep->num_blks = blocks;
|
||
|
|
}
|
||
|
|
clp->out_done_count -= blocks;
|
||
|
|
}
|
||
|
|
|
||
|
|
void sg_in_operation(Rq_coll * clp, Rq_elem * rep)
|
||
|
|
{
|
||
|
|
int res;
|
||
|
|
int status;
|
||
|
|
|
||
|
|
/* enters holding in_mutex */
|
||
|
|
while (1) {
|
||
|
|
res = sg_start_io(rep);
|
||
|
|
if (1 == res)
|
||
|
|
err_exit(ENOMEM, "sg starting in command");
|
||
|
|
else if (res < 0) {
|
||
|
|
fprintf(stderr, ME "inputting to sg failed, blk=%d\n",
|
||
|
|
rep->blk);
|
||
|
|
status = pthread_mutex_unlock(&clp->in_mutex);
|
||
|
|
if (0 != status)
|
||
|
|
err_exit(status, "unlock in_mutex");
|
||
|
|
guarded_stop_both(clp);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
/* Now release in mutex to let other reads run in parallel */
|
||
|
|
status = pthread_mutex_unlock(&clp->in_mutex);
|
||
|
|
if (0 != status)
|
||
|
|
err_exit(status, "unlock in_mutex");
|
||
|
|
|
||
|
|
res = sg_finish_io(rep->wr, rep, &clp->aux_mutex);
|
||
|
|
if (res < 0) {
|
||
|
|
if (clp->coe) {
|
||
|
|
memset(rep->buffp, 0, rep->num_blks * rep->bs);
|
||
|
|
fprintf(stderr,
|
||
|
|
">> substituted zeros for in blk=%d for "
|
||
|
|
"%d bytes\n", rep->blk,
|
||
|
|
rep->num_blks * rep->bs);
|
||
|
|
} else {
|
||
|
|
fprintf(stderr,
|
||
|
|
"error finishing sg in command\n");
|
||
|
|
guarded_stop_both(clp);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (res <= 0) { /* looks good, going to return */
|
||
|
|
if (rep->dio_incomplete || rep->resid) {
|
||
|
|
status = pthread_mutex_lock(&clp->aux_mutex);
|
||
|
|
if (0 != status)
|
||
|
|
err_exit(status, "lock aux_mutex");
|
||
|
|
clp->dio_incomplete += rep->dio_incomplete;
|
||
|
|
clp->sum_of_resids += rep->resid;
|
||
|
|
status = pthread_mutex_unlock(&clp->aux_mutex);
|
||
|
|
if (0 != status)
|
||
|
|
err_exit(status, "unlock aux_mutex");
|
||
|
|
}
|
||
|
|
status = pthread_mutex_lock(&clp->in_mutex);
|
||
|
|
if (0 != status)
|
||
|
|
err_exit(status, "lock in_mutex");
|
||
|
|
clp->in_done_count -= rep->num_blks;
|
||
|
|
status = pthread_mutex_unlock(&clp->in_mutex);
|
||
|
|
if (0 != status)
|
||
|
|
err_exit(status, "unlock in_mutex");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
/* else assume 1 == res so try again with same addr, count info */
|
||
|
|
/* now re-acquire read mutex for balance */
|
||
|
|
/* N.B. This re-read could now be out of read sequence */
|
||
|
|
status = pthread_mutex_lock(&clp->in_mutex);
|
||
|
|
if (0 != status)
|
||
|
|
err_exit(status, "lock in_mutex");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void sg_out_operation(Rq_coll * clp, Rq_elem * rep)
|
||
|
|
{
|
||
|
|
int res;
|
||
|
|
int status;
|
||
|
|
|
||
|
|
/* enters holding out_mutex */
|
||
|
|
while (1) {
|
||
|
|
res = sg_start_io(rep);
|
||
|
|
if (1 == res)
|
||
|
|
err_exit(ENOMEM, "sg starting out command");
|
||
|
|
else if (res < 0) {
|
||
|
|
fprintf(stderr,
|
||
|
|
ME "outputting from sg failed, blk=%d\n",
|
||
|
|
rep->blk);
|
||
|
|
status = pthread_mutex_unlock(&clp->out_mutex);
|
||
|
|
if (0 != status)
|
||
|
|
err_exit(status, "unlock out_mutex");
|
||
|
|
guarded_stop_both(clp);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
/* Now release in mutex to let other reads run in parallel */
|
||
|
|
status = pthread_mutex_unlock(&clp->out_mutex);
|
||
|
|
if (0 != status)
|
||
|
|
err_exit(status, "unlock out_mutex");
|
||
|
|
|
||
|
|
res = sg_finish_io(rep->wr, rep, &clp->aux_mutex);
|
||
|
|
if (res < 0) {
|
||
|
|
if (clp->coe)
|
||
|
|
fprintf(stderr,
|
||
|
|
">> ignored error for out blk=%d for "
|
||
|
|
"%d bytes\n", rep->blk,
|
||
|
|
rep->num_blks * rep->bs);
|
||
|
|
else {
|
||
|
|
fprintf(stderr,
|
||
|
|
"error finishing sg out command\n");
|
||
|
|
guarded_stop_both(clp);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (res <= 0) {
|
||
|
|
if (rep->dio_incomplete || rep->resid) {
|
||
|
|
status = pthread_mutex_lock(&clp->aux_mutex);
|
||
|
|
if (0 != status)
|
||
|
|
err_exit(status, "lock aux_mutex");
|
||
|
|
clp->dio_incomplete += rep->dio_incomplete;
|
||
|
|
clp->sum_of_resids += rep->resid;
|
||
|
|
status = pthread_mutex_unlock(&clp->aux_mutex);
|
||
|
|
if (0 != status)
|
||
|
|
err_exit(status, "unlock aux_mutex");
|
||
|
|
}
|
||
|
|
status = pthread_mutex_lock(&clp->out_mutex);
|
||
|
|
if (0 != status)
|
||
|
|
err_exit(status, "lock out_mutex");
|
||
|
|
clp->out_done_count -= rep->num_blks;
|
||
|
|
status = pthread_mutex_unlock(&clp->out_mutex);
|
||
|
|
if (0 != status)
|
||
|
|
err_exit(status, "unlock out_mutex");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
/* else assume 1 == res so try again with same addr, count info */
|
||
|
|
/* now re-acquire out mutex for balance */
|
||
|
|
/* N.B. This re-write could now be out of write sequence */
|
||
|
|
status = pthread_mutex_lock(&clp->out_mutex);
|
||
|
|
if (0 != status)
|
||
|
|
err_exit(status, "lock out_mutex");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
int sg_start_io(Rq_elem * rep)
|
||
|
|
{
|
||
|
|
sg_io_hdr_t *hp = &rep->io_hdr;
|
||
|
|
int fua = rep->wr ? (rep->fua_mode & 1) : (rep->fua_mode & 2);
|
||
|
|
int res;
|
||
|
|
|
||
|
|
if (sg_build_scsi_cdb(rep->cmd, rep->cdbsz, rep->num_blks, rep->blk,
|
||
|
|
rep->wr, fua, 0)) {
|
||
|
|
fprintf(stderr, ME "bad cdb build, start_blk=%d, blocks=%d\n",
|
||
|
|
rep->blk, rep->num_blks);
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
memset(hp, 0, sizeof(sg_io_hdr_t));
|
||
|
|
hp->interface_id = 'S';
|
||
|
|
hp->cmd_len = rep->cdbsz;
|
||
|
|
hp->cmdp = rep->cmd;
|
||
|
|
hp->dxfer_direction = rep->wr ? SG_DXFER_TO_DEV : SG_DXFER_FROM_DEV;
|
||
|
|
hp->dxfer_len = rep->bs * rep->num_blks;
|
||
|
|
hp->dxferp = rep->buffp;
|
||
|
|
hp->mx_sb_len = sizeof(rep->sb);
|
||
|
|
hp->sbp = rep->sb;
|
||
|
|
hp->timeout = DEF_TIMEOUT;
|
||
|
|
hp->usr_ptr = rep;
|
||
|
|
hp->pack_id = rep->blk;
|
||
|
|
if (rep->dio)
|
||
|
|
hp->flags |= SG_FLAG_DIRECT_IO;
|
||
|
|
if (rep->debug > 8) {
|
||
|
|
fprintf(stderr, "sg_start_io: SCSI %s, blk=%d num_blks=%d\n",
|
||
|
|
rep->wr ? "WRITE" : "READ", rep->blk, rep->num_blks);
|
||
|
|
sg_print_command(hp->cmdp);
|
||
|
|
fprintf(stderr, "dir=%d, len=%d, dxfrp=%p, cmd_len=%d\n",
|
||
|
|
hp->dxfer_direction, hp->dxfer_len, hp->dxferp,
|
||
|
|
hp->cmd_len);
|
||
|
|
}
|
||
|
|
|
||
|
|
while (((res = write(rep->wr ? rep->outfd : rep->infd, hp,
|
||
|
|
sizeof(sg_io_hdr_t))) < 0) && (EINTR == errno)) ;
|
||
|
|
if (res < 0) {
|
||
|
|
if (ENOMEM == errno)
|
||
|
|
return 1;
|
||
|
|
perror("starting io on sg device, error");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* -1 -> unrecoverable error, 0 -> successful, 1 -> try again */
|
||
|
|
int sg_finish_io(int wr, Rq_elem * rep, pthread_mutex_t * a_mutp)
|
||
|
|
{
|
||
|
|
int res, status;
|
||
|
|
sg_io_hdr_t io_hdr;
|
||
|
|
sg_io_hdr_t *hp;
|
||
|
|
#if 0
|
||
|
|
static int testing = 0; /* thread dubious! */
|
||
|
|
#endif
|
||
|
|
|
||
|
|
memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
|
||
|
|
/* FORCE_PACK_ID active set only read packet with matching pack_id */
|
||
|
|
io_hdr.interface_id = 'S';
|
||
|
|
io_hdr.dxfer_direction = rep->wr ? SG_DXFER_TO_DEV : SG_DXFER_FROM_DEV;
|
||
|
|
io_hdr.pack_id = rep->blk;
|
||
|
|
|
||
|
|
while (((res = read(wr ? rep->outfd : rep->infd, &io_hdr,
|
||
|
|
sizeof(sg_io_hdr_t))) < 0) && (EINTR == errno)) ;
|
||
|
|
if (res < 0) {
|
||
|
|
perror("finishing io on sg device, error");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
if (rep != (Rq_elem *) io_hdr.usr_ptr)
|
||
|
|
err_exit(0,
|
||
|
|
"sg_finish_io: bad usr_ptr, request-response mismatch\n");
|
||
|
|
memcpy(&rep->io_hdr, &io_hdr, sizeof(sg_io_hdr_t));
|
||
|
|
hp = &rep->io_hdr;
|
||
|
|
|
||
|
|
switch (sg_err_category3(hp)) {
|
||
|
|
case SG_ERR_CAT_CLEAN:
|
||
|
|
break;
|
||
|
|
case SG_ERR_CAT_RECOVERED:
|
||
|
|
fprintf(stderr, "Recovered error on block=%d, num=%d\n",
|
||
|
|
rep->blk, rep->num_blks);
|
||
|
|
break;
|
||
|
|
case SG_ERR_CAT_MEDIA_CHANGED:
|
||
|
|
return 1;
|
||
|
|
default:
|
||
|
|
{
|
||
|
|
char ebuff[EBUFF_SZ];
|
||
|
|
|
||
|
|
snprintf(ebuff, EBUFF_SZ,
|
||
|
|
"%s blk=%d", rep->wr ? "writing" : "reading",
|
||
|
|
rep->blk);
|
||
|
|
status = pthread_mutex_lock(a_mutp);
|
||
|
|
if (0 != status)
|
||
|
|
err_exit(status, "lock aux_mutex");
|
||
|
|
sg_chk_n_print3(ebuff, hp);
|
||
|
|
status = pthread_mutex_unlock(a_mutp);
|
||
|
|
if (0 != status)
|
||
|
|
err_exit(status, "unlock aux_mutex");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
#if 0
|
||
|
|
if (0 == (++testing % 100))
|
||
|
|
return -1;
|
||
|
|
#endif
|
||
|
|
if (rep->dio &&
|
||
|
|
((hp->info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO))
|
||
|
|
rep->dio_incomplete = 1; /* count dios done as indirect IO */
|
||
|
|
else
|
||
|
|
rep->dio_incomplete = 0;
|
||
|
|
rep->resid = hp->resid;
|
||
|
|
if (rep->debug > 8)
|
||
|
|
fprintf(stderr, "sg_finish_io: completed %s\n",
|
||
|
|
wr ? "WRITE" : "READ");
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
int sg_prepare(int fd, int bs, int bpt, int *scsi_typep)
|
||
|
|
{
|
||
|
|
int res, t;
|
||
|
|
|
||
|
|
res = ioctl(fd, SG_GET_VERSION_NUM, &t);
|
||
|
|
if ((res < 0) || (t < 30000)) {
|
||
|
|
fprintf(stderr, ME "sg driver prior to 3.x.y\n");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
res = 0;
|
||
|
|
t = bs * bpt;
|
||
|
|
res = ioctl(fd, SG_SET_RESERVED_SIZE, &t);
|
||
|
|
if (res < 0)
|
||
|
|
perror(ME "SG_SET_RESERVED_SIZE error");
|
||
|
|
t = 1;
|
||
|
|
res = ioctl(fd, SG_SET_FORCE_PACK_ID, &t);
|
||
|
|
if (res < 0)
|
||
|
|
perror(ME "SG_SET_FORCE_PACK_ID error");
|
||
|
|
if (scsi_typep) {
|
||
|
|
struct sg_scsi_id info;
|
||
|
|
|
||
|
|
res = ioctl(fd, SG_GET_SCSI_ID, &info);
|
||
|
|
if (res < 0)
|
||
|
|
perror(ME "SG_SET_SCSI_ID error");
|
||
|
|
*scsi_typep = info.scsi_type;
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
int do_scsi_sgp_read_write(char *device)
|
||
|
|
{
|
||
|
|
int skip = 0;
|
||
|
|
int seek = 0;
|
||
|
|
int count = -1;
|
||
|
|
char inf[INOUTF_SZ];
|
||
|
|
char outf[INOUTF_SZ];
|
||
|
|
int res, k;
|
||
|
|
int in_num_sect = 0;
|
||
|
|
int out_num_sect = 0;
|
||
|
|
int num_threads = DEF_NUM_THREADS;
|
||
|
|
pthread_t threads[MAX_NUM_THREADS];
|
||
|
|
int do_time = 1;
|
||
|
|
int do_sync = 1;
|
||
|
|
int in_sect_sz, out_sect_sz, status, infull, outfull;
|
||
|
|
void *vp;
|
||
|
|
char ebuff[EBUFF_SZ];
|
||
|
|
struct timeval start_tm, end_tm;
|
||
|
|
Rq_coll rcoll;
|
||
|
|
|
||
|
|
print_msg(TEST_BREAK, __FUNCTION__);
|
||
|
|
|
||
|
|
memset(&rcoll, 0, sizeof(Rq_coll));
|
||
|
|
rcoll.bpt = DEF_BLOCKS_PER_TRANSFER;
|
||
|
|
rcoll.in_type = FT_OTHER;
|
||
|
|
rcoll.out_type = FT_OTHER;
|
||
|
|
rcoll.cdbsz = DEF_SCSI_CDBSZ;
|
||
|
|
|
||
|
|
strcpy(inf, "/dev/zero");
|
||
|
|
strcpy(outf, device);
|
||
|
|
|
||
|
|
if (rcoll.bs <= 0) {
|
||
|
|
rcoll.bs = DEF_BLOCK_SIZE;
|
||
|
|
fprintf(stderr,
|
||
|
|
"Assume default 'bs' (block size) of %d bytes\n",
|
||
|
|
rcoll.bs);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (rcoll.debug)
|
||
|
|
fprintf(stderr, ME "if=%s skip=%d of=%s seek=%d count=%d\n",
|
||
|
|
inf, skip, outf, seek, count);
|
||
|
|
|
||
|
|
rcoll.infd = STDIN_FILENO;
|
||
|
|
rcoll.outfd = STDOUT_FILENO;
|
||
|
|
if (inf[0] && ('-' != inf[0])) {
|
||
|
|
rcoll.in_type = dd_filetype(inf);
|
||
|
|
|
||
|
|
if (FT_ST == rcoll.in_type) {
|
||
|
|
fprintf(stderr,
|
||
|
|
ME "unable to use scsi tape device %s\n", inf);
|
||
|
|
return 1;
|
||
|
|
} else if (FT_SG == rcoll.in_type) {
|
||
|
|
if ((rcoll.infd = open(inf, O_RDWR)) < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ,
|
||
|
|
ME "could not open %s for sg reading",
|
||
|
|
inf);
|
||
|
|
perror(ebuff);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
if (sg_prepare(rcoll.infd, rcoll.bs, rcoll.bpt,
|
||
|
|
&rcoll.in_scsi_type))
|
||
|
|
return 1;
|
||
|
|
} else {
|
||
|
|
if ((rcoll.infd = open(inf, O_RDONLY)) < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ,
|
||
|
|
ME "could not open %s for reading",
|
||
|
|
inf);
|
||
|
|
perror(ebuff);
|
||
|
|
return 1;
|
||
|
|
} else if (skip > 0) {
|
||
|
|
llse_loff_t offset = skip;
|
||
|
|
|
||
|
|
offset *= rcoll.bs; /* could exceed 32 here! */
|
||
|
|
if (llse_llseek(rcoll.infd, offset, SEEK_SET) <
|
||
|
|
0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ,
|
||
|
|
ME
|
||
|
|
"couldn't skip to required position on %s",
|
||
|
|
inf);
|
||
|
|
perror(ebuff);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (outf[0] && ('-' != outf[0])) {
|
||
|
|
rcoll.out_type = dd_filetype(outf);
|
||
|
|
|
||
|
|
if (FT_ST == rcoll.out_type) {
|
||
|
|
fprintf(stderr,
|
||
|
|
ME "unable to use scsi tape device %s\n", outf);
|
||
|
|
return 1;
|
||
|
|
} else if (FT_SG == rcoll.out_type) {
|
||
|
|
if ((rcoll.outfd = open(outf, O_RDWR)) < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ,
|
||
|
|
ME "could not open %s for sg writing",
|
||
|
|
outf);
|
||
|
|
perror(ebuff);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (sg_prepare(rcoll.outfd, rcoll.bs, rcoll.bpt,
|
||
|
|
&rcoll.out_scsi_type))
|
||
|
|
return 1;
|
||
|
|
} else if (FT_DEV_NULL == rcoll.out_type)
|
||
|
|
rcoll.outfd = -1; /* don't bother opening */
|
||
|
|
else {
|
||
|
|
if (FT_RAW != rcoll.out_type) {
|
||
|
|
if ((rcoll.outfd =
|
||
|
|
open(outf, O_WRONLY | O_CREAT,
|
||
|
|
0666)) < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ,
|
||
|
|
ME
|
||
|
|
"could not open %s for writing",
|
||
|
|
outf);
|
||
|
|
perror(ebuff);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
if ((rcoll.outfd = open(outf, O_WRONLY)) < 0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ,
|
||
|
|
ME
|
||
|
|
"could not open %s for raw writing",
|
||
|
|
outf);
|
||
|
|
perror(ebuff);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (seek > 0) {
|
||
|
|
llse_loff_t offset = seek;
|
||
|
|
|
||
|
|
offset *= rcoll.bs; /* could exceed 32 bits here! */
|
||
|
|
if (llse_llseek(rcoll.outfd, offset, SEEK_SET) <
|
||
|
|
0) {
|
||
|
|
snprintf(ebuff, EBUFF_SZ,
|
||
|
|
ME
|
||
|
|
"couldn't seek to required position on %s",
|
||
|
|
outf);
|
||
|
|
perror(ebuff);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if ((STDIN_FILENO == rcoll.infd) && (STDOUT_FILENO == rcoll.outfd)) {
|
||
|
|
fprintf(stderr,
|
||
|
|
"Disallow both if and of to be stdin and stdout");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
if (count < 0) {
|
||
|
|
if (FT_SG == rcoll.in_type) {
|
||
|
|
res =
|
||
|
|
read_capacity(rcoll.infd, &in_num_sect,
|
||
|
|
&in_sect_sz);
|
||
|
|
if (2 == res) {
|
||
|
|
fprintf(stderr,
|
||
|
|
"Unit attention, media changed(in), continuing\n");
|
||
|
|
res =
|
||
|
|
read_capacity(rcoll.infd, &in_num_sect,
|
||
|
|
&in_sect_sz);
|
||
|
|
}
|
||
|
|
if (0 != res) {
|
||
|
|
fprintf(stderr,
|
||
|
|
"Unable to read capacity on %s\n", inf);
|
||
|
|
in_num_sect = -1;
|
||
|
|
} else {
|
||
|
|
if (in_num_sect > skip)
|
||
|
|
in_num_sect -= skip;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (FT_SG == rcoll.out_type) {
|
||
|
|
res =
|
||
|
|
read_capacity(rcoll.outfd, &out_num_sect,
|
||
|
|
&out_sect_sz);
|
||
|
|
if (2 == res) {
|
||
|
|
fprintf(stderr,
|
||
|
|
"Unit attention, media changed(out), continuing\n");
|
||
|
|
res =
|
||
|
|
read_capacity(rcoll.outfd, &out_num_sect,
|
||
|
|
&out_sect_sz);
|
||
|
|
}
|
||
|
|
if (0 != res) {
|
||
|
|
fprintf(stderr,
|
||
|
|
"Unable to read capacity on %s\n",
|
||
|
|
outf);
|
||
|
|
out_num_sect = -1;
|
||
|
|
} else {
|
||
|
|
if (out_num_sect > seek)
|
||
|
|
out_num_sect -= seek;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (in_num_sect > 0) {
|
||
|
|
if (out_num_sect > 0)
|
||
|
|
count =
|
||
|
|
(in_num_sect >
|
||
|
|
out_num_sect) ? out_num_sect : in_num_sect;
|
||
|
|
else
|
||
|
|
count = in_num_sect;
|
||
|
|
} else
|
||
|
|
count = out_num_sect;
|
||
|
|
}
|
||
|
|
if (rcoll.debug > 1)
|
||
|
|
fprintf(stderr, "Start of loop, count=%d, in_num_sect=%d, "
|
||
|
|
"out_num_sect=%d\n", count, in_num_sect, out_num_sect);
|
||
|
|
if (count < 0) {
|
||
|
|
fprintf(stderr, "Couldn't calculate count, please give one\n");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
rcoll.in_count = count;
|
||
|
|
rcoll.in_done_count = count;
|
||
|
|
rcoll.skip = skip;
|
||
|
|
rcoll.in_blk = skip;
|
||
|
|
rcoll.out_count = count;
|
||
|
|
rcoll.out_done_count = count;
|
||
|
|
rcoll.seek = seek;
|
||
|
|
rcoll.out_blk = seek;
|
||
|
|
status = pthread_mutex_init(&rcoll.in_mutex, NULL);
|
||
|
|
if (0 != status)
|
||
|
|
err_exit(status, "init in_mutex");
|
||
|
|
status = pthread_mutex_init(&rcoll.out_mutex, NULL);
|
||
|
|
if (0 != status)
|
||
|
|
err_exit(status, "init out_mutex");
|
||
|
|
status = pthread_mutex_init(&rcoll.aux_mutex, NULL);
|
||
|
|
if (0 != status)
|
||
|
|
err_exit(status, "init aux_mutex");
|
||
|
|
status = pthread_cond_init(&rcoll.out_sync_cv, NULL);
|
||
|
|
if (0 != status)
|
||
|
|
err_exit(status, "init out_sync_cv");
|
||
|
|
|
||
|
|
sigemptyset(&signal_set);
|
||
|
|
sigaddset(&signal_set, SIGINT);
|
||
|
|
status = pthread_sigmask(SIG_BLOCK, &signal_set, NULL);
|
||
|
|
if (0 != status)
|
||
|
|
err_exit(status, "pthread_sigmask");
|
||
|
|
status = pthread_create(&sig_listen_thread_id, NULL,
|
||
|
|
sig_listen_thread, (void *)&rcoll);
|
||
|
|
if (0 != status)
|
||
|
|
err_exit(status, "pthread_create, sig...");
|
||
|
|
|
||
|
|
if (do_time) {
|
||
|
|
start_tm.tv_sec = 0;
|
||
|
|
start_tm.tv_usec = 0;
|
||
|
|
gettimeofday(&start_tm, NULL);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* vvvvvvvvvvv Start worker threads vvvvvvvvvvvvvvvvvvvvvvvv */
|
||
|
|
if ((rcoll.out_done_count > 0) && (num_threads > 0)) {
|
||
|
|
/* Run 1 work thread to shake down infant retryable stuff */
|
||
|
|
status = pthread_mutex_lock(&rcoll.out_mutex);
|
||
|
|
if (0 != status)
|
||
|
|
err_exit(status, "lock out_mutex");
|
||
|
|
status = pthread_create(&threads[0], NULL, read_write_thread,
|
||
|
|
(void *)&rcoll);
|
||
|
|
if (0 != status)
|
||
|
|
err_exit(status, "pthread_create");
|
||
|
|
if (rcoll.debug)
|
||
|
|
fprintf(stderr, "Starting worker thread k=0\n");
|
||
|
|
|
||
|
|
/* wait for any broadcast */
|
||
|
|
pthread_cleanup_push(cleanup_out, (void *)&rcoll);
|
||
|
|
status =
|
||
|
|
pthread_cond_wait(&rcoll.out_sync_cv, &rcoll.out_mutex);
|
||
|
|
if (0 != status)
|
||
|
|
err_exit(status, "cond out_sync_cv");
|
||
|
|
pthread_cleanup_pop(0);
|
||
|
|
status = pthread_mutex_unlock(&rcoll.out_mutex);
|
||
|
|
if (0 != status)
|
||
|
|
err_exit(status, "unlock out_mutex");
|
||
|
|
|
||
|
|
/* now start the rest of the threads */
|
||
|
|
for (k = 1; k < num_threads; ++k) {
|
||
|
|
status =
|
||
|
|
pthread_create(&threads[k], NULL, read_write_thread,
|
||
|
|
(void *)&rcoll);
|
||
|
|
if (0 != status)
|
||
|
|
err_exit(status, "pthread_create");
|
||
|
|
if (rcoll.debug)
|
||
|
|
fprintf(stderr, "Starting worker thread k=%d\n",
|
||
|
|
k);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* now wait for worker threads to finish */
|
||
|
|
for (k = 0; k < num_threads; ++k) {
|
||
|
|
status = pthread_join(threads[k], &vp);
|
||
|
|
if (0 != status)
|
||
|
|
err_exit(status, "pthread_join");
|
||
|
|
if (rcoll.debug)
|
||
|
|
fprintf(stderr,
|
||
|
|
"Worker thread k=%d terminated\n", k);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if ((do_time) && (start_tm.tv_sec || start_tm.tv_usec)) {
|
||
|
|
struct timeval res_tm;
|
||
|
|
double a, b;
|
||
|
|
|
||
|
|
gettimeofday(&end_tm, NULL);
|
||
|
|
res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec;
|
||
|
|
res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec;
|
||
|
|
if (res_tm.tv_usec < 0) {
|
||
|
|
--res_tm.tv_sec;
|
||
|
|
res_tm.tv_usec += 1000000;
|
||
|
|
}
|
||
|
|
a = res_tm.tv_sec;
|
||
|
|
a += (0.000001 * res_tm.tv_usec);
|
||
|
|
b = (double)rcoll.bs * (count - rcoll.out_done_count);
|
||
|
|
printf("time to transfer data was %d.%06d secs",
|
||
|
|
(int)res_tm.tv_sec, (int)res_tm.tv_usec);
|
||
|
|
if ((a > 0.00001) && (b > 511))
|
||
|
|
printf(", %.2f MB/sec\n", b / (a * 1000000.0));
|
||
|
|
else
|
||
|
|
printf("\n");
|
||
|
|
}
|
||
|
|
if (do_sync) {
|
||
|
|
if (FT_SG == rcoll.out_type) {
|
||
|
|
fprintf(stderr, ">> Synchronizing cache on %s\n", outf);
|
||
|
|
res = sync_cache(rcoll.outfd);
|
||
|
|
if (2 == res) {
|
||
|
|
fprintf(stderr,
|
||
|
|
"Unit attention, media changed(in), continuing\n");
|
||
|
|
res = sync_cache(rcoll.outfd);
|
||
|
|
}
|
||
|
|
if (0 != res)
|
||
|
|
fprintf(stderr,
|
||
|
|
"Unable to synchronize cache\n");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
status = pthread_cancel(sig_listen_thread_id);
|
||
|
|
if (0 != status)
|
||
|
|
err_exit(status, "pthread_cancel");
|
||
|
|
if (STDIN_FILENO != rcoll.infd)
|
||
|
|
close(rcoll.infd);
|
||
|
|
if ((STDOUT_FILENO != rcoll.outfd) && (FT_DEV_NULL != rcoll.out_type))
|
||
|
|
close(rcoll.outfd);
|
||
|
|
res = 0;
|
||
|
|
if (0 != rcoll.out_count) {
|
||
|
|
fprintf(stderr,
|
||
|
|
">>>> Some error occurred, remaining blocks=%d\n",
|
||
|
|
rcoll.out_count);
|
||
|
|
res = 2;
|
||
|
|
}
|
||
|
|
infull = count - rcoll.in_done_count - rcoll.in_partial;
|
||
|
|
fprintf(stderr, "%d+%d records in\n", infull, rcoll.in_partial);
|
||
|
|
outfull = count - rcoll.out_done_count - rcoll.out_partial;
|
||
|
|
fprintf(stderr, "%d+%d records out\n", outfull, rcoll.out_partial);
|
||
|
|
if (rcoll.dio_incomplete) {
|
||
|
|
int fd;
|
||
|
|
char c;
|
||
|
|
|
||
|
|
fprintf(stderr,
|
||
|
|
">> Direct IO requested but incomplete %d times\n",
|
||
|
|
rcoll.dio_incomplete);
|
||
|
|
if ((fd = open(proc_allow_dio, O_RDONLY)) >= 0) {
|
||
|
|
if (1 == read(fd, &c, 1)) {
|
||
|
|
if ('0' == c)
|
||
|
|
fprintf(stderr,
|
||
|
|
">>> %s set to '0' but should be set "
|
||
|
|
"to '1' for direct IO\n",
|
||
|
|
proc_allow_dio);
|
||
|
|
}
|
||
|
|
close(fd);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (rcoll.sum_of_resids)
|
||
|
|
fprintf(stderr, ">> Non-zero sum of residual counts=%d\n",
|
||
|
|
rcoll.sum_of_resids);
|
||
|
|
return res;
|
||
|
|
}
|