#include "global.h"
#include <sys/types.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/uio.h>
#include <sys/fcntl.h>
#include <errno.h>
#include <memory.h>
#include <malloc.h>
#include <unistd.h>
#include <stdlib.h>
#include <values.h>
#include <sys/byteorder.h>
#include "startup.h"
#include "scsi_com.h"
#include "misc.h"
#include "ctlr_scsi.h"
#include "analyze.h"
#include "param.h"
#include "io.h"
#ifndef DAD_MODE_CACHE_CCS
#define DAD_MODE_CACHE_CCS 0x38
#endif
#define FDH_FOV 0x80
#define FDH_IMMED 0x02
#define SENSE_LEN 20
#define RETRY_DELAY 5
#define PROGRESS_INDICATION_BASE 65536
static int scsi_format(uint64_t, uint64_t, struct defect_list *);
static int scsi_raw_format(void);
static int scsi_ms_page8(int);
static int scsi_ms_page38(int);
static void scsi_convert_list_to_new(struct defect_list *,
struct scsi_defect_list *, int);
static char *scsi_find_command_name(uint_t);
static int chg_list_affects_page(struct chg_list *, int);
static void scsi_printerr(struct uscsi_cmd *,
struct scsi_extended_sense *, int);
static diskaddr_t
scsi_extract_sense_info_descr(struct scsi_descr_sense_hdr *sdsp, int rqlen);
static void scsi_print_extended_sense(struct scsi_extended_sense *, int);
static void scsi_print_descr_sense(struct scsi_descr_sense_hdr *, int);
static int test_until_ready(int fd);
static int uscsi_reserve_release(int, int);
static int check_support_for_defects(void);
static int scsi_format_without_defects(void);
static int scsi_ms_page1(int);
static int scsi_ms_page2(int);
static int scsi_ms_page3(int);
static int scsi_ms_page4(int);
static int scsi_repair(uint64_t, int);
static int scsi_read_defect_data(struct defect_list *, int);
static int scsi_ck_format(void);
struct ctlr_ops scsiops = {
scsi_rdwr,
scsi_ck_format,
scsi_format,
scsi_ex_man,
scsi_ex_cur,
scsi_repair,
0,
};
#define SCMD_UNKNOWN 0xff
static struct scsi_command_name {
uchar_t command;
char *name;
} scsi_command_names[] = {
SCMD_FORMAT, "format",
SCMD_READ, "read",
SCMD_WRITE, "write",
SCMD_READ|SCMD_GROUP1, "read",
SCMD_WRITE|SCMD_GROUP1, "write",
SCMD_INQUIRY, "inquiry",
SCMD_MODE_SELECT, "mode select",
SCMD_MODE_SENSE, "mode sense",
SCMD_REASSIGN_BLOCK, "reassign block",
SCMD_READ_DEFECT_LIST, "read defect list",
SCMD_UNKNOWN, "unknown"
};
static slist_t page_control_strings[] = {
{ "current", "", MODE_SENSE_PC_CURRENT },
{ "changeable", "", MODE_SENSE_PC_CHANGEABLE },
{ "default", "", MODE_SENSE_PC_DEFAULT },
{ "saved", "", MODE_SENSE_PC_SAVED }
};
static slist_t mode_select_strings[] = {
{ "", "", 0 },
{ " (pf)", "", MODE_SELECT_PF },
{ " (sp)", "", MODE_SELECT_SP },
{ " (pf,sp)", "", MODE_SELECT_PF|MODE_SELECT_SP }
};
static int scsi_format_revolutions = 5;
static int scsi_format_timeout = 2 * 60 * 60;
#define INVALID_OPCODE 0x20
int
scsi_rdwr(int dir, int fd, diskaddr_t blkno, int secnt, caddr_t bufaddr,
int flags, int *xfercntp)
{
struct uscsi_cmd ucmd;
union scsi_cdb cdb;
int max_sectors;
int rc = 0;
if (xfercntp == NULL) {
max_sectors = BUF_SECTS;
} else if (*xfercntp == 0) {
max_sectors = BUF_SECTS;
*xfercntp = max_sectors;
} else {
max_sectors = *xfercntp;
}
while (secnt) {
int nsectors;
nsectors = (max_sectors < secnt) ? max_sectors : secnt;
(void) memset((char *)&ucmd, 0, sizeof (ucmd));
(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
cdb.scc_cmd = (dir == DIR_READ) ? SCMD_READ : SCMD_WRITE;
if (blkno < (2<<20) && nsectors <= 0xff) {
FORMG0ADDR(&cdb, blkno);
FORMG0COUNT(&cdb, nsectors);
ucmd.uscsi_cdblen = CDB_GROUP0;
} else {
if (blkno > 0xffffffff) {
FORMG4LONGADDR(&cdb, blkno);
FORMG4COUNT(&cdb, nsectors);
ucmd.uscsi_cdblen = CDB_GROUP4;
cdb.scc_cmd |= SCMD_GROUP4;
} else {
FORMG1ADDR(&cdb, blkno);
FORMG1COUNT(&cdb, nsectors);
ucmd.uscsi_cdblen = CDB_GROUP1;
cdb.scc_cmd |= SCMD_GROUP1;
}
}
ucmd.uscsi_cdb = (caddr_t)&cdb;
ucmd.uscsi_bufaddr = bufaddr;
ucmd.uscsi_buflen = nsectors * cur_blksz;
rc = uscsi_cmd(fd, &ucmd, flags);
if (rc != 0)
break;
if (ucmd.uscsi_resid == ucmd.uscsi_buflen) {
max_sectors >>= 1;
if (max_sectors <= 0) {
rc = -1;
break;
}
continue;
}
if (ucmd.uscsi_resid != 0) {
rc = -1;
break;
}
blkno += nsectors;
secnt -= nsectors;
bufaddr += nsectors * cur_blksz;
}
if (xfercntp != NULL && max_sectors < *xfercntp) {
if (diag_msg)
err_print("reducing xfercnt %d %d\n",
*xfercntp, max_sectors);
*xfercntp = max_sectors;
}
return (rc);
}
static int
scsi_ck_format(void)
{
int status;
status = scsi_rdwr(DIR_READ, cur_file, (diskaddr_t)0, 4,
(caddr_t)cur_buf, F_SILENT, NULL);
return (!status);
}
static int
scsi_format(uint64_t start __unused, uint64_t end, struct defect_list *list)
{
struct uscsi_cmd ucmd;
union scsi_cdb cdb;
int status;
int flag;
char rawbuf[MAX_MODE_SENSE_SIZE];
struct scsi_inquiry *inq;
uint8_t fmt_prot_info;
uint8_t prot_field_usage;
uint8_t param_long_list = 1;
uint8_t fmt_long_param_header[8];
if (uscsi_inquiry(cur_file, rawbuf, sizeof (rawbuf))) {
err_print("Inquiry failed\n");
return (-1);
}
inq = (struct scsi_inquiry *)rawbuf;
flag = (inq->inq_rdf == RDF_SCSI2);
if (uscsi_reserve_release(cur_file, SCMD_RESERVE)) {
err_print("Reserve failed\n");
return (-1);
}
if (scsi_ms_page1(flag) || scsi_ms_page2(flag) ||
scsi_ms_page4(flag) || scsi_ms_page38(flag) ||
scsi_ms_page8(flag) || scsi_ms_page3(flag)) {
(void) uscsi_reserve_release(cur_file, SCMD_RELEASE);
return (-1);
}
if (option_msg && diag_msg) {
(void) scsi_dump_mode_sense_pages(MODE_SENSE_PC_DEFAULT);
(void) scsi_dump_mode_sense_pages(MODE_SENSE_PC_CURRENT);
(void) scsi_dump_mode_sense_pages(MODE_SENSE_PC_SAVED);
(void) scsi_dump_mode_sense_pages(MODE_SENSE_PC_CHANGEABLE);
err_print("\n");
}
switch (prot_type) {
case PROT_TYPE_0:
fmt_prot_info = 0x00;
prot_field_usage = 0x00;
break;
case PROT_TYPE_1:
fmt_prot_info = 0x02;
prot_field_usage = 0x00;
break;
case PROT_TYPE_2:
fmt_prot_info = 0x03;
prot_field_usage = 0x00;
break;
case PROT_TYPE_3:
fmt_prot_info = 0x03;
prot_field_usage = 0x01;
break;
default:
fmt_print("invalid protection type\n");
return (-1);
}
(void) memset((char *)&ucmd, 0, sizeof (ucmd));
(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
cdb.scc_cmd = SCMD_FORMAT;
ucmd.uscsi_cdb = (caddr_t)&cdb;
ucmd.uscsi_cdblen = CDB_GROUP0;
cdb.cdb_opaque[1] = FPB_DATA;
cdb.cdb_opaque[1] |= (param_long_list << 5) | (fmt_prot_info << 6);
(void) memset((char *)fmt_long_param_header, 0,
sizeof (fmt_long_param_header));
fmt_long_param_header[0] = prot_field_usage;
fmt_long_param_header[1] = FDH_FOV | FDH_IMMED;
ucmd.uscsi_bufaddr = (caddr_t)fmt_long_param_header;
ucmd.uscsi_buflen = sizeof (fmt_long_param_header);
if ((list->list != NULL) && ((list->flags & LIST_PGLIST) == 0)) {
cdb.cdb_opaque[1] |= FPB_CMPLT;
}
fmt_print("Formatting...\n");
(void) fflush(stdout);
status = uscsi_cmd(cur_file, &ucmd,
(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
if (status == 0) {
status = test_until_ready(cur_file);
} else {
(void) memset((char *)fmt_long_param_header, 0,
sizeof (fmt_long_param_header));
fmt_long_param_header[0] = prot_field_usage;
fmt_long_param_header[1] = FDH_IMMED;
status = uscsi_cmd(cur_file, &ucmd,
(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
if (status == 0) {
status = test_until_ready(cur_file);
} else {
(void) memset((char *)fmt_long_param_header, 0,
sizeof (fmt_long_param_header));
fmt_long_param_header[0] = prot_field_usage;
status = uscsi_cmd(cur_file, &ucmd,
(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
}
}
if (status != 0) {
if (!(check_support_for_defects())) {
status = scsi_format_without_defects();
}
if (status != 0) {
fmt_print("Format failed\n");
status = scsi_raw_format();
}
}
(void) uscsi_reserve_release(cur_file, SCMD_RELEASE);
return (status);
}
static int
scsi_raw_format(void)
{
struct uscsi_cmd ucmd;
union scsi_cdb cdb;
struct scsi_defect_hdr defect_hdr;
int status;
fmt_print("\n"
"Retry of formatting operation without any of the standard\n"
"mode selects and ignoring disk's Grown Defects list. The\n"
"disk may be able to be reformatted this way if an earlier\n"
"formatting operation was interrupted by a power failure or\n"
"SCSI bus reset. The Grown Defects list will be recreated\n"
"by format verification and surface analysis.\n\n");
if (check("Retry format without mode selects and Grown Defects list")
!= 0) {
return (-1);
}
(void) memset((char *)&ucmd, 0, sizeof (ucmd));
(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
(void) memset((char *)&defect_hdr, 0, sizeof (defect_hdr));
cdb.scc_cmd = SCMD_FORMAT;
ucmd.uscsi_cdb = (caddr_t)&cdb;
ucmd.uscsi_cdblen = CDB_GROUP0;
cdb.cdb_opaque[1] = FPB_DATA | FPB_CMPLT | FPB_BFI;
ucmd.uscsi_bufaddr = (caddr_t)&defect_hdr;
ucmd.uscsi_buflen = sizeof (defect_hdr);
defect_hdr.descriptor = FDH_FOV | FDH_IMMED;
fmt_print("Formatting...\n");
(void) fflush(stdout);
status = uscsi_cmd(cur_file, &ucmd, F_NORMAL);
if (status == 0) {
status = test_until_ready(cur_file);
} else {
(void) memset((char *)&defect_hdr, 0, sizeof (defect_hdr));
status = uscsi_cmd(cur_file, &ucmd, F_NORMAL);
}
return (status);
}
int
scsi_format_time(void)
{
struct mode_geometry *page4;
struct scsi_ms_header header;
int status;
int p4_cylinders, p4_heads, p4_rpm;
int length;
int format_time;
union {
struct mode_geometry page4;
char rawbuf[MAX_MODE_SENSE_SIZE];
} u_page4;
page4 = &u_page4.page4;
(void) memset(&u_page4, 0, sizeof (u_page4));
status = uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY,
MODE_SENSE_PC_DEFAULT, (caddr_t)page4,
MAX_MODE_SENSE_SIZE, &header);
if (status) {
status = uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY,
MODE_SENSE_PC_SAVED, (caddr_t)page4,
MAX_MODE_SENSE_SIZE, &header);
}
if (status) {
status = uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY,
MODE_SENSE_PC_CURRENT, (caddr_t)page4,
MAX_MODE_SENSE_SIZE, &header);
}
if (status) {
return (0);
}
length = MODESENSE_PAGE_LEN(page4);
if (length < MIN_PAGE4_LEN) {
return (0);
}
page4->rpm = BE_16(page4->rpm);
p4_cylinders = (page4->cyl_ub << 16) + (page4->cyl_mb << 8) +
page4->cyl_lb;
p4_heads = page4->heads;
p4_rpm = page4->rpm;
if (p4_rpm < MIN_RPM || p4_rpm > MAX_RPM) {
err_print("Mode sense page(4) reports rpm value as %d,"
" adjusting it to %d\n", p4_rpm, AVG_RPM);
p4_rpm = AVG_RPM;
}
if (p4_cylinders <= 0 || p4_heads <= 0)
return (0);
format_time = ((scsi_format_revolutions * p4_heads *
p4_cylinders) + p4_rpm) / p4_rpm;
if (option_msg && diag_msg) {
err_print(" pcyl: %d\n", p4_cylinders);
err_print(" heads: %d\n", p4_heads);
err_print(" rpm: %d\n", p4_rpm);
err_print("format_time: %d minutes\n", format_time);
}
return (format_time);
}
static int
scsi_ms_page1(int scsi2_flag __unused)
{
struct mode_err_recov *page1;
struct mode_err_recov *fixed;
struct scsi_ms_header header;
struct scsi_ms_header fixed_hdr;
int status;
int tmp1, tmp2;
int flag;
int length;
int sp_flags;
union {
struct mode_err_recov page1;
char rawbuf[MAX_MODE_SENSE_SIZE];
} u_page1, u_fixed;
page1 = &u_page1.page1;
fixed = &u_fixed.page1;
if (option_msg && diag_msg) {
(void) uscsi_mode_sense(cur_file, DAD_MODE_ERR_RECOV,
MODE_SENSE_PC_DEFAULT, (caddr_t)page1,
MAX_MODE_SENSE_SIZE, &header);
(void) uscsi_mode_sense(cur_file, DAD_MODE_ERR_RECOV,
MODE_SENSE_PC_CURRENT, (caddr_t)page1,
MAX_MODE_SENSE_SIZE, &header);
}
status = uscsi_mode_sense(cur_file, DAD_MODE_ERR_RECOV,
MODE_SENSE_PC_SAVED, (caddr_t)page1,
MAX_MODE_SENSE_SIZE, &header);
if (status) {
status = uscsi_mode_sense(cur_file, DAD_MODE_ERR_RECOV,
MODE_SENSE_PC_CURRENT, (caddr_t)page1,
MAX_MODE_SENSE_SIZE, &header);
if (status) {
return (0);
}
}
length = MODESENSE_PAGE_LEN(page1);
if (length < MIN_PAGE1_LEN) {
return (0);
}
status = uscsi_mode_sense(cur_file, DAD_MODE_ERR_RECOV,
MODE_SENSE_PC_CHANGEABLE, (caddr_t)fixed,
MAX_MODE_SENSE_SIZE, &fixed_hdr);
if (status || MODESENSE_PAGE_LEN(fixed) < MIN_PAGE1_LEN) {
return (0);
}
flag = 0;
tmp1 = page1->read_retry_count;
tmp2 = page1->write_retry_count;
if (cur_dtype->dtype_options & SUP_READ_RETRIES &&
fixed->read_retry_count != 0) {
flag |= (page1->read_retry_count !=
cur_dtype->dtype_read_retries);
page1->read_retry_count = cur_dtype->dtype_read_retries;
}
if (length > 8) {
if (cur_dtype->dtype_options & SUP_WRITE_RETRIES &&
fixed->write_retry_count != 0) {
flag |= (page1->write_retry_count !=
cur_dtype->dtype_write_retries);
page1->write_retry_count =
cur_dtype->dtype_write_retries;
}
}
if (flag && option_msg) {
fmt_print("PAGE 1: read retries= %d (%d) "
" write retries= %d (%d)\n",
page1->read_retry_count, tmp1,
page1->write_retry_count, tmp2);
}
flag |= apply_chg_list(DAD_MODE_ERR_RECOV, length,
(uchar_t *)page1, (uchar_t *)fixed, cur_dtype->dtype_chglist);
if (flag == 0) {
return (0);
}
sp_flags = MODE_SELECT_PF;
if (page1->mode_page.ps) {
sp_flags |= MODE_SELECT_SP;
}
page1->mode_page.ps = 0;
header.mode_header.length = 0;
header.mode_header.device_specific = 0;
status = uscsi_mode_select(cur_file, DAD_MODE_ERR_RECOV,
sp_flags, (caddr_t)page1, length, &header);
if (status && (sp_flags & MODE_SELECT_SP)) {
sp_flags &= ~MODE_SELECT_SP;
status = uscsi_mode_select(cur_file, DAD_MODE_ERR_RECOV,
sp_flags, (caddr_t)page1, length, &header);
}
if (status && option_msg) {
err_print("Warning: Using default error recovery "
"parameters.\n\n");
}
if (option_msg && diag_msg) {
(void) uscsi_mode_sense(cur_file, DAD_MODE_ERR_RECOV,
MODE_SENSE_PC_CURRENT, (caddr_t)page1,
MAX_MODE_SENSE_SIZE, &header);
(void) uscsi_mode_sense(cur_file, DAD_MODE_ERR_RECOV,
MODE_SENSE_PC_SAVED, (caddr_t)page1,
MAX_MODE_SENSE_SIZE, &header);
}
return (0);
}
static int
scsi_ms_page2(int scsi2_flag __unused)
{
struct mode_disco_reco *page2;
struct mode_disco_reco *fixed;
struct scsi_ms_header header;
struct scsi_ms_header fixed_hdr;
int status;
int flag;
int length;
int sp_flags;
union {
struct mode_disco_reco page2;
char rawbuf[MAX_MODE_SENSE_SIZE];
} u_page2, u_fixed;
page2 = &u_page2.page2;
fixed = &u_fixed.page2;
if (option_msg && diag_msg) {
(void) uscsi_mode_sense(cur_file, MODEPAGE_DISCO_RECO,
MODE_SENSE_PC_DEFAULT, (caddr_t)page2,
MAX_MODE_SENSE_SIZE, &header);
(void) uscsi_mode_sense(cur_file, MODEPAGE_DISCO_RECO,
MODE_SENSE_PC_CURRENT, (caddr_t)page2,
MAX_MODE_SENSE_SIZE, &header);
}
status = uscsi_mode_sense(cur_file, MODEPAGE_DISCO_RECO,
MODE_SENSE_PC_SAVED, (caddr_t)page2,
MAX_MODE_SENSE_SIZE, &header);
if (status) {
status = uscsi_mode_sense(cur_file, MODEPAGE_DISCO_RECO,
MODE_SENSE_PC_CURRENT, (caddr_t)page2,
MAX_MODE_SENSE_SIZE, &header);
if (status) {
return (0);
}
}
length = MODESENSE_PAGE_LEN(page2);
if (length < MIN_PAGE2_LEN) {
return (0);
}
status = uscsi_mode_sense(cur_file, MODEPAGE_DISCO_RECO,
MODE_SENSE_PC_CHANGEABLE, (caddr_t)fixed,
MAX_MODE_SENSE_SIZE, &fixed_hdr);
if (status || MODESENSE_PAGE_LEN(fixed) < MIN_PAGE2_LEN) {
return (0);
}
flag = 0;
flag |= apply_chg_list(MODEPAGE_DISCO_RECO, length,
(uchar_t *)page2, (uchar_t *)fixed, cur_dtype->dtype_chglist);
if (flag == 0) {
return (0);
}
sp_flags = MODE_SELECT_PF;
if (page2->mode_page.ps) {
sp_flags |= MODE_SELECT_SP;
}
page2->mode_page.ps = 0;
header.mode_header.length = 0;
header.mode_header.device_specific = 0;
status = uscsi_mode_select(cur_file, MODEPAGE_DISCO_RECO,
MODE_SELECT_SP, (caddr_t)page2, length, &header);
if (status && (sp_flags & MODE_SELECT_SP)) {
sp_flags &= ~MODE_SELECT_SP;
status = uscsi_mode_select(cur_file, MODEPAGE_DISCO_RECO,
sp_flags, (caddr_t)page2, length, &header);
}
if (status && option_msg) {
err_print("Warning: Using default .\n\n");
}
if (option_msg && diag_msg) {
(void) uscsi_mode_sense(cur_file, MODEPAGE_DISCO_RECO,
MODE_SENSE_PC_CURRENT, (caddr_t)page2,
MAX_MODE_SENSE_SIZE, &header);
(void) uscsi_mode_sense(cur_file, MODEPAGE_DISCO_RECO,
MODE_SENSE_PC_SAVED, (caddr_t)page2,
MAX_MODE_SENSE_SIZE, &header);
}
return (0);
}
static int
scsi_ms_page3(int scsi2_flag __unused)
{
struct mode_format *page3;
struct mode_format *fixed;
struct scsi_ms_header header;
struct scsi_ms_header fixed_hdr;
int status;
int tmp1, tmp2, tmp3;
int tmp4, tmp5, tmp6;
int flag;
int length;
int sp_flags;
union {
struct mode_format page3;
char rawbuf[MAX_MODE_SENSE_SIZE];
} u_page3, u_fixed;
page3 = &u_page3.page3;
fixed = &u_fixed.page3;
if (option_msg && diag_msg) {
(void) uscsi_mode_sense(cur_file, DAD_MODE_FORMAT,
MODE_SENSE_PC_DEFAULT, (caddr_t)page3,
MAX_MODE_SENSE_SIZE, &header);
(void) uscsi_mode_sense(cur_file, DAD_MODE_FORMAT,
MODE_SENSE_PC_CURRENT, (caddr_t)page3,
MAX_MODE_SENSE_SIZE, &header);
}
status = uscsi_mode_sense(cur_file, DAD_MODE_FORMAT,
MODE_SENSE_PC_SAVED, (caddr_t)page3,
MAX_MODE_SENSE_SIZE, &header);
if (status) {
status = uscsi_mode_sense(cur_file, DAD_MODE_FORMAT,
MODE_SENSE_PC_CURRENT, (caddr_t)page3,
MAX_MODE_SENSE_SIZE, &header);
if (status) {
return (0);
}
}
length = MODESENSE_PAGE_LEN(page3);
if (length < MIN_PAGE3_LEN) {
return (0);
}
status = uscsi_mode_sense(cur_file, DAD_MODE_FORMAT,
MODE_SENSE_PC_CHANGEABLE, (caddr_t)fixed,
MAX_MODE_SENSE_SIZE, &fixed_hdr);
if (status || MODESENSE_PAGE_LEN(fixed) < MIN_PAGE3_LEN) {
return (0);
}
tmp1 = page3->track_skew;
tmp2 = page3->cylinder_skew;
tmp3 = page3->sect_track;
tmp4 = page3->tracks_per_zone;
tmp5 = page3->alt_tracks_vol;
tmp6 = page3->alt_sect_zone;
flag = (page3->data_bytes_sect != cur_blksz);
page3->data_bytes_sect = cur_blksz;
flag |= (page3->interleave != 1);
page3->interleave = 1;
if (cur_dtype->dtype_options & SUP_CYLSKEW &&
fixed->cylinder_skew != 0) {
flag |= (page3->cylinder_skew != cur_dtype->dtype_cyl_skew);
page3->cylinder_skew = cur_dtype->dtype_cyl_skew;
}
if (cur_dtype->dtype_options & SUP_TRKSKEW && fixed->track_skew != 0) {
flag |= (page3->track_skew != cur_dtype->dtype_trk_skew);
page3->track_skew = cur_dtype->dtype_trk_skew;
}
if (cur_dtype->dtype_options & SUP_PSECT && fixed->sect_track != 0) {
flag |= (page3->sect_track != psect);
page3->sect_track = (ushort_t)psect;
}
if (cur_dtype->dtype_options & SUP_TRKS_ZONE &&
fixed->tracks_per_zone != 0) {
flag |= (page3->tracks_per_zone != cur_dtype->dtype_trks_zone);
page3->tracks_per_zone = cur_dtype->dtype_trks_zone;
}
if (cur_dtype->dtype_options & SUP_ASECT && fixed->alt_sect_zone != 0) {
flag |= (page3->alt_sect_zone != cur_dtype->dtype_asect);
page3->alt_sect_zone = cur_dtype->dtype_asect;
}
if (cur_dtype->dtype_options & SUP_ATRKS &&
fixed->alt_tracks_vol != 0) {
flag |= (page3->alt_tracks_vol != cur_dtype->dtype_atrks);
page3->alt_tracks_vol = cur_dtype->dtype_atrks;
}
if (flag && option_msg) {
fmt_print("PAGE 3: trk skew= %d (%d) cyl skew= %d (%d) ",
page3->track_skew, tmp1, page3->cylinder_skew, tmp2);
fmt_print("sects/trk= %d (%d)\n", page3->sect_track, tmp3);
fmt_print(" trks/zone= %d (%d) alt trks= %d (%d) ",
page3->tracks_per_zone, tmp4,
page3->alt_tracks_vol, tmp5);
fmt_print("alt sects/zone= %d (%d)\n",
page3->alt_sect_zone, tmp6);
}
flag |= apply_chg_list(DAD_MODE_FORMAT, length,
(uchar_t *)page3, (uchar_t *)fixed, cur_dtype->dtype_chglist);
if (flag == 0) {
return (0);
}
sp_flags = MODE_SELECT_PF;
if (page3->mode_page.ps) {
sp_flags |= MODE_SELECT_SP;
}
page3->mode_page.ps = 0;
header.mode_header.length = 0;
header.mode_header.device_specific = 0;
status = uscsi_mode_select(cur_file, DAD_MODE_FORMAT,
MODE_SELECT_SP, (caddr_t)page3, length, &header);
if (status && (sp_flags & MODE_SELECT_SP)) {
sp_flags &= ~MODE_SELECT_SP;
status = uscsi_mode_select(cur_file, DAD_MODE_FORMAT,
sp_flags, (caddr_t)page3, length, &header);
}
if (status && option_msg) {
err_print("Warning: Using default drive format parameters.\n");
err_print("Warning: Drive format may not be correct.\n\n");
}
if (option_msg && diag_msg) {
(void) uscsi_mode_sense(cur_file, DAD_MODE_FORMAT,
MODE_SENSE_PC_CURRENT, (caddr_t)page3,
MAX_MODE_SENSE_SIZE, &header);
(void) uscsi_mode_sense(cur_file, DAD_MODE_FORMAT,
MODE_SENSE_PC_SAVED, (caddr_t)page3,
MAX_MODE_SENSE_SIZE, &header);
}
return (0);
}
static int
scsi_ms_page4(int scsi2_flag __unused)
{
struct mode_geometry *page4;
struct mode_geometry *fixed;
struct scsi_ms_header header;
struct scsi_ms_header fixed_hdr;
int status;
int tmp1, tmp2;
int flag;
int length;
int sp_flags;
union {
struct mode_geometry page4;
char rawbuf[MAX_MODE_SENSE_SIZE];
} u_page4, u_fixed;
page4 = &u_page4.page4;
fixed = &u_fixed.page4;
if (option_msg && diag_msg) {
(void) uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY,
MODE_SENSE_PC_DEFAULT, (caddr_t)page4,
MAX_MODE_SENSE_SIZE, &header);
(void) uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY,
MODE_SENSE_PC_CURRENT, (caddr_t)page4,
MAX_MODE_SENSE_SIZE, &header);
}
status = uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY,
MODE_SENSE_PC_SAVED, (caddr_t)page4,
MAX_MODE_SENSE_SIZE, &header);
if (status) {
status = uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY,
MODE_SENSE_PC_CURRENT, (caddr_t)page4,
MAX_MODE_SENSE_SIZE, &header);
if (status) {
return (0);
}
}
length = MODESENSE_PAGE_LEN(page4);
if (length < MIN_PAGE4_LEN) {
return (0);
}
status = uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY,
MODE_SENSE_PC_CHANGEABLE, (caddr_t)fixed,
MAX_MODE_SENSE_SIZE, &fixed_hdr);
if (status || MODESENSE_PAGE_LEN(fixed) < MIN_PAGE4_LEN) {
return (0);
}
tmp1 = (page4->cyl_ub << 16) + (page4->cyl_mb << 8) + page4->cyl_lb;
tmp2 = page4->heads;
flag = 0;
if ((cur_dtype->dtype_options & SUP_PHEAD) && fixed->heads != 0) {
flag |= (page4->heads != phead);
page4->heads = phead;
}
if (flag && option_msg) {
fmt_print("PAGE 4: cylinders= %d heads= %d (%d)\n",
tmp1, page4->heads, tmp2);
}
flag |= apply_chg_list(DAD_MODE_GEOMETRY, length,
(uchar_t *)page4, (uchar_t *)fixed, cur_dtype->dtype_chglist);
if (flag == 0) {
return (0);
}
sp_flags = MODE_SELECT_PF;
if (page4->mode_page.ps) {
sp_flags |= MODE_SELECT_SP;
}
page4->mode_page.ps = 0;
header.mode_header.length = 0;
header.mode_header.device_specific = 0;
status = uscsi_mode_select(cur_file, DAD_MODE_GEOMETRY,
MODE_SELECT_SP, (caddr_t)page4, length, &header);
if (status && (sp_flags & MODE_SELECT_SP)) {
sp_flags &= ~MODE_SELECT_SP;
status = uscsi_mode_select(cur_file, DAD_MODE_GEOMETRY,
sp_flags, (caddr_t)page4, length, &header);
}
if (status && option_msg) {
err_print("Warning: Using default drive geometry.\n\n");
}
if (option_msg && diag_msg) {
(void) uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY,
MODE_SENSE_PC_CURRENT, (caddr_t)page4,
MAX_MODE_SENSE_SIZE, &header);
(void) uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY,
MODE_SENSE_PC_SAVED, (caddr_t)page4,
MAX_MODE_SENSE_SIZE, &header);
}
return (0);
}
static int
scsi_ms_page8(int scsi2_flag __unused)
{
struct mode_cache *page8;
struct mode_cache *fixed;
struct scsi_ms_header header;
struct scsi_ms_header fixed_hdr;
int status;
int flag;
int length;
int sp_flags;
union {
struct mode_cache page8;
char rawbuf[MAX_MODE_SENSE_SIZE];
} u_page8, u_fixed;
page8 = &u_page8.page8;
fixed = &u_fixed.page8;
if (!scsi2_flag) {
return (0);
}
if (option_msg && diag_msg) {
(void) uscsi_mode_sense(cur_file, DAD_MODE_CACHE,
MODE_SENSE_PC_DEFAULT, (caddr_t)page8,
MAX_MODE_SENSE_SIZE, &header);
(void) uscsi_mode_sense(cur_file, DAD_MODE_CACHE,
MODE_SENSE_PC_CURRENT, (caddr_t)page8,
MAX_MODE_SENSE_SIZE, &header);
}
status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE,
MODE_SENSE_PC_SAVED, (caddr_t)page8,
MAX_MODE_SENSE_SIZE, &header);
if (status) {
status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE,
MODE_SENSE_PC_CURRENT, (caddr_t)page8,
MAX_MODE_SENSE_SIZE, &header);
if (status) {
return (0);
}
}
length = MODESENSE_PAGE_LEN(page8);
if (length < MIN_PAGE8_LEN) {
return (0);
}
status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE,
MODE_SENSE_PC_CHANGEABLE, (caddr_t)fixed,
MAX_MODE_SENSE_SIZE, &fixed_hdr);
if (status || MODESENSE_PAGE_LEN(fixed) < MIN_PAGE8_LEN) {
return (0);
}
flag = 0;
flag |= apply_chg_list(DAD_MODE_CACHE, length,
(uchar_t *)page8, (uchar_t *)fixed, cur_dtype->dtype_chglist);
if (flag == 0) {
return (0);
}
sp_flags = MODE_SELECT_PF;
if (page8->mode_page.ps) {
sp_flags |= MODE_SELECT_SP;
}
page8->mode_page.ps = 0;
header.mode_header.length = 0;
header.mode_header.device_specific = 0;
status = uscsi_mode_select(cur_file, DAD_MODE_CACHE,
sp_flags, (caddr_t)page8, length, &header);
if (status && (sp_flags & MODE_SELECT_SP)) {
sp_flags &= ~MODE_SELECT_SP;
status = uscsi_mode_select(cur_file, DAD_MODE_CACHE,
sp_flags, (caddr_t)page8, length, &header);
}
if (status && option_msg) {
err_print(
"Warning: Using default SCSI-2 cache parameters.\n\n");
}
if (option_msg && diag_msg) {
(void) uscsi_mode_sense(cur_file, DAD_MODE_CACHE,
MODE_SENSE_PC_CURRENT, (caddr_t)page8,
MAX_MODE_SENSE_SIZE, &header);
(void) uscsi_mode_sense(cur_file, DAD_MODE_CACHE,
MODE_SENSE_PC_SAVED, (caddr_t)page8,
MAX_MODE_SENSE_SIZE, &header);
}
return (0);
}
static int
scsi_ms_page38(int scsi2_flag __unused)
{
struct mode_cache_ccs *page38;
struct mode_cache_ccs *fixed;
struct scsi_ms_header header;
struct scsi_ms_header fixed_hdr;
int status;
int tmp1, tmp2, tmp3, tmp4;
int flag;
int length;
int sp_flags;
union {
struct mode_cache_ccs page38;
char rawbuf[MAX_MODE_SENSE_SIZE];
} u_page38, u_fixed;
if (((cur_dtype->dtype_options & (SUP_CACHE | SUP_PREFETCH |
SUP_CACHE_MIN | SUP_CACHE_MAX)) == 0) &&
(!chg_list_affects_page(cur_dtype->dtype_chglist, 0x38))) {
return (0);
}
page38 = &u_page38.page38;
fixed = &u_fixed.page38;
if (option_msg && diag_msg) {
(void) uscsi_mode_sense(cur_file, DAD_MODE_CACHE_CCS,
MODE_SENSE_PC_DEFAULT, (caddr_t)page38,
MAX_MODE_SENSE_SIZE, &header);
(void) uscsi_mode_sense(cur_file, DAD_MODE_CACHE_CCS,
MODE_SENSE_PC_CURRENT, (caddr_t)page38,
MAX_MODE_SENSE_SIZE, &header);
}
status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE_CCS,
MODE_SENSE_PC_SAVED, (caddr_t)page38,
MAX_MODE_SENSE_SIZE, &header);
if (status) {
status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE_CCS,
MODE_SENSE_PC_CURRENT, (caddr_t)page38,
MAX_MODE_SENSE_SIZE, &header);
if (status) {
return (0);
}
}
length = MODESENSE_PAGE_LEN(page38);
if (length < MIN_PAGE38_LEN) {
return (0);
}
status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE_CCS,
MODE_SENSE_PC_CHANGEABLE, (caddr_t)fixed,
MAX_MODE_SENSE_SIZE, &fixed_hdr);
if (status || MODESENSE_PAGE_LEN(fixed) < MIN_PAGE38_LEN) {
return (0);
}
tmp1 = page38->mode;
tmp2 = page38->threshold;
tmp3 = page38->min_prefetch;
tmp4 = page38->max_prefetch;
flag = 0;
if ((cur_dtype->dtype_options & SUP_CACHE) &&
(fixed->mode & cur_dtype->dtype_cache) ==
cur_dtype->dtype_cache) {
flag |= (page38->mode != cur_dtype->dtype_cache);
page38->mode = cur_dtype->dtype_cache;
}
if ((cur_dtype->dtype_options & SUP_PREFETCH) &&
(fixed->threshold & cur_dtype->dtype_threshold) ==
cur_dtype->dtype_threshold) {
flag |= (page38->threshold != cur_dtype->dtype_threshold);
page38->threshold = cur_dtype->dtype_threshold;
}
if ((cur_dtype->dtype_options & SUP_CACHE_MIN) &&
(fixed->min_prefetch & cur_dtype->dtype_prefetch_min) ==
cur_dtype->dtype_prefetch_min) {
flag |= (page38->min_prefetch != cur_dtype->dtype_prefetch_min);
page38->min_prefetch = cur_dtype->dtype_prefetch_min;
}
if ((cur_dtype->dtype_options & SUP_CACHE_MAX) &&
(fixed->max_prefetch & cur_dtype->dtype_prefetch_max) ==
cur_dtype->dtype_prefetch_max) {
flag |= (page38->max_prefetch != cur_dtype->dtype_prefetch_max);
page38->max_prefetch = cur_dtype->dtype_prefetch_max;
}
if (flag && option_msg) {
fmt_print("PAGE 38: cache mode= 0x%x (0x%x)\n",
page38->mode, tmp1);
fmt_print(" min. prefetch multiplier= %d ",
page38->min_multiplier);
fmt_print("max. prefetch multiplier= %d\n",
page38->max_multiplier);
fmt_print(" threshold= %d (%d) ",
page38->threshold, tmp2);
fmt_print("min. prefetch= %d (%d) ",
page38->min_prefetch, tmp3);
fmt_print("max. prefetch= %d (%d)\n",
page38->max_prefetch, tmp4);
}
flag |= apply_chg_list(DAD_MODE_CACHE_CCS, length,
(uchar_t *)page38, (uchar_t *)fixed,
cur_dtype->dtype_chglist);
if (flag == 0) {
return (0);
}
sp_flags = MODE_SELECT_PF;
if (page38->mode_page.ps) {
sp_flags |= MODE_SELECT_SP;
}
page38->mode_page.ps = 0;
header.mode_header.length = 0;
header.mode_header.device_specific = 0;
status = uscsi_mode_select(cur_file, DAD_MODE_CACHE_CCS,
sp_flags, (caddr_t)page38, length, &header);
if (status && (sp_flags & MODE_SELECT_SP)) {
sp_flags &= ~MODE_SELECT_SP;
status = uscsi_mode_select(cur_file, DAD_MODE_CACHE_CCS,
sp_flags, (caddr_t)page38, length, &header);
}
if (status && option_msg) {
err_print("Warning: Using default CCS cache parameters.\n\n");
}
if (option_msg && diag_msg) {
(void) uscsi_mode_sense(cur_file, DAD_MODE_CACHE_CCS,
MODE_SENSE_PC_CURRENT, (caddr_t)page38,
MAX_MODE_SENSE_SIZE, &header);
(void) uscsi_mode_sense(cur_file, DAD_MODE_CACHE_CCS,
MODE_SENSE_PC_SAVED, (caddr_t)page38,
MAX_MODE_SENSE_SIZE, &header);
}
return (0);
}
int
scsi_ex_man(struct defect_list *list)
{
int i;
i = scsi_read_defect_data(list, DLD_MAN_DEF_LIST);
if (i != 0)
return (i);
list->flags &= ~LIST_PGLIST;
return (0);
}
int
scsi_ex_cur(struct defect_list *list)
{
int i;
i = scsi_read_defect_data(list, DLD_GROWN_DEF_LIST|DLD_MAN_DEF_LIST);
if (i != 0)
return (i);
list->flags |= LIST_PGLIST;
return (0);
}
int
scsi_ex_grown(struct defect_list *list)
{
int i;
i = scsi_read_defect_data(list, DLD_GROWN_DEF_LIST);
if (i != 0)
return (i);
list->flags |= LIST_PGLIST;
return (0);
}
static int
scsi_read_defect_data(struct defect_list *list, int pglist_flags)
{
struct uscsi_cmd ucmd;
char rqbuf[255];
union scsi_cdb cdb;
struct scsi_defect_list *defects;
struct scsi_defect_list def_list;
struct scsi_defect_hdr *hdr;
int status;
int nbytes;
int len;
struct scsi_extended_sense *rq;
hdr = (struct scsi_defect_hdr *)&def_list;
(void) memset((char *)&def_list, 0, sizeof (def_list));
(void) memset((char *)&ucmd, 0, sizeof (ucmd));
(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
(void) memset((char *)rqbuf, 0, 255);
cdb.scc_cmd = SCMD_READ_DEFECT_LIST;
FORMG1COUNT(&cdb, sizeof (struct scsi_defect_hdr));
cdb.cdb_opaque[2] = pglist_flags | DLD_BFI_FORMAT;
ucmd.uscsi_cdb = (caddr_t)&cdb;
ucmd.uscsi_cdblen = CDB_GROUP1;
ucmd.uscsi_bufaddr = (caddr_t)hdr;
ucmd.uscsi_buflen = sizeof (struct scsi_defect_hdr);
ucmd.uscsi_rqbuf = rqbuf;
ucmd.uscsi_rqlen = sizeof (rqbuf);
ucmd.uscsi_rqresid = sizeof (rqbuf);
rq = (struct scsi_extended_sense *)ucmd.uscsi_rqbuf;
status = uscsi_cmd(cur_file, &ucmd,
(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
if (status != 0) {
if (ucmd.uscsi_rqstatus == STATUS_GOOD &&
rq->es_key == KEY_ILLEGAL_REQUEST &&
rq->es_add_code == INVALID_OPCODE) {
err_print("\nWARNING: Current Disk does not support"
" defect lists.\n");
} else if (option_msg) {
err_print("No %s defect list.\n",
pglist_flags & DLD_GROWN_DEF_LIST ?
"grown" : "manufacturer's");
}
return (-1);
}
hdr->length = BE_16(hdr->length);
len = hdr->length;
nbytes = len + sizeof (struct scsi_defect_hdr);
defects = zalloc(nbytes);
*(struct scsi_defect_hdr *)defects = *(struct scsi_defect_hdr *)hdr;
(void) memset((char *)&ucmd, 0, sizeof (ucmd));
(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
cdb.scc_cmd = SCMD_READ_DEFECT_LIST;
FORMG1COUNT(&cdb, nbytes);
cdb.cdb_opaque[2] = pglist_flags | DLD_BFI_FORMAT;
ucmd.uscsi_cdb = (caddr_t)&cdb;
ucmd.uscsi_cdblen = CDB_GROUP1;
ucmd.uscsi_bufaddr = (caddr_t)defects;
ucmd.uscsi_buflen = nbytes;
status = uscsi_cmd(cur_file, &ucmd,
(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
if (status) {
err_print("can't read defect list 2nd time");
destroy_data((char *)defects);
return (-1);
}
defects->length = BE_16(defects->length);
if (len != hdr->length) {
err_print("not enough defects");
destroy_data((char *)defects);
return (-1);
}
scsi_convert_list_to_new(list, (struct scsi_defect_list *)defects,
DLD_BFI_FORMAT);
destroy_data((char *)defects);
return (0);
}
static int
scsi_repair(uint64_t bn, int flag __unused)
{
struct uscsi_cmd ucmd;
union scsi_cdb cdb;
struct scsi_reassign_blk defect_list;
(void) memset((char *)&ucmd, 0, sizeof (ucmd));
(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
(void) memset((char *)&defect_list, 0,
sizeof (struct scsi_reassign_blk));
cdb.scc_cmd = SCMD_REASSIGN_BLOCK;
ucmd.uscsi_cdb = (caddr_t)&cdb;
ucmd.uscsi_cdblen = CDB_GROUP0;
ucmd.uscsi_bufaddr = (caddr_t)&defect_list;
ucmd.uscsi_buflen = sizeof (struct scsi_reassign_blk);
defect_list.length = sizeof (defect_list.defect);
defect_list.length = BE_16(defect_list.length);
defect_list.defect = bn;
defect_list.defect = BE_32(defect_list.defect);
return (uscsi_cmd(cur_file, &ucmd,
(option_msg && diag_msg) ? F_NORMAL : F_SILENT));
}
static void
scsi_convert_list_to_new(struct defect_list *list,
struct scsi_defect_list *def_list, int list_format)
{
struct scsi_bfi_defect *old_defect, *old_defect1;
struct defect_entry *new_defect;
int len, new_len, obfi, nbfi;
int i;
int old_cyl, new_cyl;
unsigned char *cp;
switch (list_format) {
case DLD_BFI_FORMAT:
len = def_list->length / sizeof (struct scsi_bfi_defect);
old_defect = def_list->list;
new_defect = (struct defect_entry *)
zalloc(deflist_size(cur_blksz, len) *
cur_blksz);
list->header.magicno = (uint_t)DEFECT_MAGIC;
list->list = new_defect;
for (i = 0, new_len = 0; i < len; new_defect++, new_len++) {
cp = (unsigned char *)old_defect;
new_defect->cyl = (cp[0] << 16 | cp[1] << 8) | cp[2];
new_defect->head = old_defect->head;
new_defect->bfi = (int)old_defect->bytes_from_index;
new_defect->bfi = BE_32(new_defect->bfi);
new_defect->nbits = 0;
old_defect1 = old_defect++;
i++;
if (i == len) {
new_len++;
break;
}
obfi = new_defect->bfi;
nbfi = (int)old_defect->bytes_from_index;
nbfi = BE_32(nbfi);
old_cyl = new_defect->cyl;
cp = (unsigned char *)old_defect;
new_cyl = (cp[0] << 16 | cp[1] << 8) | cp[2];
while ((i < len) &&
(old_cyl == new_cyl) &&
(old_defect->head == old_defect1->head) &&
(nbfi == (obfi + BITSPERBYTE))) {
old_defect1 = old_defect++;
cp = (unsigned char *)old_defect;
new_cyl = (cp[0] << 16 | cp[1] << 8) | cp[2];
obfi = (int)old_defect1->bytes_from_index;
obfi = BE_32(obfi);
nbfi = (int)old_defect->bytes_from_index;
nbfi = BE_32(nbfi);
new_defect->nbits += (8*BITSPERBYTE);
i++;
}
}
list->header.count = new_len;
break;
default:
err_print("scsi_convert_list_to_new: can't deal with it\n");
exit(0);
}
(void) checkdefsum(list, CK_MAKESUM);
}
int
uscsi_cmd(int fd, struct uscsi_cmd *ucmd, int flags)
{
struct scsi_extended_sense *rq;
char rqbuf[255];
int status;
int rqlen;
int timeout = 0;
ucmd->uscsi_flags = USCSI_ISOLATE;
if (flags & F_SILENT) {
ucmd->uscsi_flags |= USCSI_SILENT;
}
if (flags & F_RQENABLE) {
ucmd->uscsi_flags |= USCSI_RQENABLE;
}
if (ucmd->uscsi_buflen > 0) {
switch ((uint8_t)ucmd->uscsi_cdb[0]) {
case SCMD_READ:
case SCMD_READ|SCMD_GROUP1:
case SCMD_READ|SCMD_GROUP4:
case SCMD_MODE_SENSE:
case SCMD_INQUIRY:
case SCMD_READ_DEFECT_LIST:
case SCMD_READ_CAPACITY:
case SCMD_SVC_ACTION_IN_G4:
ucmd->uscsi_flags |= USCSI_READ;
break;
}
}
switch (ucmd->uscsi_cdb[0]) {
case SCMD_FORMAT:
if (ucmd->uscsi_timeout == 0) {
ucmd->uscsi_timeout = scsi_format_timeout;
if ((timeout = scsi_format_time()) > 0) {
timeout *= 60;
timeout += timeout;
if (timeout > SHRT_MAX)
timeout = SHRT_MAX;
if (timeout > scsi_format_timeout)
ucmd->uscsi_timeout = timeout;
}
}
if (option_msg && diag_msg) {
err_print("format_timeout set to %d seconds, %d"
" required\n", ucmd->uscsi_timeout, timeout);
}
break;
default:
ucmd->uscsi_timeout = 30;
break;
}
ucmd->uscsi_flags |= USCSI_RQENABLE;
if (ucmd->uscsi_rqbuf == NULL) {
ucmd->uscsi_rqbuf = rqbuf;
ucmd->uscsi_rqlen = sizeof (rqbuf);
ucmd->uscsi_rqresid = sizeof (rqbuf);
}
ucmd->uscsi_rqstatus = IMPOSSIBLE_SCSI_STATUS;
media_error = 0;
status = ioctl(fd, USCSICMD, ucmd);
if (status == 0 && ucmd->uscsi_status == 0) {
return (status);
}
if (status == -1 && errno == EAGAIN) {
disk_error = DISK_STAT_UNAVAILABLE;
return (DSK_UNAVAILABLE);
}
if ((ucmd->uscsi_status & STATUS_MASK) == STATUS_RESERVATION_CONFLICT) {
disk_error = DISK_STAT_RESERVED;
return (DSK_RESERVED);
}
if (status == -1 && !ucmd->uscsi_status && errno == EIO) {
disk_error = DISK_STAT_UNAVAILABLE;
return (DSK_UNAVAILABLE);
}
if (ucmd->uscsi_rqstatus == IMPOSSIBLE_SCSI_STATUS) {
if (option_msg && diag_msg) {
err_print("No request sense for command %s\n",
scsi_find_command_name(ucmd->uscsi_cdb[0]));
}
return (-1);
}
if (ucmd->uscsi_rqstatus != STATUS_GOOD) {
if (option_msg && diag_msg) {
err_print("Request sense status for command %s: 0x%x\n",
scsi_find_command_name(ucmd->uscsi_cdb[0]),
ucmd->uscsi_rqstatus);
}
return (-1);
}
rq = (struct scsi_extended_sense *)ucmd->uscsi_rqbuf;
rqlen = ucmd->uscsi_rqlen - ucmd->uscsi_rqresid;
if ((((int)rq->es_add_len) + 8) < MIN_REQUEST_SENSE_LEN ||
rq->es_class != CLASS_EXTENDED_SENSE ||
rqlen < MIN_REQUEST_SENSE_LEN) {
if (option_msg) {
err_print("Request sense for command %s failed\n",
scsi_find_command_name(ucmd->uscsi_cdb[0]));
}
if (option_msg && diag_msg) {
err_print("Sense data:\n");
dump("", (caddr_t)rqbuf, rqlen, HEX_ONLY);
}
if (errno == EIO) {
disk_error = DISK_STAT_UNAVAILABLE;
return (DSK_UNAVAILABLE);
} else {
return (-1);
}
}
if (ucmd->uscsi_cdb[0] == SCMD_MODE_SELECT) {
if (rq->es_key == KEY_RECOVERABLE_ERROR &&
rq->es_add_code == ROUNDED_PARAMETER &&
rq->es_qual_code == 0) {
return (0);
}
}
switch (rq->es_key) {
case KEY_NOT_READY:
disk_error = DISK_STAT_NOTREADY;
break;
case KEY_DATA_PROTECT:
disk_error = DISK_STAT_DATA_PROTECT;
break;
}
if (flags & F_ALLERRS) {
media_error = (rq->es_key == KEY_MEDIUM_ERROR);
}
if (!(flags & F_SILENT) || option_msg) {
scsi_printerr(ucmd, rq, rqlen);
}
if ((rq->es_key != KEY_RECOVERABLE_ERROR) || (flags & F_ALLERRS)) {
return (-1);
}
if (status == -1 && errno == EIO) {
disk_error = DISK_STAT_UNAVAILABLE;
return (DSK_UNAVAILABLE);
}
return (0);
}
int
uscsi_mode_sense(int fd, int page_code, int page_control, caddr_t page_data,
int page_size, struct scsi_ms_header *header)
{
caddr_t mode_sense_buf;
struct mode_header *hdr;
struct mode_page *pg;
int nbytes;
struct uscsi_cmd ucmd;
union scsi_cdb cdb;
int status;
int maximum;
assert(page_size >= 0 && page_size < 256);
assert(page_control == MODE_SENSE_PC_CURRENT ||
page_control == MODE_SENSE_PC_CHANGEABLE ||
page_control == MODE_SENSE_PC_DEFAULT ||
page_control == MODE_SENSE_PC_SAVED);
nbytes = sizeof (struct block_descriptor) +
sizeof (struct mode_header) + page_size;
nbytes = page_size;
if ((mode_sense_buf = malloc((uint_t)nbytes)) == NULL) {
err_print("cannot malloc %d bytes\n", nbytes);
return (-1);
}
(void) memset(mode_sense_buf, 0, nbytes);
(void) memset((char *)&ucmd, 0, sizeof (ucmd));
(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
cdb.scc_cmd = SCMD_MODE_SENSE;
FORMG0COUNT(&cdb, (uchar_t)nbytes);
cdb.cdb_opaque[2] = page_control | page_code;
ucmd.uscsi_cdb = (caddr_t)&cdb;
ucmd.uscsi_cdblen = CDB_GROUP0;
ucmd.uscsi_bufaddr = mode_sense_buf;
ucmd.uscsi_buflen = nbytes;
status = uscsi_cmd(fd, &ucmd,
(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
if (status) {
if (option_msg) {
err_print("Mode sense page 0x%x failed\n",
page_code);
}
free(mode_sense_buf);
return (-1);
}
hdr = (struct mode_header *)mode_sense_buf;
(void) memset((caddr_t)header, 0, sizeof (struct scsi_ms_header));
if (hdr->bdesc_length != sizeof (struct block_descriptor) &&
hdr->bdesc_length != 0) {
if (option_msg) {
err_print("\nMode sense page 0x%x: block "
"descriptor length %d incorrect\n",
page_code, hdr->bdesc_length);
if (diag_msg)
dump("Mode sense: ", mode_sense_buf,
nbytes, HEX_ONLY);
}
free(mode_sense_buf);
return (-1);
}
(void) memcpy((caddr_t)header, mode_sense_buf,
sizeof (struct mode_header) + hdr->bdesc_length);
pg = (struct mode_page *)((ulong_t)mode_sense_buf +
sizeof (struct mode_header) + hdr->bdesc_length);
if (pg->code != page_code) {
if (option_msg) {
err_print("\nMode sense page 0x%x: incorrect page "
"code 0x%x\n", page_code, pg->code);
if (diag_msg)
dump("Mode sense: ", mode_sense_buf,
nbytes, HEX_ONLY);
}
free(mode_sense_buf);
return (-1);
}
maximum = page_size - sizeof (struct mode_page) - hdr->bdesc_length;
if (((int)pg->length) > maximum) {
if (option_msg) {
err_print("Mode sense page 0x%x: incorrect page "
"length %d - expected max %d\n",
page_code, pg->length, maximum);
if (diag_msg)
dump("Mode sense: ", mode_sense_buf,
nbytes, HEX_ONLY);
}
free(mode_sense_buf);
return (-1);
}
(void) memcpy(page_data, (caddr_t)pg, MODESENSE_PAGE_LEN(pg));
if (option_msg && diag_msg) {
char *pc = find_string(page_control_strings, page_control);
err_print("\nMode sense page 0x%x (%s):\n", page_code,
pc != NULL ? pc : "");
dump("header: ", (caddr_t)header,
sizeof (struct scsi_ms_header), HEX_ONLY);
dump("data: ", page_data,
MODESENSE_PAGE_LEN(pg), HEX_ONLY);
}
free(mode_sense_buf);
return (0);
}
int
uscsi_mode_select(int fd, int page_code, int options, caddr_t page_data,
int page_size, struct scsi_ms_header *header)
{
caddr_t mode_select_buf;
int nbytes;
struct uscsi_cmd ucmd;
union scsi_cdb cdb;
int status;
assert(((struct mode_page *)page_data)->ps == 0);
assert(header->mode_header.length == 0);
assert(header->mode_header.device_specific == 0);
assert((options & ~(MODE_SELECT_SP|MODE_SELECT_PF)) == 0);
nbytes = sizeof (struct block_descriptor) +
sizeof (struct mode_header) + page_size;
if ((mode_select_buf = malloc((uint_t)nbytes)) == NULL) {
err_print("cannot malloc %d bytes\n", nbytes);
return (-1);
}
(void) memset(mode_select_buf, 0, nbytes);
nbytes = sizeof (struct mode_header);
if (header->mode_header.bdesc_length ==
sizeof (struct block_descriptor)) {
nbytes += sizeof (struct block_descriptor);
}
if (option_msg && diag_msg) {
char *s;
s = find_string(mode_select_strings,
options & (MODE_SELECT_SP|MODE_SELECT_PF));
err_print("\nMode select page 0x%x%s:\n", page_code,
s != NULL ? s : "");
dump("header: ", (caddr_t)header,
nbytes, HEX_ONLY);
dump("data: ", (caddr_t)page_data,
page_size, HEX_ONLY);
}
switch (page_code) {
case DAD_MODE_ERR_RECOV:
{
struct mode_err_recov *pd;
pd = (struct mode_err_recov *)(void *)page_data;
pd->recovery_time_limit = BE_16(pd->recovery_time_limit);
break;
}
case MODEPAGE_DISCO_RECO:
{
struct mode_disco_reco *pd;
pd = (struct mode_disco_reco *)(void *)page_data;
pd->bus_inactivity_limit = BE_16(pd->bus_inactivity_limit);
pd->disconect_time_limit = BE_16(pd->disconect_time_limit);
pd->connect_time_limit = BE_16(pd->connect_time_limit);
pd->max_burst_size = BE_16(pd->max_burst_size);
break;
}
case DAD_MODE_FORMAT:
{
struct mode_format *pd;
pd = (struct mode_format *)(void *)page_data;
pd->tracks_per_zone = BE_16(pd->tracks_per_zone);
pd->alt_sect_zone = BE_16(pd->alt_sect_zone);
pd->alt_tracks_zone = BE_16(pd->alt_tracks_zone);
pd->alt_tracks_vol = BE_16(pd->alt_tracks_vol);
pd->sect_track = BE_16(pd->sect_track);
pd->data_bytes_sect = BE_16(pd->data_bytes_sect);
pd->interleave = BE_16(pd->interleave);
pd->track_skew = BE_16(pd->track_skew);
pd->cylinder_skew = BE_16(pd->cylinder_skew);
break;
}
case DAD_MODE_GEOMETRY:
{
struct mode_geometry *pd;
pd = (struct mode_geometry *)(void *)page_data;
pd->step_rate = BE_16(pd->step_rate);
pd->rpm = BE_16(pd->rpm);
break;
}
case DAD_MODE_CACHE:
{
struct mode_cache *pd;
pd = (struct mode_cache *)(void *)page_data;
pd->dis_prefetch_len = BE_16(pd->dis_prefetch_len);
pd->min_prefetch = BE_16(pd->min_prefetch);
pd->max_prefetch = BE_16(pd->max_prefetch);
pd->prefetch_ceiling = BE_16(pd->prefetch_ceiling);
break;
}
case MODEPAGE_PDEVICE:
{
struct mode_pdevice *pd;
pd = (struct mode_pdevice *)(void *)page_data;
pd->if_ident = BE_16(pd->if_ident);
break;
}
case MODEPAGE_CTRL_MODE:
{
struct mode_control *pd;
pd = (struct mode_control *)(void *)page_data;
pd->ready_aen_holdoff = BE_16(pd->ready_aen_holdoff);
break;
}
}
(void) memcpy(mode_select_buf, (caddr_t)header, nbytes);
(void) memcpy(mode_select_buf + nbytes, page_data, page_size);
nbytes += page_size;
(void) memset((char *)&ucmd, 0, sizeof (ucmd));
(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
cdb.scc_cmd = SCMD_MODE_SELECT;
FORMG0COUNT(&cdb, (uchar_t)nbytes);
cdb.cdb_opaque[1] = (uchar_t)options;
ucmd.uscsi_cdb = (caddr_t)&cdb;
ucmd.uscsi_cdblen = CDB_GROUP0;
ucmd.uscsi_bufaddr = mode_select_buf;
ucmd.uscsi_buflen = nbytes;
status = uscsi_cmd(fd, &ucmd,
(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
if (status && option_msg) {
err_print("Mode select page 0x%x failed\n", page_code);
}
free(mode_select_buf);
return (status);
}
int
uscsi_inquiry(int fd, caddr_t inqbuf, int inqbufsiz)
{
struct uscsi_cmd ucmd;
union scsi_cdb cdb;
struct scsi_inquiry *inq;
int n;
int status;
assert(inqbufsiz >= sizeof (struct scsi_inquiry) && inqbufsiz < 256);
(void) memset((char *)inqbuf, 0, inqbufsiz);
(void) memset((char *)&ucmd, 0, sizeof (ucmd));
(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
cdb.scc_cmd = SCMD_INQUIRY;
FORMG0COUNT(&cdb, (uchar_t)inqbufsiz);
ucmd.uscsi_cdb = (caddr_t)&cdb;
ucmd.uscsi_cdblen = CDB_GROUP0;
ucmd.uscsi_bufaddr = (caddr_t)inqbuf;
ucmd.uscsi_buflen = inqbufsiz;
status = uscsi_cmd(fd, &ucmd,
(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
if (status) {
if (option_msg) {
err_print("Inquiry failed\n");
}
} else if (option_msg && diag_msg) {
inq = (struct scsi_inquiry *)inqbuf;
n = (int)inq->inq_len + 4;
n = min(n, inqbufsiz);
err_print("Inquiry:\n");
dump("", (caddr_t)inqbuf, n, HEX_ASCII);
}
return (status);
}
int
uscsi_inquiry_page_86h(int fd, caddr_t inqbuf, int inqbufsiz)
{
struct uscsi_cmd ucmd;
union scsi_cdb cdb;
int status;
assert(inqbuf);
assert(inqbufsiz >= sizeof (struct scsi_inquiry) &&
inqbufsiz < 256);
(void) memset((char *)inqbuf, 0, inqbufsiz);
(void) memset((char *)&ucmd, 0, sizeof (ucmd));
(void) memset((char *)&cdb, 0, sizeof (cdb));
cdb.scc_cmd = SCMD_INQUIRY;
FORMG0COUNT(&cdb, (uchar_t)inqbufsiz);
cdb.cdb_opaque[1] |= 0x01;
cdb.cdb_opaque[2] = 0x86;
ucmd.uscsi_cdb = (caddr_t)&cdb;
ucmd.uscsi_cdblen = CDB_GROUP0;
ucmd.uscsi_bufaddr = (caddr_t)inqbuf;
ucmd.uscsi_buflen = inqbufsiz;
status = uscsi_cmd(fd, &ucmd,
(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
if (status) {
if (option_msg) {
err_print("Inquriy with page_86h failed\n");
}
}
return (status);
}
int
uscsi_read_capacity_16(int fd, struct scsi_capacity_16 *capacity)
{
struct uscsi_cmd ucmd;
union scsi_cdb cdb;
int status;
(void) memset((char *)capacity, 0, sizeof (struct scsi_capacity_16));
(void) memset((char *)&ucmd, 0, sizeof (ucmd));
(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
ucmd.uscsi_cdb = (caddr_t)&cdb;
ucmd.uscsi_cdblen = CDB_GROUP4;
ucmd.uscsi_bufaddr = (caddr_t)capacity;
ucmd.uscsi_buflen = sizeof (struct scsi_capacity_16);
cdb.scc_cmd = SCMD_SVC_ACTION_IN_G4;
cdb.cdb_opaque[1] = SSVC_ACTION_READ_CAPACITY_G4;
cdb.cdb_opaque[10] =
(uchar_t)((ucmd.uscsi_buflen & 0xff000000) >> 24);
cdb.cdb_opaque[11] =
(uchar_t)((ucmd.uscsi_buflen & 0x00ff0000) >> 16);
cdb.cdb_opaque[12] =
(uchar_t)((ucmd.uscsi_buflen & 0x0000ff00) >> 8);
cdb.cdb_opaque[13] =
(uchar_t)(ucmd.uscsi_buflen & 0x000000ff);
status = uscsi_cmd(fd, &ucmd,
(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
if (status) {
if (option_msg) {
err_print("Read capacity 16 failed\n");
}
} else if (option_msg && diag_msg) {
dump("Capacity: ", (caddr_t)capacity,
sizeof (struct scsi_capacity_16), HEX_ONLY);
}
capacity->sc_capacity = BE_64(capacity->sc_capacity);
capacity->sc_lbasize = BE_32(capacity->sc_lbasize);
return (status);
}
int
uscsi_read_capacity(int fd, struct scsi_capacity_16 *capacity)
{
struct uscsi_cmd ucmd;
union scsi_cdb cdb;
int status;
struct scsi_capacity cap_old;
(void) memset((char *)capacity, 0, sizeof (struct scsi_capacity_16));
(void) memset((char *)&cap_old, 0, sizeof (struct scsi_capacity));
(void) memset((char *)&ucmd, 0, sizeof (ucmd));
(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
cdb.scc_cmd = SCMD_READ_CAPACITY;
ucmd.uscsi_cdb = (caddr_t)&cdb;
ucmd.uscsi_cdblen = CDB_GROUP1;
ucmd.uscsi_bufaddr = (caddr_t)&cap_old;
ucmd.uscsi_buflen = sizeof (struct scsi_capacity);
status = uscsi_cmd(fd, &ucmd,
(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
if (cap_old.capacity == UINT_MAX32) {
(void) memset((char *)&ucmd, 0, sizeof (ucmd));
(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
ucmd.uscsi_cdb = (caddr_t)&cdb;
ucmd.uscsi_cdblen = CDB_GROUP4;
ucmd.uscsi_bufaddr = (caddr_t)capacity;
ucmd.uscsi_buflen = sizeof (struct scsi_capacity_16);
cdb.scc_cmd = SCMD_SVC_ACTION_IN_G4;
cdb.cdb_opaque[1] = SSVC_ACTION_READ_CAPACITY_G4;
cdb.cdb_opaque[10] =
(uchar_t)((ucmd.uscsi_buflen & 0xff000000) >> 24);
cdb.cdb_opaque[11] =
(uchar_t)((ucmd.uscsi_buflen & 0x00ff0000) >> 16);
cdb.cdb_opaque[12] =
(uchar_t)((ucmd.uscsi_buflen & 0x0000ff00) >> 8);
cdb.cdb_opaque[13] =
(uchar_t)(ucmd.uscsi_buflen & 0x000000ff);
status = uscsi_cmd(fd, &ucmd,
(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
}
if (status) {
if (option_msg) {
if (cdb.scc_cmd == SCMD_READ_CAPACITY) {
err_print("Read capacity failed\n");
} else {
err_print("Read capacity 16 failed\n");
}
}
} else if (option_msg && diag_msg) {
if (cap_old.capacity == UINT_MAX32) {
dump("Capacity: ", (caddr_t)capacity,
sizeof (struct scsi_capacity_16), HEX_ONLY);
} else {
dump("Capacity: ", (caddr_t)&cap_old,
sizeof (struct scsi_capacity), HEX_ONLY);
}
}
if (cap_old.capacity == UINT_MAX32) {
capacity->sc_capacity = BE_64(capacity->sc_capacity);
capacity->sc_lbasize = BE_32(capacity->sc_lbasize);
} else {
capacity->sc_capacity = (uint64_t)BE_32(cap_old.capacity);
capacity->sc_lbasize = BE_32(cap_old.lbasize);
}
return (status);
}
static int
uscsi_reserve_release(int fd __maybe_unused, int cmd __maybe_unused)
{
int status = 0;
#ifdef sparc
struct uscsi_cmd ucmd;
union scsi_cdb cdb;
(void) memset((char *)&ucmd, 0, sizeof (ucmd));
(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
cdb.scc_cmd = (cmd == SCMD_RESERVE) ? SCMD_RESERVE : SCMD_RELEASE;
ucmd.uscsi_cdb = (caddr_t)&cdb;
ucmd.uscsi_cdblen = CDB_GROUP0;
status = uscsi_cmd(fd, &ucmd,
(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
if (status) {
(void) memset((char *)&ucmd, 0, sizeof (ucmd));
(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
ucmd.uscsi_cdb = (caddr_t)&cdb;
cdb.scc_cmd = (cmd == SCMD_RESERVE) ?
SCMD_RESERVE_G1 : SCMD_RELEASE_G1;
ucmd.uscsi_cdblen = CDB_GROUP1;
status = uscsi_cmd(fd, &ucmd,
(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
if (status) {
if (option_msg) {
err_print("%s failed\n", (cmd == SCMD_RESERVE) ?
"Reserve" : "Release");
}
}
}
#endif
return (status);
}
int
scsi_dump_mode_sense_pages(int page_control)
{
struct uscsi_cmd ucmd;
union scsi_cdb cdb;
char *msbuf;
int nbytes;
char *pc_str;
int status;
struct mode_header *mh;
char *p;
struct mode_page *mp;
int n;
char s[16];
int result = 0;
pc_str = find_string(page_control_strings, page_control);
nbytes = 255;
msbuf = (char *)zalloc(nbytes);
(void) memset(msbuf, 0, nbytes);
(void) memset((char *)&ucmd, 0, sizeof (ucmd));
(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
cdb.scc_cmd = SCMD_MODE_SENSE;
FORMG0COUNT(&cdb, (uchar_t)nbytes);
cdb.cdb_opaque[2] = page_control | 0x3f;
ucmd.uscsi_cdb = (caddr_t)&cdb;
ucmd.uscsi_cdblen = CDB_GROUP0;
ucmd.uscsi_bufaddr = msbuf;
ucmd.uscsi_buflen = nbytes;
status = uscsi_cmd(cur_file, &ucmd,
(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
if (status) {
err_print("\nMode sense page 0x3f (%s) failed\n",
pc_str);
result = 1;
} else {
err_print("\nMode sense pages (%s):\n", pc_str);
mh = (struct mode_header *)msbuf;
nbytes = mh->length - sizeof (struct mode_header) -
mh->bdesc_length + 1;
p = msbuf + sizeof (struct mode_header) +
mh->bdesc_length;
dump(" ", msbuf, sizeof (struct mode_header) +
(int)mh->bdesc_length, HEX_ONLY);
while (nbytes > 0) {
mp = (struct mode_page *)p;
n = mp->length + sizeof (struct mode_page);
nbytes -= n;
if (nbytes < 0)
break;
(void) sprintf(s, " %3x: ", mp->code);
dump(s, p, n, HEX_ONLY);
p += n;
}
if (nbytes < 0) {
err_print(" Sense data formatted incorrectly:\n");
dump(" ", msbuf, (int)mh->length + 1, HEX_ONLY);
result = 1;
}
err_print("\n");
}
free(msbuf);
return (result);
}
static void
scsi_printerr(struct uscsi_cmd *ucmd, struct scsi_extended_sense *rq, int rqlen)
{
diskaddr_t blkno;
struct scsi_descr_sense_hdr *sdsp =
(struct scsi_descr_sense_hdr *)rq;
switch (rq->es_key) {
case KEY_NO_SENSE:
err_print("No sense error");
break;
case KEY_RECOVERABLE_ERROR:
err_print("Recoverable error");
break;
case KEY_NOT_READY:
err_print("Not ready error");
break;
case KEY_MEDIUM_ERROR:
err_print("Medium error");
break;
case KEY_HARDWARE_ERROR:
err_print("Hardware error");
break;
case KEY_ILLEGAL_REQUEST:
err_print("Illegal request");
break;
case KEY_UNIT_ATTENTION:
err_print("Unit attention error");
break;
case KEY_WRITE_PROTECT:
err_print("Write protect error");
break;
case KEY_BLANK_CHECK:
err_print("Blank check error");
break;
case KEY_VENDOR_UNIQUE:
err_print("Vendor unique error");
break;
case KEY_COPY_ABORTED:
err_print("Copy aborted error");
break;
case KEY_ABORTED_COMMAND:
err_print("Aborted command");
break;
case KEY_EQUAL:
err_print("Equal error");
break;
case KEY_VOLUME_OVERFLOW:
err_print("Volume overflow");
break;
case KEY_MISCOMPARE:
err_print("Miscompare error");
break;
case KEY_RESERVED:
err_print("Reserved error");
break;
default:
err_print("Unknown error");
break;
}
err_print(" during %s", scsi_find_command_name(ucmd->uscsi_cdb[0]));
switch (rq->es_code) {
case CODE_FMT_DESCR_CURRENT:
case CODE_FMT_DESCR_DEFERRED:
blkno =
(diskaddr_t)scsi_extract_sense_info_descr(sdsp, rqlen);
if (blkno != (diskaddr_t)-1) {
err_print(": block %lld (0x%llx) (", blkno, blkno);
pr_dblock(err_print, blkno);
err_print(")");
}
err_print("\n");
err_print("ASC: 0x%x ASCQ: 0x%x\n",
sdsp->ds_add_code, sdsp->ds_qual_code);
break;
case CODE_FMT_FIXED_CURRENT:
case CODE_FMT_FIXED_DEFERRED:
default:
if (rq->es_valid) {
blkno = (rq->es_info_1 << 24) |
(rq->es_info_2 << 16) |
(rq->es_info_3 << 8) | rq->es_info_4;
err_print(": block %lld (0x%llx) (", blkno, blkno);
pr_dblock(err_print, blkno);
err_print(")");
}
err_print("\n");
if (rq->es_add_len >= 6) {
err_print("ASC: 0x%x ASCQ: 0x%x\n",
rq->es_add_code, rq->es_qual_code);
}
break;
}
if (option_msg && diag_msg) {
if (rq->es_key == KEY_ILLEGAL_REQUEST) {
dump("cmd: ", (caddr_t)ucmd,
sizeof (struct uscsi_cmd), HEX_ONLY);
dump("cdb: ", (caddr_t)ucmd->uscsi_cdb,
ucmd->uscsi_cdblen, HEX_ONLY);
}
dump("sense: ", (caddr_t)rq, rqlen, HEX_ONLY);
}
if (option_msg) {
switch (rq->es_code) {
case CODE_FMT_DESCR_CURRENT:
case CODE_FMT_DESCR_DEFERRED:
scsi_print_descr_sense(sdsp, rqlen);
break;
case CODE_FMT_FIXED_CURRENT:
case CODE_FMT_FIXED_DEFERRED:
default:
scsi_print_extended_sense(rq, rqlen);
break;
}
}
}
static diskaddr_t
scsi_extract_sense_info_descr(struct scsi_descr_sense_hdr *sdsp, int rqlen)
{
diskaddr_t result;
uint8_t *descr_offset;
int valid_sense_length;
struct scsi_information_sense_descr *isd;
result = (diskaddr_t)-1;
descr_offset = (uint8_t *)(sdsp+1);
valid_sense_length =
min((sizeof (struct scsi_descr_sense_hdr) +
sdsp->ds_addl_sense_length),
rqlen);
while ((descr_offset + sizeof (struct scsi_information_sense_descr)) <=
(uint8_t *)sdsp + valid_sense_length) {
isd = (struct scsi_information_sense_descr *)descr_offset;
if (isd->isd_descr_type == DESCR_INFORMATION) {
result =
(((diskaddr_t)isd->isd_information[0] << 56) |
((diskaddr_t)isd->isd_information[1] << 48) |
((diskaddr_t)isd->isd_information[2] << 40) |
((diskaddr_t)isd->isd_information[3] << 32) |
((diskaddr_t)isd->isd_information[4] << 24) |
((diskaddr_t)isd->isd_information[5] << 16) |
((diskaddr_t)isd->isd_information[6] << 8) |
((diskaddr_t)isd->isd_information[7]));
break;
}
descr_offset += (isd->isd_addl_length + 2);
}
return (result);
}
static char *
scsi_find_command_name(uint_t cmd)
{
struct scsi_command_name *c;
for (c = scsi_command_names; c->command != SCMD_UNKNOWN; c++)
if (c->command == cmd)
break;
return (c->name);
}
int
scsi_supported_page(int page)
{
return (page == 1 || page == 2 || page == 3 || page == 4 ||
page == 8 || page == 0x38);
}
int
apply_chg_list(int pageno, int pagsiz, uchar_t *curbits,
uchar_t *chgbits, struct chg_list *chglist)
{
uchar_t c;
int i;
int m;
int delta;
int changed = 0;
while (chglist != NULL) {
if (chglist->pageno == pageno &&
chglist->byteno < pagsiz) {
i = chglist->byteno;
c = curbits[i];
switch (chglist->mode) {
case CHG_MODE_SET:
c |= (uchar_t)chglist->value;
break;
case CHG_MODE_CLR:
c &= (uchar_t)chglist->value;
break;
case CHG_MODE_ABS:
c = (uchar_t)chglist->value;
break;
}
delta = c ^ curbits[i];
for (m = 0x01; m < 0x100; m <<= 1) {
if ((delta & m) && (chgbits[i] & m)) {
curbits[i] ^= m;
changed = 1;
}
}
}
chglist = chglist->next;
}
return (changed);
}
static int
chg_list_affects_page(struct chg_list *chglist, int pageno)
{
while (chglist != NULL) {
if (chglist->pageno == pageno) {
return (1);
}
chglist = chglist->next;
}
return (0);
}
static char *scsi_extended_sense_labels[] = {
"Request sense valid: ",
"Error class and code: ",
"Segment number: ",
"Filemark: ",
"End-of-medium: ",
"Incorrect length indicator: ",
"Sense key: ",
"Information field: ",
"Additional sense length: ",
"Command-specific information: ",
"Additional sense code: ",
"Additional sense code qualifier: ",
"Field replaceable unit code: ",
"Sense-key specific: ",
"Additional sense bytes: "
};
static void
scsi_print_extended_sense(struct scsi_extended_sense *rq, int rqlen)
{
char **p;
p = scsi_extended_sense_labels;
if (rqlen < (sizeof (*rq) - 2) || !rq->es_valid) {
return;
}
fmt_print("\n%s%s\n", *p++, rq->es_valid ? "yes" : "no");
fmt_print("%s0x%02x\n", *p++, (rq->es_class << 4) + rq->es_code);
fmt_print("%s%d\n", *p++, rq->es_segnum);
fmt_print("%s%s\n", *p++, rq->es_filmk ? "yes" : "no");
fmt_print("%s%s\n", *p++, rq->es_eom ? "yes" : "no");
fmt_print("%s%s\n", *p++, rq->es_ili ? "yes" : "no");
fmt_print("%s%d\n", *p++, rq->es_key);
fmt_print("%s0x%02x 0x%02x 0x%02x 0x%02x\n", *p++, rq->es_info_1,
rq->es_info_2, rq->es_info_3, rq->es_info_4);
fmt_print("%s%d\n", *p++, rq->es_add_len);
fmt_print("%s0x%02x 0x%02x 0x%02x 0x%02x\n", *p++, rq->es_cmd_info[0],
rq->es_cmd_info[1], rq->es_cmd_info[2], rq->es_cmd_info[3]);
fmt_print("%s0x%02x = %d\n", *p++, rq->es_add_code, rq->es_add_code);
fmt_print("%s0x%02x = %d\n", *p++, rq->es_qual_code, rq->es_qual_code);
fmt_print("%s%d\n", *p++, rq->es_fru_code);
fmt_print("%s0x%02x 0x%02x 0x%02x\n", *p++, rq->es_skey_specific[0],
rq->es_skey_specific[1], rq->es_skey_specific[2]);
if (rqlen >= sizeof (*rq)) {
fmt_print("%s0x%02x 0x%02x%s\n", *p, rq->es_add_info[0],
rq->es_add_info[1], (rqlen > sizeof (*rq)) ? " ..." : "");
}
fmt_print("\n");
}
static char *scsi_descr_sense_labels[] = {
"Error class and code: ",
"Sense key: ",
"Additional sense length: ",
"Additional sense code: ",
"Additional sense code qualifier: ",
"Additional sense bytes: "
};
static void
scsi_print_descr_sense(struct scsi_descr_sense_hdr *rq, int rqlen)
{
char **p;
uint8_t *descr_offset;
int valid_sense_length;
struct scsi_information_sense_descr *isd;
p = scsi_descr_sense_labels;
if (rqlen < sizeof (struct scsi_descr_sense_hdr)) {
return;
}
fmt_print("%s0x%02x\n", *p++, (rq->ds_class << 4) + rq->ds_code);
fmt_print("%s%d\n", *p++, rq->ds_key);
fmt_print("%s%d\n", *p++, rq->ds_addl_sense_length);
fmt_print("%s0x%02x = %d\n", *p++, rq->ds_add_code, rq->ds_add_code);
fmt_print("%s0x%02x = %d\n", *p++, rq->ds_qual_code, rq->ds_qual_code);
fmt_print("\n");
descr_offset = (uint8_t *)(rq+1);
valid_sense_length =
min((sizeof (struct scsi_descr_sense_hdr) +
rq->ds_addl_sense_length), rqlen);
while ((descr_offset + *(descr_offset + 1)) <=
(uint8_t *)rq + valid_sense_length) {
isd = (struct scsi_information_sense_descr *)descr_offset;
switch (isd->isd_descr_type) {
case DESCR_INFORMATION: {
uint64_t information;
information =
(((uint64_t)isd->isd_information[0] << 56) |
((uint64_t)isd->isd_information[1] << 48) |
((uint64_t)isd->isd_information[2] << 40) |
((uint64_t)isd->isd_information[3] << 32) |
((uint64_t)isd->isd_information[4] << 24) |
((uint64_t)isd->isd_information[5] << 16) |
((uint64_t)isd->isd_information[6] << 8) |
((uint64_t)isd->isd_information[7]));
fmt_print("Information field: "
"%0llx\n", information);
break;
}
case DESCR_COMMAND_SPECIFIC: {
struct scsi_cmd_specific_sense_descr *c =
(struct scsi_cmd_specific_sense_descr *)isd;
uint64_t cmd_specific;
cmd_specific =
(((uint64_t)c->css_cmd_specific_info[0] << 56) |
((uint64_t)c->css_cmd_specific_info[1] << 48) |
((uint64_t)c->css_cmd_specific_info[2] << 40) |
((uint64_t)c->css_cmd_specific_info[3] << 32) |
((uint64_t)c->css_cmd_specific_info[4] << 24) |
((uint64_t)c->css_cmd_specific_info[5] << 16) |
((uint64_t)c->css_cmd_specific_info[6] << 8) |
((uint64_t)c->css_cmd_specific_info[7]));
fmt_print("Command-specific information: "
"%0llx\n", cmd_specific);
break;
}
case DESCR_SENSE_KEY_SPECIFIC: {
struct scsi_sk_specific_sense_descr *ssd =
(struct scsi_sk_specific_sense_descr *)isd;
uint8_t *sk_spec_ptr = (uint8_t *)&ssd->sss_data;
fmt_print("Sense-key specific: "
"0x%02x 0x%02x 0x%02x\n", sk_spec_ptr[0],
sk_spec_ptr[1], sk_spec_ptr[2]);
break;
}
case DESCR_FRU: {
struct scsi_fru_sense_descr *fsd =
(struct scsi_fru_sense_descr *)isd;
fmt_print("Field replaceable unit code: "
"%d\n", fsd->fs_fru_code);
break;
}
case DESCR_BLOCK_COMMANDS: {
struct scsi_block_cmd_sense_descr *bsd =
(struct scsi_block_cmd_sense_descr *)isd;
fmt_print("Incorrect length indicator: "
"%s\n", bsd->bcs_ili ? "yes" : "no");
break;
}
default:
break;
}
descr_offset += (isd->isd_addl_length + 2);
}
fmt_print("\n");
}
static int
check_support_for_defects(void)
{
struct uscsi_cmd ucmd;
union scsi_cdb cdb;
struct scsi_defect_list def_list;
struct scsi_defect_hdr *hdr;
int status;
char rqbuf[255];
struct scsi_extended_sense *rq;
hdr = (struct scsi_defect_hdr *)&def_list;
(void) memset((char *)&def_list, 0, sizeof (def_list));
(void) memset((char *)&ucmd, 0, sizeof (ucmd));
(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
(void) memset((char *)rqbuf, 0, 255);
cdb.scc_cmd = SCMD_READ_DEFECT_LIST;
FORMG1COUNT(&cdb, sizeof (struct scsi_defect_hdr));
cdb.cdb_opaque[2] = DLD_MAN_DEF_LIST | DLD_BFI_FORMAT;
ucmd.uscsi_cdb = (caddr_t)&cdb;
ucmd.uscsi_cdblen = CDB_GROUP1;
ucmd.uscsi_bufaddr = (caddr_t)hdr;
ucmd.uscsi_buflen = sizeof (struct scsi_defect_hdr);
ucmd.uscsi_rqbuf = rqbuf;
ucmd.uscsi_rqlen = sizeof (rqbuf);
ucmd.uscsi_rqresid = sizeof (rqbuf);
rq = (struct scsi_extended_sense *)ucmd.uscsi_rqbuf;
status = uscsi_cmd(cur_file, &ucmd,
(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
if (status != 0) {
if (ucmd.uscsi_rqstatus == STATUS_GOOD &&
rq->es_key == KEY_ILLEGAL_REQUEST &&
rq->es_add_code == INVALID_OPCODE)
return (0);
}
return (1);
}
static int
scsi_format_without_defects(void)
{
struct uscsi_cmd ucmd;
union scsi_cdb cdb;
struct scsi_defect_hdr defect_hdr;
int status;
(void) memset((char *)&ucmd, 0, sizeof (ucmd));
(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
(void) memset((char *)&defect_hdr, 0, sizeof (defect_hdr));
cdb.scc_cmd = SCMD_FORMAT;
ucmd.uscsi_cdb = (caddr_t)&cdb;
ucmd.uscsi_cdblen = CDB_GROUP0;
ucmd.uscsi_bufaddr = (caddr_t)&defect_hdr;
ucmd.uscsi_buflen = sizeof (defect_hdr);
cdb.cdb_opaque[1] = 0;
status = uscsi_cmd(cur_file, &ucmd,
(option_msg && diag_msg) ? F_NORMAL : F_SILENT);
return (status);
}
static int test_until_ready(int fd) {
int status = 1;
struct uscsi_cmd ucmd;
union scsi_cdb cdb;
struct scsi_extended_sense sense;
time_t start, check, time_left;
uint16_t progress;
int hour, min, sec;
(void) memset((char *)&ucmd, 0, sizeof (ucmd));
(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
ucmd.uscsi_cdb = (caddr_t)&cdb;
ucmd.uscsi_cdblen = CDB_GROUP0;
ucmd.uscsi_rqbuf = (caddr_t)&sense;
ucmd.uscsi_rqlen = SENSE_LEN;
start = check = time((time_t *)0);
while (status) {
ucmd.uscsi_rqstatus = 0;
ucmd.uscsi_rqresid = 0;
(void) memset((char *)&sense, 0, SENSE_LEN);
status = uscsi_cmd(fd, &ucmd, F_SILENT
| F_RQENABLE);
check = time((time_t *)0);
if (status != 0 && errno == EIO) {
if (sense.es_skey_specific[0] == 0x80) {
progress = ((uint16_t)sense.
es_skey_specific[1]) << 8;
progress |= (uint16_t)sense.
es_skey_specific[2];
progress = (uint16_t)(((float)progress /
(float)PROGRESS_INDICATION_BASE)*100);
fmt_print("\015");
if ((check - start) <= 0 || progress <= 5) {
fmt_print(" %02d%% complete ",
progress);
} else {
time_left = (time_t)(((float)(check
- start) / (float)progress) *
(float)(100 - progress));
sec = time_left % 60;
min = (time_left / 60) % 60;
hour = time_left / 3600;
fmt_print(" %02d%% complete "
"(%02d:%02d:%02d remaining) ",
progress, hour, min, sec);
}
(void) fflush(stdout);
}
} else {
if (option_msg) {
fmt_print("\nRequest Sense ASC=0x%x ASCQ=0x%x",
sense.es_add_code, sense.es_qual_code);
}
break;
}
(void) sleep(RETRY_DELAY);
}
return (status);
}
uint8_t
get_cur_protection_type(struct scsi_capacity_16 *capacity)
{
uint8_t cp13;
uint8_t prot_en;
uint8_t p_type;
cp13 = ((capacity->sc_rsvd0 & 0x3f) << 2)
| ((capacity->sc_prot_en & 0x01) << 1)
| (capacity->sc_rto_en & 0x01);
prot_en = cp13 & 0x01;
if (prot_en == 0) {
p_type = 0;
} else {
p_type = (cp13 << 4) >> 5;
p_type += 1;
}
return (p_type);
}