blob: 040cf02bd86bb91ac089e4da93b7f674e1de4ad9 [file] [log] [blame]
/* * This program reads various mode pages and bits of other
* information from a scsi device and interprets the raw data for you
* with a report written to stdout. Usage:
*
* ./sginfo [options] /dev/sda2
*
* Options are:
* -c Display information from Cache control page.
* -C Display information from Control Page.
* -d Display defect lists.
* -Farg Defect list format (-Flogical, -Fphysical, -Findex)
* -e Display information from error recovery page.
* -f Display information from Format Device Page.
* -g Display information from Hard disk geometry page.
* -i Display all information from Inquiry command.
* -s Display all information from unit serial number page.
* -D Display information from disconnect-reconnect page.
* -n Display information from notch parameters page.
* -p Display information from Peripheral Device Page.
* -V Display information from Verify Error Recovery Page.
* -uno Display info from page number no.
* -v Show version number
* -a All of the above.
* -l List known scsi devices on the system
* -L List pages supported by program and target
*
* Only one of the following three options can be specified.
* None of these three implies the current values are returned.
* -m Display modifiable fields instead of current values
* -M Display manufacturer defaults instead of current values
* -S Display saved defaults instead of current values
*
* -X Display output suitable for the X-based interface.
* -R Replace parameters - best used with -X
*
* Compile with command: gcc -O2 -o sginfo sginfo.c sg_err.c
* You need to be able to open the scsi device as a file.
* Depending on the permissions, you may need root privileges
* to run this.
*
* Eric Youngdale - 11/1/93. Version 1.0.
*
* Version 1.1: Ability to change parameters on cache page, support for
* X front end.
*
* 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 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Michael Weller (eowmob@exp-math.uni-essen.de)
* 11/23/94 massive extensions from 1.4a
* 08/23/97 fix problems with defect lists
*
* Douglas Gilbert (dgilbert@interlog.com)
* 990628 port to sg .... (version 1.81)
* up 4KB limit on defect list to 32KB
* 'sginfo -l' also shows sg devices and mapping to other
* scsi devices
* 'sginfo' commands can take either an sd, sr (scd), st
* or an sg device (all non-sg devices converted to a
* sg device)
*
* 001208 Add Kurt Garloff's "-uno" flag for displaying info
* from a page number. <garloff@suse.de> [version 1.90]
*
* Kurt Garloff <garloff@suse.de>
* 20000715 allow displaying and modification of vendor specific pages
* (unformatted - @ hexdatafield)
* accept vendor lengths for those pages
* enabled page saving
* cleaned parameter parsing a bit (it's still a terrible mess!)
* Use sr (instead of scd) and sg%d (instead of sga,b,...) in -l
* and support much more devs in -l (incl. nosst)
* Fix segfault in defect list (len=0xffff) and adapt formatting
* to large disks. Support up to 256kB defect lists with
* 0xB7 (12byte) command if necessary and fallback to 0x37
* (10byte) in case of failure. Report truncation.
* sizeof(buffer) (which is sizeof(char*) == 4 or 32 bit archs)
* was used incorrectly all over the place. Fixed.
* [version 1.95]
* Douglas Gilbert (dgilbert@interlog.com)
* 20020113 snprintf() type cleanup [version 1.96]
* 20021211 correct sginfo MODE_SELECT, protect against block devices
* that answer sg's ioctls. [version 1.97]
* 20021228 scan for some "scd<n>" as well as "sr<n>" device names [1.98]
*/
#include <stdio.h>
#include <string.h>
#include <getopt.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "sg_include.h"
#ifdef SG_GET_RESERVED_SIZE
#include "sg_err.h"
#else
#include <scsi/scsi.h>
static const unsigned char scsi_command_size[8] = { 6, 10, 10, 12,
12, 12, 10, 10 };
#define COMMAND_SIZE(opcode) scsi_command_size[((opcode) >> 5) & 7]
static int sg_get_command_size(unsigned char opcode)
{
return COMMAND_SIZE(opcode);
}
#endif
#define SG_HSZ sizeof(struct sg_header)
#define OFF (SG_HSZ - (2 * sizeof(int)))
/* #define SG_IO_DEBUG 1 */
static int glob_fd;
static char *device_name;
#define SIZEOF_BUFFER (256*1024)
#define SIZEOF_BUFFER1 (16*1024)
static unsigned char buff_a[SIZEOF_BUFFER + SG_HSZ + 12];
static unsigned char buff_b[SIZEOF_BUFFER1 + SG_HSZ + 12];
static unsigned char * buffer = buff_a + OFF;
static unsigned char * buffer1 = buff_b + OFF;
static char cache = 0;
static char defect = 0;
static char geometry = 0;
static char format = 0;
static char error = 0;
static char disconnect = 0;
static char control = 0;
static char inquiry = 0;
static char serial_number = 0;
static char default_param = 0;
static char modifiable = 0;
static char saved = 0;
static char x_interface = 0;
static char replace = 0;
static char notch = 0;
static char list = 0;
static char list_pages = 0;
static char verify = 0;
static char peripheral = 0;
static char defectformat = 0x4;
static char save_mode = 0; /* All mode pages output only a single line, and
preceed the output with the appropriate
restore command */
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 *))
#if 0
static char *log_names[] =
{
"Supported Log Pages ",
"Buffer Over-Run/Under-Run ",
"Error Counter Write ",
"Error Counter Read ",
"Error Counter Read Reverse ",
"Error Counter Verify ",
"Non-Medium Error ",
"Last n Error Events "};
#define MAX_LOGNO (sizeof(log_names)/sizeof(char *))
#endif
#define MAXPARM 32
static int next_parameter;
static int n_replacement_values;
static unsigned long long replacement_values[MAXPARM];
static char is_hex[MAXPARM];
/* These are the mode pages for direct access devices (i.e. disks).
* Asterisks mark those which this program can currently read and interpret.
* Section 7 is generic scsi, section 8 is hard disk, 13 is cdrom and 9 is
* tape.
* 01h Read-Write Error Recovery Page 8.3.3.6
* 02h Disconnect-Reconnect Page 7.3.3.2
* 03h Format Device Page 8.3.3.3
* 04h Rigid Disk Geometry Page 8.3.3.7
05h Flexible Disk Page 8.3.3.2
* 07h Verify Error Recovery Page 8.3.3.8
* 08h Caching Page 8.3.3.1
* 09h Peripheral Device Page 7.3.3.3
* 0Ah Control Mode Page 7.3.3.1
0Bh Medium Types Supported Page 8.3.3.4
* 0Ch Notch and Partition Page 8.3.3.5
0Dh CD-ROM Page 13.3.3.2
0Eh CD-ROM Audio Control Page 13.3.3.1
10h Device Configuration Page 9.3.3.1
11h Medium Partition Page(1) 9.3.3.2
12h Medium Partition Page(2) 9.3.3.3
13h Medium Partition Page(3) 9.3.3.3
14h Medium Partition Page(4) 9.3.3.3
*/
#define MODE_SENSE 0x1a
#define MODE_SENSE_10 0x5a
#define MODE_SELECT 0x15
#define LOG_SENSE 0x4d
#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; \
if(x_interface && replace) { \
if((NPARAM && n_replacement_values != NPARAM) || \
(!NPARAM && n_replacement_values != pagestart[1])) { \
fprintf(stdout,"Wrong number of replacement values (%i instead of %i)\n", n_replacement_values, \
NPARAM? NPARAM: pagestart[1]); \
return 0; \
}; \
next_parameter = 1; \
};
/* forward declaration */
static void usage(char *, char quiet);
/* 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];
}
#if 0
static char *get_log_name(int pageno)
{
if ((pageno >= MAX_LOGNO) || (!log_names[pageno]))
return "Unknown ";
return log_names[pageno];
}
#endif
static void dump(void *buffer, unsigned int length)
{
unsigned int i;
for (i = 0; i < length; i++) {
#if 0
if (((unsigned char *) buffer)[i] > 0x20)
printf(" %c ", (unsigned int) ((unsigned char *) buffer)[i]);
else
#endif
printf("%02x ", (unsigned int) ((unsigned char *) buffer)[i]);
if (i % 16 == 15) {
printf("\n");
}
}
printf("\n");
}
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 int putnbyte(unsigned char *pnt, unsigned int value,
unsigned int nbyte)
{
int i;
for (i = nbyte - 1; i >= 0; i--) {
pnt[i] = value & 0xff;
value = value >> 8;
}
return 0;
}
#define REASON_SZ 128
static void check_parm_type(int i)
{
char reason[REASON_SZ];
if (i == 1 && is_hex[next_parameter] != 1) {
snprintf(reason, REASON_SZ,
"simple number (pos %i) instead of @ hexdatafield: %llu",
next_parameter, replacement_values[next_parameter]);
usage (reason, 1);
}
if (i != 1 && is_hex[next_parameter]) {
snprintf(reason, REASON_SZ,
"@ hexdatafield (pos %i) instead of a simple number: %llu",
next_parameter, replacement_values[next_parameter]);
usage (reason, 1);
}
}
static void bitfield(unsigned char *pageaddr, char * text, int mask, int shift)
{
if (x_interface && replace) {
check_parm_type(0);
*pageaddr = (*pageaddr & ~(mask << shift)) |
((replacement_values[next_parameter++] & mask) << shift);
} else if (x_interface)
printf("%d ", (*pageaddr >> shift) & mask);
else
printf("%-35s%d\n", text, (*pageaddr >> shift) & mask);
}
static void notbitfield(unsigned char *pageaddr, char * text, int mask,
int shift)
{
if (modifiable) {
bitfield(pageaddr, text, mask, shift);
return;
}
if (x_interface && replace) {
check_parm_type(0);
*pageaddr = (*pageaddr & ~(mask << shift)) |
(((!replacement_values[next_parameter++]) & mask) << shift);
} else if (x_interface)
printf("%d ", !((*pageaddr >> shift) & mask));
else
printf("%-35s%d\n", text, !((*pageaddr >> shift) & mask));
}
static void intfield(unsigned char * pageaddr, int nbytes, char * text)
{
if (x_interface && replace) {
check_parm_type(0);
putnbyte(pageaddr, replacement_values[next_parameter++], nbytes);
} else if (x_interface)
printf("%d ", getnbyte(pageaddr, nbytes));
else
printf("%-35s%d\n", text, getnbyte(pageaddr, nbytes));
}
static void hexfield(unsigned char * pageaddr, int nbytes, char * text)
{
if (x_interface && replace) {
check_parm_type(0);
putnbyte(pageaddr, replacement_values[next_parameter++], nbytes);
} else if (x_interface)
printf("%d ", getnbyte(pageaddr, nbytes));
else
printf("%-35s0x%x\n", text, getnbyte(pageaddr, nbytes));
}
static void hexdatafield(unsigned char * pageaddr, int nbytes, char * text)
{
if (x_interface && replace) {
unsigned char *ptr;
unsigned tmp;
/* Though in main we ensured that a @string has the right format,
we have to check that we are working on a @ hexdata field */
check_parm_type(1);
ptr = (unsigned char *) (unsigned long)
(replacement_values[next_parameter++]);
ptr++; /* Skip @ */
while (*ptr) {
if (!nbytes)
goto illegal;
tmp = (*ptr >= 'a') ? (*ptr - 'a' + 'A') : *ptr;
tmp -= (tmp >= 'A') ? 'A' - 10 : '0';
*pageaddr = tmp << 4;
ptr++;
tmp = (*ptr >= 'a') ? (*ptr - 'a' + 'A') : *ptr;
tmp -= (tmp >= 'A') ? 'A' - 10 : '0';
*pageaddr++ += tmp;
ptr++;
nbytes--;
}
if (nbytes) {
illegal:
fputs("sginfo: incorrect number of bytes in @hexdatafield.\n",
stdout);
exit(2);
}
} else if (x_interface) {
putchar('@');
while (nbytes-- > 0)
printf("%02x", *pageaddr++);
putchar(' ');
} else {
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;
}
#if 0
static int get_log_page(int page, int page_code, char save_values,
unsigned short parameter_ptr)
{
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 buffer */
cmd = (char *) (((int *) buffer) + 2);
cmd[0] = LOG_SENSE; /* LOG SENSE */
cmd[1] = 0x00 | (save_values & 1); /* lun = 0, Parameter pointer
control 0 for full page */
cmd[2] = (page_code << 6) | page;
cmd[3] = 0x00; /* (reserved) */
cmd[4] = 0x00; /* (reserved) */
cmd[5] = parameter_ptr >> 8; /* hi */
cmd[6] = parameter_ptr & 0xff; /* lo */
cmd[7] = 0; /* allocation length hi */
cmd[8] = 0xff; /* allocation length lo */
cmd[9] = 0x00; /* control */
dump(buffer, 200);
status = do_sg_io(glob_fd, buffer);
if (status && (!quiet))
fprintf(stdout, ">>> Unable to read %sLog Page %02xh\n",
get_log_name(page), page);
dump(buffer, 400);
return status;
}
#endif
/* 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 put_mode_page(int page, unsigned char * contents, int page_code)
{
int status;
int pagelen, pagelen1;
unsigned char *cmd;
memset(buffer1, 0, SIZEOF_BUFFER1);
pagelen = contents[3] + 4; /* How many actual bytes we are sending in
the page */
pagelen1 = contents[0] + 1;
*((int *) buffer1) = pagelen1+14; /* length of input data */
*(((int *) buffer1) + 1) = 0; /* length of output buffer */
/* *(((int *) buffer1) + 1) = pagelen1+8; was before .... */
cmd = (unsigned char *) (((int *) buffer1) + 2);
cmd[0] = MODE_SELECT;
cmd[1] = 0x10 | (page_code ? 1 : 0);
cmd[2] = 0x00;
cmd[3] = 0x00; /* (reserved) */
cmd[4] = pagelen1; /* (reserved) */
cmd[5] = 0x00; /* (reserved) */
memcpy(cmd + 6, contents, pagelen1);
cmd[6] = 0; /* Mask off the mode parameter list length
- reserved field */
cmd[8] = 0;
memset(cmd + 6 + 4, 0, 5); /* Mask off reserved fields in block
descriptor */
cmd[6 + pagelen] &= 0x3f; /* Mask off this reserved field in page
code */
//dump (cmd-8, pagelen1+14);
status = do_sg_io(glob_fd, buffer1);
if (status) {
fprintf(stdout, ">>> Unable to store %s Page %02xh\n",
get_page_name(page), page);
dump(cmd-14, pagelen1+14);
}
return status;
}
static int read_geometry(int page_code)
{
int status;
int bdlen;
unsigned char *pagestart;
if (save_mode)
printf("/usr/bin/sginfo -gXR %s ", device_name);
SETUP_MODE_PAGE(4, 9);
if (!x_interface && !replace) {
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");
if (x_interface && replace)
return put_mode_page(4, buffer + 8, 0);
else
printf("\n");
return 0;
}
static int read_disconnect_reconnect_data(int page_code)
{
int status;
int bdlen;
unsigned char *pagestart;
if (save_mode)
printf("/usr/bin/sginfo -DXR %s ", device_name);
SETUP_MODE_PAGE(2, 7);
if (!x_interface && !replace) {
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");
if (x_interface && replace)
return put_mode_page(2, buffer + 8, 0);
else
printf("\n");
return 0;
}
static int read_control_page(int page_code)
{
int status;
int bdlen;
unsigned char *pagestart;
if (save_mode)
printf("/usr/bin/sginfo -CXR %s ", device_name);
SETUP_MODE_PAGE(10, 9);
if (!x_interface && !replace) {
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");
if (x_interface && replace)
return put_mode_page(10, buffer + 8, 0);
else
printf("\n");
return 0;
}
static int error_recovery_page(int page_code)
{
int status;
int bdlen;
unsigned char *pagestart;
if (save_mode)
printf("/usr/bin/sginfo -eXR %s ", device_name);
SETUP_MODE_PAGE(1, 14);
if (!x_interface && !replace) {
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");
if (x_interface && replace)
return put_mode_page(1, buffer + 8, 0);
else
printf("\n");
return 0;
}
static int notch_parameters_page(int page_code)
{
int status;
int bdlen;
unsigned char *pagestart;
if (save_mode)
printf("/usr/bin/sginfo -nXR %s ", device_name);
SETUP_MODE_PAGE(0xc, 7);
if (!x_interface && !replace) {
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");
}
if (x_interface && !replace) {
if (modifiable)
printf("0");
else
printf("0x%8.8x%8.8x", getnbyte(pagestart + 16, 4),
getnbyte(pagestart + 20, 4));
};
if (!x_interface)
printf("Pages Notched %8.8x %8.8x\n",
getnbyte(pagestart + 16, 4), getnbyte(pagestart + 20, 4));
if (x_interface && replace)
return put_mode_page(0xc, buffer + 8, 0);
else
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;
if (save_mode)
printf("/usr/bin/sginfo -cXR %s ", device_name);
SETUP_MODE_PAGE(8, 9);
if (!x_interface && !replace) {
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");
if (x_interface && replace)
return put_mode_page(8, buffer + 8, 0);
else
printf("\n");
return 0;
}
static int read_format_info(int page_code)
{
int status;
int bdlen;
unsigned char *pagestart;
if (save_mode)
printf("/usr/bin/sginfo -fXR %s ", device_name);
SETUP_MODE_PAGE(3, 13);
if (!x_interface && !replace) {
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");
if (x_interface && replace)
return put_mode_page(3, buffer + 8, 0);
else
printf("\n");
return 0;
}
static int verify_error_recovery(int page_code)
{
int status;
int bdlen;
unsigned char *pagestart;
if (save_mode)
printf("/usr/bin/sginfo -VXR %s ", device_name);
SETUP_MODE_PAGE(7, 7);
if (!x_interface && !replace) {
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)");
if (x_interface && replace)
return put_mode_page(7, buffer + 8, 0);
else
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;
if (save_mode)
printf("/usr/bin/sginfo -pXR %s ", device_name);
SETUP_MODE_PAGE(9, 2);
if (!x_interface && !replace) {
printf("Data from Peripheral Device Page\n");
printf("--------------------------------\n");
};
#if 0
dump(pagestart, 20);
pagestart[1] += 2; /*TEST */
buffer[8] += 2; /*TEST */
#endif
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");
if (!x_interface) {
for (ident = 0; ident < 35; ident++)
putchar(' ');
puts(name);
}
hexdatafield(pagestart + 8, bdlen, "Vendor Specific Data");
if (x_interface && replace)
return put_mode_page(9, buffer + 8, 0);
else
printf("\n");
if (x_interface && !save_mode)
puts(name);
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;
if (save_mode)
printf("/usr/bin/sginfo -u %i -XR %s ", page_no, device_name);
SETUP_MODE_PAGE(page_no, 0);
//printf ("Page 0x%02x len: %i\n", page_code, pagestart[1]);
#if 0
dump(pagestart, page_start[1]+2);
pagestart[1] += 2; /*TEST */
buffer[8] += 2; /*TEST */
#endif
name = "Vendor specific";
for (i = 2; i < pagestart[1]+2; i++)
{
char nm[8]; snprintf (nm, 8, "%02x", i);
hexdatafield (pagestart + i, 1, nm);
}
if (x_interface && replace)
return put_mode_page(page_no, buffer + 8, 0);
else
printf("\n");
if (!save_mode)
puts(name);
return 0;
}
/* end */
static int do_inquiry(int page_code)
{
int status, i;
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;
if (!x_interface && !replace) {
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);
if (x_interface)
printf("\n");
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;
}
typedef struct sg_map {
int bus;
int channel;
int target_id;
int lun;
char * dev_name;
} Sg_map;
typedef struct my_scsi_idlun
{
int mux4;
int host_unique_id;
} My_scsi_idlun;
#define MDEV_NAME_SZ 256
static void make_dev_name(char * fname, int k, int do_numeric)
{
char buff[MDEV_NAME_SZ];
size_t len;
strncpy(fname, "/dev/sg", MDEV_NAME_SZ);
fname[MDEV_NAME_SZ - 1] = '\0';
len = strlen(fname);
if (do_numeric)
snprintf(fname + len, MDEV_NAME_SZ - len, "%d", k);
else {
if (k <= 26) {
buff[0] = 'a' + (char)k;
buff[1] = '\0';
strcat(fname, buff);
}
else
strcat(fname, "xxxx");
}
}
#define MAX_SG_DEVS 48
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 Sg_map sg_map_arr[(sizeof(devices) / sizeof(char *)) + 1];
#define EBUFF_SZ 256
#define MAX_HOLES 4
/* 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.mux4 >> 16) & 0xff;
sg_map_arr[j].lun = (m_idlun.mux4 >> 8) & 0xff;
sg_map_arr[j].target_id = m_idlun.mux4 & 0xff;
sg_map_arr[j].dev_name = devices[k];
#if 0
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);
#endif
++j;
printf("%s ", devices[k]);
close(fd);
};
printf("\n"); // <<<<<<<<<<<<<<<<<<<<<
for (k = 0; k < MAX_SG_DEVS; k++) {
make_dev_name(name, 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, 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;
}
#if 0
printf("[scsi%d ch=%d id=%d lun=%d %s]", bus,
(m_idlun.mux4 >> 16) & 0xff, m_idlun.mux4 & 0xff,
(m_idlun.mux4 >> 8) & 0xff, name);
#endif
for (j = 0; sg_map_arr[j].dev_name; ++j) {
if ((bus == sg_map_arr[j].bus) &&
((m_idlun.mux4 & 0xff) == sg_map_arr[j].target_id) &&
(((m_idlun.mux4 >> 16) & 0xff) == sg_map_arr[j].channel) &&
(((m_idlun.mux4 >> 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.mux4 >> 16) & 0xff), m_idlun.mux4 & 0xff,
((m_idlun.mux4 >> 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.mux4 >> 16) & 0xff), m_idlun.mux4 & 0xff,
((m_idlun.mux4 >> 8) & 0xff));
close(fd);
}
printf("\n");
}
/* select the given notch w/o changing anything else, if save_mode is
set output code to do the same in a script as well.. */
static int select_notch(int notch)
{
int status;
int bdlen;
int page_code = 0; /* for SETUP_MODE_PAGE */
unsigned char *pagestart;
if (save_mode) {
printf("set -- `/usr/bin/sginfo -nX %s`\n", device_name);
printf("/usr/bin/sginfo -nXR %s $1 $2 $3 %d $5 $6 $7\n",
device_name, notch);
}
SETUP_MODE_PAGE(0xc, 0);
putnbyte(pagestart + 6, notch, 2);
return put_mode_page(0xc, buffer + 8, 0);
}
static int show_pages(int page_code)
{
int offset;
int length;
int i;
int status = 0;
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;
}
if (replace) {
/* Ok we are creating a safe file.. first of all reset the
fake replace setting. */
replace = 0;
save_mode = 1;
x_interface = 1;
if (modifiable)
usage("do not use -LR with -m", 1);
/* Just a reminder: */
puts("#!/bin/sh");
if (pages_sup & (1 << 12)) {
/* save away the notched pages list.. */
pages_mask = (unsigned long) getnbyte(buffer + offset + 16, 4),
pages_mask <<= 32;
pages_mask = getnbyte(buffer + offset + 20, 4),
i = getnbyte(buffer + offset + 4, 2);
/* loop through all notches > 0 */
while (i > 0) {
status |= select_notch(i);
if (pages_mask & (1 << 1))
status |= error_recovery_page(page_code);
if (pages_mask & (1 << 2))
status |= read_disconnect_reconnect_data(page_code);
if (pages_mask & (1 << 3))
status |= read_format_info(page_code);
if (pages_mask & (1 << 4))
status |= read_geometry(page_code);
if (pages_mask & (1 << 7))
status |= verify_error_recovery(page_code);
if (pages_mask & (1 << 8))
status |= read_cache(page_code);
if (pages_mask & (1 << 9))
status |= peripheral_device_page(page_code);
if (pages_mask & (1 << 10))
status |= read_control_page(page_code);
if (pages_mask & (1 << 12))
status |= notch_parameters_page(page_code);
i--;
}
/* Back to notch 0 and safe the notch 0 page itself */
status |= select_notch(0);
status |= notch_parameters_page(page_code);
}
if (pages_sup & (1 << 1))
status |= error_recovery_page(page_code);
if (pages_sup & (1 << 2))
status |= read_disconnect_reconnect_data(page_code);
if (pages_sup & (1 << 3))
status |= read_format_info(page_code);
if (pages_sup & (1 << 4))
status |= read_geometry(page_code);
if (pages_sup & (1 << 7))
status |= verify_error_recovery(page_code);
if (pages_sup & (1 << 8))
status |= read_cache(page_code);
if (pages_sup & (1 << 9))
status |= peripheral_device_page(page_code);
if (pages_sup & (1 << 10))
status |= read_control_page(page_code);
return status;
}
if (x_interface) {
printf("0x%08lx%08lx 0x%08x%08x %d\n",
(unsigned long) (pages_sup >> 32),
(unsigned long) pages_sup,
getnbyte(buffer + offset + 16, 4),
getnbyte(buffer + offset + 20, 4),
getnbyte(buffer + offset + 6, 2));
} else {
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;
}
#define DEVNAME_SZ 256
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, 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, 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.mux4 & 0xff) == (mm_idlun.mux4 & 0xff)) &&
(((m_idlun.mux4 >> 8) & 0xff) ==
((mm_idlun.mux4 >> 8) & 0xff)) &&
(((m_idlun.mux4 >> 16) & 0xff) ==
((mm_idlun.mux4 >> 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;
}
static void usage(char *errtext, char quiet)
{
fprintf(stderr, "Error: sginfo - %s\n", errtext);
fprintf(stderr, "Usage: sginfo [-options] [device]\n");
if (!quiet)
fputs("\tAllowed options are:\n"
"\t-c Display information from Caching Page.\n"
"\t-C Display information from Control Mode Page.\n"
"\t-d Display defect lists.\n"
"\t-Farg Format of the defect list:\n"
"\t\t-Flogical - logical blocks\n"
"\t\t-Fphysical - physical blocks\n"
"\t\t-Findex - defect bytes from index\n"
"\t-e Display information from Error Recovery page.\n"
"\t-f Display information from Format Device Page.\n", stdout);
fputs("\t-g Display information from Rigid Disk Drive Geometry Page.\n"
"\t-i Display all information from Inquiry command.\n"
"\t-s Display all information from unit serial number page.\n"
"\t-D Display information from Disconnect-Reconnect Page.\n"
"\t-n Display information from Notch and Partition Page.\n"
"\t-p Display information from Peripheral Device Page.\n"
"\t-V Display information from Verify Error Recovery Page.\n"
"\t-u<no> Display information from page number <no> (18 bytes).\n"
"\t-v Show version number\n"
"\t-a All of the above.\n\n", stdout);
fputs("\t-l List known scsi devices on the system\n"
"\t-L List pages supported notched by program and target\n"
"\t (notched and active notch are also returned)\n\n"
"\tOnly one of the following three options can be specified.\n"
"\tNone of these three implies the current values are returned.\n", stdout);
fputs("\t-m Display modifiable fields instead of current values\n"
"\t-M Display manufacturer defaults instead of current values\n"
"\t-S Display saved defaults instead of current values\n\n"
"\t-X Display output suitable for the X-based interface.\n"
"\t-R Replace parameters - best used with -X (expert use only)\n\n"
"All options except -l and -v require that exactly one device is given.\n"
"-X and -R can be used only with one of the display page options.\n"
"-m and -M cannot be used with -R.\n", stdout);
fputs("You may use -M, -S with -L though it should make no difference\n"
"as a special goodie when using -LXR then a /bin/sh script is written\n"
"to stdout that will restore the current settings of the target when\n"
"executed. You can use one of -M, -S with -LXR to save the corresponding\n"
"values.\n", stderr);
exit(2+quiet);
}
#if 1
/* This is a bit of a cheat, but we simply request the info from
the disconnect/reconnect page, and then send it back again, except
with the save bit set. In theory, sending a single page with the
Save Pages bit set should be sufficient to save all the pages, but
in at least one case (Conner CFP4207S) this did not work correctly,
so we perform a MODE SELECT for all the important pages just to be
sure. */
static int replace_parameters()
{
/* Update Read-Write Error Recovery Page */
get_mode_page(0x01, 0);
put_mode_page(0x01, buffer + 8, 1);
/* Update Disconnect-Reconnect Page */
get_mode_page(0x02, 0);
put_mode_page(0x02, buffer + 8, 1);
/* Update Format Device Page */
get_mode_page(0x03, 0);
put_mode_page(0x03, buffer + 8, 1);
/* Update Caching Page */
get_mode_page(0x08, 0);
put_mode_page(0x08, buffer + 8, 1);
/* Update Control Mode Page */
get_mode_page(0x0A, 0);
put_mode_page(0x0A, buffer + 8, 1);
return 0;
}
#endif
int main(int argc, char *argv[])
{
int k;
int user_page = -1;
char c;
int page_code;
int status = 0;
char all = 0;
int i;
long tmp;
if (argc < 2)
usage("too few arguments", 0);
while ((k = getopt(argc, argv, "agdcfisDeCXmMSRvlnLpVu:F:")) != EOF) {
c = (char)k;
switch (c) {
case 'F':
if (!strcasecmp(optarg, "logical"))
defectformat = 0x0;
else if (!strcasecmp(optarg, "physical"))
defectformat = 0x5;
else if (!strcasecmp(optarg, "index"))
defectformat = 0x4;
else usage(
"Illegal -F parameter, must be one of logical, physical, or index.", 1);
break;
case 'l':
list = 1;
break;
case 'e':
error = 1;
break;
case 'd':
defect = 1;
break;
case 'n':
notch = 1;
break;
case 'i':
inquiry = 1;
break;
case 's':
serial_number = 1;
break;
case 'D':
disconnect = 1;
break;
case 'M':
default_param = 1;
break;
case 'm':
modifiable = 1;
break;
case 'S':
saved = 1;
break;
case 'f':
format = 1;
break;
case 'g':
geometry = 1;
break;
case 'C':
control = 1;
break;
case 'c':
cache = 1;
break;
case 'X':
x_interface = 1;
break;
case 'R':
replace = 1;
break;
case 'L':
list_pages = 1;
break;
case 'V':
verify = 1;
break;
case 'u':
//x_interface = 1;
user_page = atol (optarg);
break;
case 'p':
peripheral = 1;
break;
case 'a':
all = 1;
verify = 1;
peripheral = 1;
error = 1;
defect = 1;
inquiry = 1;
serial_number = 1;
disconnect = 1;
format = 1;
geometry = 1;
control = 1;
cache = 1;
notch = 1;
/* fall through */
case 'v':
fprintf(stdout, " Sginfo version 1.98\n");
break;
default:
fprintf(stdout, "Unknown option '-%c' (ascii %02xh)\n", c, c);
usage("bad option", 0);
};
};
if (saved + modifiable + default_param > 1)
usage("only one of -m, -M, or -S allowed", 1);
if (x_interface && (inquiry + geometry + cache + format +
error + control + disconnect + defect + list_pages) > 1)
usage("-X can be used only with exactly one display page option.", 1);
if (replace && !x_interface)
usage("-R requires -X", 1);
if (replace && (modifiable || default_param) && !list_pages)
usage("-R not allowed for -m or -M", 1);
if (replace && !saved) {
memset (is_hex, 0, 32);
for (i = 1; i < argc - optind; i++) {
if (strncmp(argv[optind + i], "0x", 2) == 0) {
char *pnt = argv[optind + i] + 2;
replacement_values[i] = 0;
/* This is a kluge, but we can handle 64 bit quantities this way. */
while (*pnt) {
if (*pnt >= 'a' && *pnt <= 'f')
*pnt -= 32;
replacement_values[i] = (replacement_values[i] << 4) |
(*pnt > '9' ? (*pnt - 'A' + 10) : (*pnt - '0'));
pnt++;
}
continue;
}
if (argv[optind + i][0] == '@') {
/*Ensure that this string contains an even number of hex-digits */
int len = strlen(argv[optind + i] + 1);
if ((len & 1) || (len != strspn(argv[optind + i] + 1,
"0123456789ABCDEFabcdef")))
usage("Odd number of chars or non-hex digit in @hexdatafield", 1);
replacement_values[i] = (unsigned long) argv[optind + i];
is_hex[i] = 1;
continue;
}
/* Using a tmp here is silly but the most clean approach */
sscanf(argv[optind + i], "%ld", &tmp);
replacement_values[i] = tmp;
};
n_replacement_values = argc - optind - 1;
};
if (list) {
show_devices();
exit(0);
}
if (optind >= argc)
usage("no device name given", 1);
glob_fd = open_sg_dev(device_name = argv[optind]);
if (glob_fd < 0) {
if (-9999 == glob_fd)
fprintf(stderr, "Couldn't find sg device corresponding to %s\n",
device_name);
else {
perror("sginfo(open)");
fprintf(stderr, "file=%s, or no corresponding sg device found\n", device_name);
fprintf(stderr, "Is sg driver loaded?\n");
}
exit(1);
}
/* Save the current parameters in NOVRAM on the device */
#if 1
if (saved && replace && !list_pages) {
#if 1
replace_parameters();
#endif
close(glob_fd);
exit(0);
};
#endif
page_code = 0;
if (modifiable)
page_code = 1;
if (default_param)
page_code = 2;
if (saved)
page_code = 3;
if (!x_interface)
printf("\n");
if (inquiry)
status |= do_inquiry(page_code);
if (serial_number)
status |= do_serial_number(page_code);
if (geometry)
status |= read_geometry(page_code);
if (cache)
status |= read_cache(page_code);
if (format)
status |= read_format_info(page_code);
if (error)
status |= error_recovery_page(page_code);
if (control)
status |= read_control_page(page_code);
if (disconnect)
status |= read_disconnect_reconnect_data(page_code);
if (defect)
status |= read_defect_list(page_code);
if (notch)
status |= notch_parameters_page(page_code);
if (verify)
status |= verify_error_recovery(page_code);
if (peripheral)
status |= peripheral_device_page(page_code);
if (user_page != -1)
status |= do_user_page(page_code, user_page);
if (list_pages)
status |= show_pages(page_code);
if (all)
return 0;
return status ? 1 : 0;
}