Files
Arquivotheca.SunOS-4.1.4/usr.etc/format/ctlr_scsi.c
seta75D ff309bfe1c Init
2021-10-11 18:37:13 -03:00

2498 lines
61 KiB
C

#ifndef lint
#ident "@(#)ctlr_scsi.c 1.1 94/10/31 SMI"
#endif lint
/*
* Copyright (c) 1991 by Sun Microsystems, Inc.
*/
/*
* This file contains the routines for embedded scsi disks
*/
#include "global.h"
#include <sys/types.h>
#include <sys/dk.h>
#include <sys/ioctl.h>
#include <sys/uio.h>
#include <sys/fcntl.h>
#include <assert.h>
#include <memory.h>
#include <malloc.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <scsi/generic/mode.h>
#include <scsi/generic/sense.h>
#include <scsi/generic/commands.h>
#include <scsi/generic/dad_mode.h>
#include <scsi/impl/mode.h>
#include <scsi/impl/uscsi.h>
#include <scsi/impl/commands.h>
#include <scsi/targets/sddef.h>
#include <sun/dkio.h>
extern int errno;
#ifndef DAD_MODE_CACHE_CCS
#define DAD_MODE_CACHE_CCS 0x38
#endif
#include "startup.h"
#include "scsi_com.h"
#include "misc.h"
#include "ctlr_scsi.h"
#include "analyze.h"
#include "param.h"
#include "io.h"
struct ctlr_ops scsiops = {
scsi_rdwr,
scsi_ck_format,
scsi_format,
scsi_ex_man,
scsi_ex_cur,
scsi_repair,
0,
};
#define SCMD_UNKNOWN 0xff
/*
* Names of commands. Must have SCMD_UNKNOWN at end of list.
*/
struct scsi_command_name {
int 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_REQUEST_SENSE, "request sense",
SCMD_READ_DEFECT_LIST, "read defect list",
SCMD_UNKNOWN, "unknown"
};
/*
* Strings for printing mode sense page control values
*/
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 }
};
/*
* Strings for printing the mode select options
*/
slist_t mode_select_strings[] = {
{ "", 0 },
{ " (pf)", MODE_SELECT_PF },
{ " (sp)", MODE_SELECT_SP },
{ " (pf,sp)", MODE_SELECT_PF|MODE_SELECT_SP }
};
/*
* Enable extensive diagnostic messages
*/
int diag_msg = 1;
/*
* Read or write the disk.
*/
int
scsi_rdwr(dir, fd, blkno, secnt, bufaddr, flags)
int dir;
int fd;
daddr_t blkno;
int secnt;
caddr_t bufaddr;
int flags;
{
struct uscsi_cmd ucmd;
union scsi_cdb cdb;
/*
* Build and execute the uscsi ioctl. We build a group0
* or group1 command as necessary, since some targets
* do not support group1 commands.
*/
(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) && secnt <= 0xff) {
FORMG0ADDR(&cdb, blkno);
FORMG0COUNT(&cdb, secnt);
ucmd.uscsi_cdblen = CDB_GROUP0;
} else {
FORMG1ADDR(&cdb, blkno);
FORMG1COUNT(&cdb, secnt);
ucmd.uscsi_cdblen = CDB_GROUP1;
cdb.scc_cmd |= SCMD_GROUP1;
}
ucmd.uscsi_cdb = (caddr_t) &cdb;
ucmd.uscsi_bufaddr = bufaddr;
ucmd.uscsi_buflen = secnt * SECSIZE;
return (uscsi_cmd(fd, &ucmd, flags));
}
/*
* Check to see if the disk has been formatted.
* If we are able to read the first track, we conclude that
* the disk has been formatted.
*/
int
scsi_ck_format()
{
int status;
/*
* Try to read the first four blocks.
*/
status = scsi_rdwr(DIR_READ, cur_file, (daddr_t) 0, 4,
(caddr_t) cur_buf, F_SILENT);
return (!status);
}
/*
* Format the disk, the whole disk, and nothing but the disk.
*/
/*ARGSUSED*/
int
scsi_format(start, end, list)
int start; /* irrelevant for us */
int end;
struct defect_list *list;
{
struct uscsi_cmd ucmd;
union scsi_cdb cdb;
struct scsi_defect_hdr defect_hdr;
int status;
int flag;
struct scsi_inquiry *inq;
char rawbuf[MAX_MODE_SENSE_SIZE];
/*
* Determine if the target appears to be SCSI-2
* compliant. We handle mode sense/mode selects
* a little differently, depending upon CCS/SCSI-2
*/
if (uscsi_inquiry(cur_file, rawbuf, sizeof (rawbuf))) {
eprint("Inquiry failed\n");
return (-1);
}
inq = (struct scsi_inquiry *) rawbuf;
flag = (inq->inq_rdf == RDF_SCSI2);
/*
* Set up the various SCSI parameters specified before
* formatting the disk. Each routine handles the
* parameters relevent to a particular page.
* If no parameters are specified for a page, there's
* no need to do anything. Otherwise, issue a mode
* sense for that page. If a specified parameter
* differs from the drive's default value, and that
* parameter is not fixed, then issue a mode select to
* set the default value for the disk as specified
* in format.dat.
*/
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)) {
return (-1);
}
/*
* If we're debugging the drive, dump every page
* the device supports, for thorough analysis.
*/
if (option_msg && diag_msg) {
scsi_dump_mode_sense_pages(MODE_SENSE_PC_DEFAULT);
scsi_dump_mode_sense_pages(MODE_SENSE_PC_CURRENT);
scsi_dump_mode_sense_pages(MODE_SENSE_PC_SAVED);
scsi_dump_mode_sense_pages(MODE_SENSE_PC_CHANGEABLE);
eprint("\n");
}
if (sun4_flag) {
int i;
int num_defects;
struct defect_entry *defect;
struct dk_cmd dkcmd;
struct defect_list glist;
struct scsi_format_params *format_params;
if (list->flags & LIST_PGLIST) {
/*
* Format with both the P&G lists. Since the
* sun4 driver interface does not allow us to
* control the cdb options, we extract the
* G list, and send it down with the format
* command itself. Hopefully this is an
* equivalent operation.
*/
if (scsi_ex_grown(&glist)) {
eprint("Cannot extract the grown defects list\n");
return (-1);
}
} else {
/*
* Format with just the P list. Here we
* just fake out an empty G list.
*/
glist.header.count = 0;
}
format_params = (struct scsi_format_params *)
zalloc((glist.header.count * 8) + 4);
num_defects = 0;
defect = glist.list;
for (i = 0; i < glist.header.count; i++) {
if (defect->bfi != -1) {
format_params->list[i].cyl = defect->cyl;
format_params->list[i].head = defect->head;
format_params->list[i].bytes_from_index =
defect->bfi;
num_defects++;
}
defect++;
}
format_params->length = num_defects * 8;
dkcmd.dkc_cmd = SCMD_FORMAT;
dkcmd.dkc_bufaddr = (caddr_t) format_params;
dkcmd.dkc_buflen = (num_defects * 8) + 4;
/*
* Issue the format ioctl
*/
if (option_msg && diag_msg) {
printf("\nFormatting...\n");
}
(void) fflush(stdout);
flag = (option_msg && diag_msg) ? F_NORMAL : F_SILENT;
status = scsi_dkcmd(cur_file, &dkcmd, flag);
} else {
/*
* Construct the uscsi format ioctl. The form depends
* upon the defect list the user extracted. If s/he
* extracted the "original" list, we format with only
* the P (manufacturer's defect) list. Otherwise, we
* format with both the P and the G (grown) list.
* To format with the P and G list, we set the fmtData
* bit, and send an empty list. To format with the
* P list only, we also set the cmpLst bit, meaning
* that the (empty) list we send down is the complete
* G list, thereby discarding the old G list..
*/
(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] = FPB_DATA;
if ((list->flags & LIST_PGLIST) == 0) {
/*
* No G list. The empty list we
* send down is the complete list.
*/
cdb.cdb_opaque[1] |= FPB_CMPLT;
}
/*
* Issue the format ioctl
*/
if (option_msg && diag_msg) {
printf("\nFormatting...\n");
}
(void) fflush(stdout);
flag = (option_msg && diag_msg) ? F_NORMAL : F_SILENT;
status = uscsi_cmd(cur_file, &ucmd, flag);
}
return (status);
}
/*
* Check disk error recovery parameters via mode sense.
* Issue a mode select if we need to change something.
*/
/*ARGSUSED*/
int
scsi_ms_page1(scsi2_flag)
int scsi2_flag;
{
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 debugging, issue mode senses on the default and
* current values.
*/
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);
}
/*
* Issue a mode sense to determine the saved parameters
* If the device cannot return the saved parameters,
* use the current, instead.
*/
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);
}
}
/*
* We only need the common subset between the CCS
* and SCSI-2 structures, so we can treat both
* cases identically. Whatever the drive gives
* us, we return to the drive in the mode select,
* delta'ed by whatever we want to change.
*/
length = MODESENSE_PAGE_LEN(page1);
if (length < MIN_PAGE1_LEN) {
return (0);
}
/*
* Ask for changeable parameters.
*/
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);
}
/*
* We need to issue a mode select only if one or more
* parameters need to be changed, and those parameters
* are flagged by the drive as changeable.
*/
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;
}
}
/*
* Report any changes so far...
*/
if (flag && option_msg) {
eprint(
"PAGE 1: read retries= %d (%d) write retries= %d (%d)\n",
page1->read_retry_count, tmp1,
page1->write_retry_count, tmp2);
}
/*
* Apply any changes requested via the change list method
*/
flag |= apply_chg_list(DAD_MODE_ERR_RECOV, length,
(u_char *) page1, (u_char *) fixed,
cur_dtype->dtype_chglist);
/*
* If no changes required, do not issue a mode select
*/
if (flag == 0) {
return (0);
}
/*
* We always want to set the Page Format bit for mode
* selects. Set the Save Page bit if the drive indicates
* that it can save this page via the mode sense.
*/
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)) {
/* If failed, try not saving mode select params. */
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) {
eprint("\
Warning: Using default error recovery parameters.\n\n");
}
/*
* If debugging, issue mode senses on the current and
* saved values, so we can see the result of the mode
* selects.
*/
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);
}
/*
* Check disk disconnect/reconnect parameters via mode sense.
* Issue a mode select if we need to change something.
*/
/*ARGSUSED*/
int
scsi_ms_page2(scsi2_flag)
int scsi2_flag;
{
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 debugging, issue mode senses on the default and
* current values.
*/
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);
}
/*
* Issue a mode sense to determine the saved parameters
* If the device cannot return the saved parameters,
* use the current, instead.
*/
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);
}
}
/*
* We only need the common subset between the CCS
* and SCSI-2 structures, so we can treat both
* cases identically. Whatever the drive gives
* us, we return to the drive in the mode select,
* delta'ed by whatever we want to change.
*/
length = MODESENSE_PAGE_LEN(page2);
if (length < MIN_PAGE2_LEN) {
return (0);
}
/*
* Ask for changeable parameters.
*/
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);
}
/*
* We need to issue a mode select only if one or more
* parameters need to be changed, and those parameters
* are flagged by the drive as changeable.
*/
flag = 0;
/*
* Apply any changes requested via the change list method
*/
flag |= apply_chg_list(MODEPAGE_DISCO_RECO, length,
(u_char *) page2, (u_char *) fixed,
cur_dtype->dtype_chglist);
/*
* If no changes required, do not issue a mode select
*/
if (flag == 0) {
return (0);
}
/*
* Issue a mode select
*/
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,
sp_flags, (caddr_t) page2, length, &header);
if (status && (sp_flags & MODE_SELECT_SP)) {
/* If failed, try not saving mode select params. */
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) {
eprint("Warning: Using default .\n\n");
}
/*
* If debugging, issue mode senses on the current and
* saved values, so we can see the result of the mode
* selects.
*/
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);
}
/*
* Check disk format parameters via mode sense.
* Issue a mode select if we need to change something.
*/
/*ARGSUSED*/
int
scsi_ms_page3(scsi2_flag)
int scsi2_flag;
{
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 debugging, issue mode senses on the default and
* current values.
*/
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);
}
/*
* Issue a mode sense to determine the saved parameters
* If the device cannot return the saved parameters,
* use the current, instead.
*/
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);
}
}
/*
* We only need the common subset between the CCS
* and SCSI-2 structures, so we can treat both
* cases identically. Whatever the drive gives
* us, we return to the drive in the mode select,
* delta'ed by whatever we want to change.
*/
length = MODESENSE_PAGE_LEN(page3);
if (length < MIN_PAGE3_LEN) {
return (0);
}
/*
* Ask for changeable parameters.
*/
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);
}
/*
* We need to issue a mode select only if one or more
* parameters need to be changed, and those parameters
* are flagged by the drive as changeable.
*/
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 != SECSIZE);
page3->data_bytes_sect = SECSIZE;
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 = 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;
}
/*
* Notify user of any changes so far
*/
if (flag && option_msg) {
eprint("PAGE 3: trk skew= %d (%d) cyl skew= %d (%d) ",
page3->track_skew, tmp1, page3->cylinder_skew, tmp2);
eprint("sects/trk= %d (%d)\n", page3->sect_track, tmp3);
eprint(" trks/zone= %d (%d) alt trks= %d (%d) ",
page3->tracks_per_zone, tmp4,
page3->alt_tracks_vol, tmp5);
eprint("alt sects/zone= %d (%d)\n",
page3->alt_sect_zone, tmp6);
}
/*
* Apply any changes requested via the change list method
*/
flag |= apply_chg_list(DAD_MODE_FORMAT, length,
(u_char *) page3, (u_char *) fixed,
cur_dtype->dtype_chglist);
/*
* If no changes required, do not issue a mode select
*/
if (flag == 0) {
return (0);
}
/*
* Issue a mode select
*/
/*
* We always want to set the Page Format bit for mode
* selects. Set the Save Page bit if the drive indicates
* that it can save this page via the mode sense.
*/
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,
sp_flags, (caddr_t) page3, length, &header);
if (status && (sp_flags & MODE_SELECT_SP)) {
/* If failed, try not saving mode select params. */
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) {
eprint("Warning: Using default drive format parameters.\n");
eprint("Warning: Drive format may not be correct.\n\n");
}
/*
* If debugging, issue mode senses on the current and
* saved values, so we can see the result of the mode
* selects.
*/
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);
}
/*
* Check disk geometry parameters via mode sense.
* Issue a mode select if we need to change something.
*/
/*ARGSUSED*/
int
scsi_ms_page4(scsi2_flag)
int scsi2_flag;
{
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 debugging, issue mode senses on the default and
* current values.
*/
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);
}
/*
* Issue a mode sense to determine the saved parameters
* If the device cannot return the saved parameters,
* use the current, instead.
*/
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);
}
}
/*
* We only need the common subset between the CCS
* and SCSI-2 structures, so we can treat both
* cases identically. Whatever the drive gives
* us, we return to the drive in the mode select,
* delta'ed by whatever we want to change.
*/
length = MODESENSE_PAGE_LEN(page4);
if (length < MIN_PAGE4_LEN) {
return (0);
}
/*
* Ask for changeable parameters.
*/
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);
}
/*
* We need to issue a mode select only if one or more
* parameters need to be changed, and those parameters
* are flagged by the drive as changeable.
*/
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 = (u_char)phead;
}
/*
* Notify user of changes so far
*/
if (flag && option_msg) {
eprint("PAGE 4: cylinders= %d heads= %d (%d)\n",
tmp1, page4->heads, tmp2);
}
/*
* Apply any changes requested via the change list method
*/
flag |= apply_chg_list(DAD_MODE_GEOMETRY, length,
(u_char *) page4, (u_char *) fixed,
cur_dtype->dtype_chglist);
/*
/*
* If no changes required, do not issue a mode select
*/
if (flag == 0) {
return (0);
}
/*
* Issue a mode select
*/
/*
* We always want to set the Page Format bit for mode
* selects. Set the Save Page bit if the drive indicates
* that it can save this page via the mode sense.
*/
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,
sp_flags, (caddr_t) page4, length, &header);
if (status && (sp_flags & MODE_SELECT_SP)) {
/* If failed, try not saving mode select params. */
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) {
eprint("Warning: Using default drive geometry.\n\n");
}
/*
* If debugging, issue mode senses on the current and
* saved values, so we can see the result of the mode
* selects.
*/
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);
}
/*
* Check SCSI-2 disk cache parameters via mode sense.
* Issue a mode select if we need to change something.
*/
/*ARGSUSED*/
int
scsi_ms_page8(scsi2_flag)
int scsi2_flag;
{
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;
/*
* Only SCSI-2 devices support this page
*/
if (!scsi2_flag) {
return (0);
}
/*
* If debugging, issue mode senses on the default and
* current values.
*/
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);
}
/*
* Issue a mode sense to determine the saved parameters
* If the device cannot return the saved parameters,
* use the current, instead.
*/
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);
}
}
/*
* We only need the common subset between the CCS
* and SCSI-2 structures, so we can treat both
* cases identically. Whatever the drive gives
* us, we return to the drive in the mode select,
* delta'ed by whatever we want to change.
*/
length = MODESENSE_PAGE_LEN(page8);
if (length < MIN_PAGE8_LEN) {
return (0);
}
/*
* Ask for changeable parameters.
*/
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);
}
/*
* We need to issue a mode select only if one or more
* parameters need to be changed, and those parameters
* are flagged by the drive as changeable.
*/
flag = 0;
/*
* Apply any changes requested via the change list method
*/
flag |= apply_chg_list(DAD_MODE_CACHE, length,
(u_char *) page8, (u_char *) fixed,
cur_dtype->dtype_chglist);
/*
* If no changes required, do not issue a mode select
*/
if (flag == 0) {
return (0);
}
/*
* Issue a mode select
*/
/*
* We always want to set the Page Format bit for mode
* selects. Set the Save Page bit if the drive indicates
* that it can save this page via the mode sense.
*/
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)) {
/* If failed, try not saving mode select params. */
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) {
eprint("\
Warning: Using default SCSI-2 cache parameters.\n\n");
}
/*
* If debugging, issue mode senses on the current and
* saved values, so we can see the result of the mode
* selects.
*/
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);
}
/*
* Check CCS disk cache parameters via mode sense.
* Issue a mode select if we need to change something.
*/
/*ARGSUSED*/
int
scsi_ms_page38(scsi2_flag)
int scsi2_flag;
{
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;
page38 = &u_page38.page38;
fixed = &u_fixed.page38;
/*
* If debugging, issue mode senses on the default and
* current values.
*/
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);
}
/*
* Issue a mode sense to determine the saved parameters
* If the device cannot return the saved parameters,
* use the current, instead.
*/
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);
}
}
/*
* We only need the common subset between the CCS
* and SCSI-2 structures, so we can treat both
* cases identically. Whatever the drive gives
* us, we return to the drive in the mode select,
* delta'ed by whatever we want to change.
*/
length = MODESENSE_PAGE_LEN(page38);
if (length < MIN_PAGE38_LEN) {
return (0);
}
/*
* Ask for changeable parameters.
*/
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);
}
/*
* We need to issue a mode select only if one or more
* parameters need to be changed, and those parameters
* are flagged by the drive as changeable.
*/
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;
}
/*
* Notify the user of changes up to this point
*/
if (flag && option_msg) {
eprint("PAGE 38: cache mode= 0x%x (0x%x)\n",
page38->mode, tmp1);
eprint(" min. prefetch multiplier= %d ",
page38->min_multiplier);
eprint("max. prefetch multiplier= %d\n",
page38->max_multiplier);
eprint(" threshold= %d (%d) ",
page38->threshold, tmp2);
eprint("min. prefetch= %d (%d) ",
page38->min_prefetch, tmp3);
eprint("max. prefetch= %d (%d)\n",
page38->max_prefetch, tmp4);
}
/*
* Apply any changes requested via the change list method
*/
flag |= apply_chg_list(DAD_MODE_CACHE_CCS, length,
(u_char *) page38, (u_char *) fixed,
cur_dtype->dtype_chglist);
/*
* If no changes required, do not issue a mode select
*/
if (flag == 0) {
return (0);
}
/*
* Issue a mode select
*
* We always want to set the Page Format bit for mode
* selects. Set the Save Page bit if the drive indicates
* that it can save this page via the mode sense.
*/
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)) {
/* If failed, try not saving mode select params. */
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) {
eprint("Warning: Using default CCS cache parameters.\n\n");
}
/*
* If debugging, issue mode senses on the current and
* saved values, so we can see the result of the mode
* selects.
*/
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);
}
/*
* Extract the manufacturer's defect list.
*/
int
scsi_ex_man(list)
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);
}
/*
* Extract the current defect list.
* For embedded scsi drives, this means both the P & G lists.
*/
int
scsi_ex_cur(list)
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);
}
/*
* Extract just the G list.
*/
int
scsi_ex_grown(list)
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);
}
int
scsi_read_defect_data(list, pglist_flags)
struct defect_list *list;
int pglist_flags;
{
struct uscsi_cmd ucmd;
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; /* returned defect list length */
hdr = (struct scsi_defect_hdr *)&def_list;
/*
* First get length of list by asking for the header only.
*/
(void) memset((char *)&def_list, 0, sizeof (def_list));
/*
* Build and execute the uscsi ioctl
*/
(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, 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);
status = uscsi_cmd(cur_file, &ucmd, F_SILENT);
if (status != 0) {
if (option_msg) {
eprint("No %s defect list.\n",
pglist_flags & DLD_GROWN_DEF_LIST ?
"grown" : "manufacturer's");
}
return (-1);
}
/*
* Read the full list the second time
*/
len = hdr->length;
nbytes = len + sizeof (struct scsi_defect_hdr);
defects = (struct scsi_defect_list *) 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, F_SILENT);
if (status) {
eprint("can't read defect list 2nd time");
destroy_data((char *) defects);
return (-1);
}
if (len != hdr->length) {
eprint("not enough defects");
destroy_data((char *) defects);
return (-1);
}
scsi_convert_list_to_new(list, defects, DLD_BFI_FORMAT);
destroy_data((char *) defects);
return (0);
}
/*
* Map a block.
*/
/*ARGSUSED*/
int
scsi_repair(bn, flag)
int bn;
int flag;
{
struct uscsi_cmd ucmd;
union scsi_cdb cdb;
struct scsi_reassign_blk defect_list;
/*
* Build and execute the uscsi ioctl
*/
(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.defect = bn;
return (uscsi_cmd(cur_file, &ucmd, F_NORMAL));
}
/*
* Convert a SCSI-style defect list to our generic format.
* We can handle different format lists.
*/
void
scsi_convert_list_to_new(list, def_list, list_format)
struct defect_list *list;
struct scsi_defect_list *def_list;
int list_format;
{
register struct scsi_bfi_defect *old_defect;
register struct defect_entry *new_defect;
register int len;
register int i;
switch (list_format) {
case DLD_BFI_FORMAT:
/*
* Allocate space for the rest of the list.
*/
len = def_list->length / sizeof (struct scsi_bfi_defect);
old_defect = def_list->list;
new_defect = (len == 0) ? NULL :
(struct defect_entry *)
zalloc(LISTSIZE(len) * SECSIZE);
list->header.count = len;
list->header.magicno = (u_int) DEFECT_MAGIC;
list->list = new_defect;
for (i = 0; i < len; i++, new_defect++, old_defect++) {
new_defect->cyl = (short) old_defect->cyl;
new_defect->head = (short) old_defect->head;
new_defect->bfi = (int) old_defect->bytes_from_index;
new_defect->nbits = UNKNOWN; /* size of defect */
}
break;
default:
eprint("scsi_convert_list_to_new: can't deal with it\n");
exit (0);
/*NOTREACHED*/
}
(void) checkdefsum(list, CK_MAKESUM);
}
/*
* Execute a command and determine the result.
* Uses the "uscsi" ioctl interface, which is
* fully supported.
*/
int
uscsi_cmd(fd, ucmd, flags)
int fd;
struct uscsi_cmd *ucmd;
int flags;
{
struct uscsi_cmd rqcmd;
union scsi_cdb rqcdb;
struct scsi_extended_sense *rq;
char rqbuf[255];
int status;
int rqlen;
/*
* If sun4, convert this uscsi command to
* the equivalent dkcmd for the old driver.
*/
if (sun4_flag) {
return (scsi_maptodkcmd(fd, ucmd, flags));
}
/*
* Set function flags for driver.
*/
ucmd->uscsi_flags = USCSI_ISOLATE | USCSI_DIAGNOSE;
if (flags & F_SILENT) {
ucmd->uscsi_flags |= USCSI_SILENT;
}
/*
* If this command will perform a read, set the USCSI_READ flag
*/
if (ucmd->uscsi_buflen > 0) {
switch (ucmd->uscsi_cdb[0]) {
case SCMD_READ:
case SCMD_READ|SCMD_GROUP1:
case SCMD_MODE_SENSE:
case SCMD_REQUEST_SENSE:
case SCMD_INQUIRY:
case SCMD_READ_DEFECT_LIST:
ucmd->uscsi_flags |= USCSI_READ;
break;
}
}
/*
* Execute the ioctl
*/
status = ioctl(fd, USCSICMD, ucmd);
if (status == 0 && ucmd->uscsi_status == 0)
return (status);
/*
* Check to see if this is the old sun4 driver which
* doesn't support the uscsi interface.
*/
if (status == -1 && (errno == EINVAL || errno == ENOTTY)) {
sun4_flag = 1;
return (scsi_maptodkcmd(fd, ucmd, flags));
}
/*
* Get the request sense info, if possible, to find out
* more about the error
*/
(void) memset((char *)rqbuf, 0, sizeof (rqbuf));
(void) memset((char *)&rqcmd, 0, sizeof (rqcmd));
(void) memset((char *)&rqcdb, 0, sizeof (union scsi_cdb));
rqcdb.scc_cmd = SCMD_REQUEST_SENSE;
FORMG0COUNT(&rqcdb, sizeof (rqbuf));
rqcmd.uscsi_cdb = (caddr_t) &rqcdb;
rqcmd.uscsi_cdblen = CDB_GROUP0;
rqcmd.uscsi_bufaddr = (caddr_t) rqbuf;
rqcmd.uscsi_buflen = sizeof (rqbuf);
rqcmd.uscsi_flags = USCSI_ISOLATE | USCSI_DIAGNOSE | USCSI_READ;
status = ioctl(fd, USCSICMD, &rqcmd);
rq = (struct scsi_extended_sense *) rqbuf;
rqlen = (int) rq->es_add_len + 8;
if (status || rq->es_class != CLASS_EXTENDED_SENSE ||
rqlen < MIN_REQUEST_SENSE_LEN) {
if (option_msg) {
eprint("Request sense for command %s failed\n",
scsi_find_command_name(ucmd->uscsi_cdb[0]));
}
if (status == 0) {
if (option_msg && diag_msg) {
dump("sense: ", (caddr_t) rqbuf,
sizeof (rqbuf), HEX_ONLY);
}
}
return (-1);
}
/*
* If the failed command is a Mode Select, and the
* target is indicating that it has rounded one of
* the mode select parameters, as defined in the SCSI-2
* specification, then we should accept the command
* as successful.
*/
if (ucmd->uscsi_cdb[0] == SCMD_MODE_SELECT) {
if (rq->es_key == KEY_RECOVERABLE_ERROR &&
rq->es_add_info[ADD_SENSE_CODE] ==
ROUNDED_PARAMETER &&
rq->es_add_info[ADD_SENSE_QUAL_CODE] == 0) {
return (0);
}
}
if (flags & F_ALLERRS) {
media_error = (rq->es_key == KEY_MEDIUM_ERROR);
}
if (!(flags & F_SILENT)) {
scsi_printerr(ucmd, rq, rqlen);
}
if ((rq->es_key != KEY_RECOVERABLE_ERROR) || (flags & F_ALLERRS)) {
return (-1);
}
return (0);
}
/*
* Execute a uscsi mode sense command.
* This can only be used to return one page at a time.
* Return the mode header/block descriptor and the actual
* page data separately - this allows us to support
* devices which return either 0 or 1 block descriptors.
* Whatever a device gives us in the mode header/block descriptor
* will be returned to it upon subsequent mode selects.
*/
int
uscsi_mode_sense(fd, page_code, page_control, page_data, page_size, header)
int fd; /* file descriptor */
int page_code; /* requested page number */
int page_control; /* current, changeable, etc. */
caddr_t page_data; /* place received data here */
int page_size; /* size of page_data */
struct scsi_ms_header *header; /* mode header/block descriptor */
{
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;
int flags;
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);
/*
* Allocate a buffer for the mode sense headers
* and mode sense data itself.
*/
nbytes = sizeof (struct block_descriptor) +
sizeof (struct mode_header) + page_size;
nbytes = page_size;
if ((mode_sense_buf = malloc((u_int) nbytes)) == NULL) {
eprint("cannot malloc %ld bytes\n", nbytes);
return (-1);
}
/*
* Use F_SILENT normally, but if we're in expert
* mode, allow all driver messages.
*/
flags = (option_msg && diag_msg) ? F_NORMAL : F_SILENT;
/*
* Build and execute the uscsi ioctl
*/
(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, (u_char)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, flags);
if (status) {
if (option_msg) {
eprint("Mode sense page 0x%x failed\n",
page_code);
}
free(mode_sense_buf);
return (-1);
}
/*
* Verify that the returned data looks reasonabled,
* find the actual page data, and copy it into the
* user's buffer. Copy the mode_header and block_descriptor
* into the header structure, which can then be used to
* return the same data to the drive when issuing a mode select.
*/
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) {
eprint("\
\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,
(int) (sizeof (struct mode_header) + hdr->bdesc_length));
pg = (struct mode_page *) ((u_long) mode_sense_buf +
sizeof (struct mode_header) + hdr->bdesc_length);
if (pg->code != page_code) {
if (option_msg) {
eprint("\
\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);
}
/*
* Accept up to "page_size" bytes of mode sense data.
* This allows us to accept both CCS and SCSI-2
* structures, as long as we request the greater
* of the two.
*/
maximum = page_size - sizeof (struct mode_page) - hdr->bdesc_length;
if (((int) pg->length) > maximum) {
if (option_msg) {
eprint("\
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);
eprint("\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);
}
/*
* Execute a uscsi mode select command.
*/
int
uscsi_mode_select(fd, page_code, options, page_data, page_size, header)
int fd; /* file descriptor */
int page_code; /* mode select page */
int options; /* save page/page format */
caddr_t page_data; /* place received data here */
int page_size; /* size of page_data */
struct scsi_ms_header *header; /* mode header/block descriptor */
{
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);
/*
* Allocate a buffer for the mode select header and data
*/
nbytes = sizeof (struct block_descriptor) +
sizeof (struct mode_header) + page_size;
if ((mode_select_buf = malloc((u_int) nbytes)) == NULL) {
eprint("cannot malloc %ld bytes\n", nbytes);
return (-1);
}
/*
* Build the mode select data out of the header and page data
* This allows us to support devices which return either
* 0 or 1 block descriptors.
*/
(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);
}
/*
* Dump the structures if anyone's interested
*/
if (option_msg && diag_msg) {
char *s;
s = find_string(mode_select_strings,
options & (MODE_SELECT_SP|MODE_SELECT_PF));
eprint("\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);
}
/*
* Put the header and data together
*/
(void) memcpy(mode_select_buf, (caddr_t) header, nbytes);
(void) memcpy(mode_select_buf + nbytes, page_data, page_size);
nbytes += page_size;
/*
* Build and execute the uscsi ioctl
*/
(void) memset((char *)&ucmd, 0, sizeof (ucmd));
(void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
cdb.scc_cmd = SCMD_MODE_SELECT;
FORMG0COUNT(&cdb, (u_char)nbytes);
cdb.cdb_opaque[1] = (u_char)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, F_NORMAL);
if (status && option_msg) {
eprint("Mode select page 0x%x failed\n", page_code);
}
free(mode_select_buf);
return (status);
}
/*
* Execute a uscsi inquiry command and return the
* resulting data.
*/
int
uscsi_inquiry(fd, inqbuf, inqbufsiz)
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);
/*
* Build and execute the uscsi ioctl
*/
(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, (u_char)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, F_SILENT);
if (status) {
if (option_msg) {
eprint("Inquiry failed\n");
}
} else if (option_msg && diag_msg) {
/*
* Dump the inquiry data if anyone's interested
*/
inq = (struct scsi_inquiry *) inqbuf;
n = (int) inq->inq_len + 4;
n = min(n, inqbufsiz);
eprint("Inquiry:\n");
dump("", (caddr_t) inqbuf, n, HEX_ASCII);
}
return (status);
}
void
scsi_dump_mode_sense_pages(page_control)
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];
pc_str = find_string(page_control_strings, page_control);
/*
* Allocate memory for the mode sense buffer.
*/
nbytes = 255;
msbuf = (char *) zalloc(nbytes);
/*
* Build and execute the uscsi ioctl
*/
(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, (u_char)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, F_SILENT);
if (status) {
eprint("\nMode sense page 0x3f (%s) failed\n",
pc_str);
} else {
eprint("\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;
sprintf(s, " %3x: ", mp->code);
dump(s, p, n, HEX_ONLY);
p += n;
}
if (nbytes < 0) {
eprint(" Sense data formatted incorrectly:\n");
dump(" ", msbuf, (int)mh->length+1, HEX_ONLY);
}
eprint("\n");
}
free(msbuf);
}
void
scsi_printerr(ucmd, rq, rqlen)
struct uscsi_cmd *ucmd;
struct scsi_extended_sense *rq;
int rqlen;
{
int blkno;
switch (rq->es_key) {
case KEY_NO_SENSE:
eprint("No sense error");
break;
case KEY_RECOVERABLE_ERROR:
eprint("Recoverable error");
break;
case KEY_NOT_READY:
eprint("Not ready error");
break;
case KEY_MEDIUM_ERROR:
eprint("Medium error");
break;
case KEY_HARDWARE_ERROR:
eprint("Hardware error");
break;
case KEY_ILLEGAL_REQUEST:
eprint("Illegal request");
break;
case KEY_UNIT_ATTENTION:
eprint("Unit attention error");
break;
case KEY_WRITE_PROTECT:
eprint("Write protect error");
break;
case KEY_BLANK_CHECK:
eprint("Blank check error");
break;
case KEY_VENDOR_UNIQUE:
eprint("Vendor unique error");
break;
case KEY_COPY_ABORTED:
eprint("Copy aborted error");
break;
case KEY_ABORTED_COMMAND:
eprint("Aborted command");
break;
case KEY_EQUAL:
eprint("Equal error");
break;
case KEY_VOLUME_OVERFLOW:
eprint("Volume overflow");
break;
case KEY_MISCOMPARE:
eprint("Miscompare error");
break;
case KEY_RESERVED:
eprint("Reserved error");
break;
default:
eprint("Unknown error");
break;
}
eprint(" during %s", scsi_find_command_name(ucmd->uscsi_cdb[0]));
if (rq->es_valid) {
blkno = (rq->es_info_1 << 24) | (rq->es_info_2 << 16) |
(rq->es_info_3 << 8) | rq->es_info_4;
eprint(": block %d (0x%x) (", blkno, blkno);
pr_dblock(eprint, (daddr_t)blkno);
eprint(")");
}
eprint("\n");
if (rq->es_add_len >= 6) {
eprint("ASC: 0x%x ASCQ: 0x%x\n",
rq->es_add_info[ADD_SENSE_CODE],
rq->es_add_info[ADD_SENSE_QUAL_CODE]);
}
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);
}
}
/*
* Return a pointer to a string telling us the name of the command.
*/
char *
scsi_find_command_name(cmd)
int cmd;
{
struct scsi_command_name *c;
for (c = scsi_command_names; c->command != SCMD_UNKNOWN; c++)
if (c->command == cmd)
break;
return (c->name);
}
/*
* Return true if we support a particular mode page
*/
int
scsi_supported_page(page)
int page;
{
return (page == 1 || page == 2 || page == 3 || page == 4 ||
page == 8 || page == 0x38);
}
int
apply_chg_list(pageno, pagsiz, curbits, chgbits, chglist)
int pageno;
int pagsiz;
u_char *curbits;
u_char *chgbits;
struct chg_list *chglist;
{
u_char c;
int i;
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 |= (u_char) chglist->value;
break;
case CHG_MODE_CLR:
c &= (u_char) chglist->value;
break;
case CHG_MODE_ABS:
c = (u_char) chglist->value;
break;
}
/*
* Figure out which bits changed, and
* are marked as changeable. If this
* result actually differs from the
* current value, update the current
* value, and note that a mode select
* should be done.
*/
if (((c ^ curbits[i]) & chgbits[i]) != 0) {
c &= chgbits[i];
if (c != curbits[i]) {
curbits[i] = c;
changed = 1;
}
}
}
chglist = chglist->next;
}
return (changed);
}
/*
* Search a string list for a particular value.
* Return the string associated with that value.
*/
char *
find_string(slist, match_value)
slist_t *slist;
int match_value;
{
for (; slist->str != NULL; slist++) {
if (slist->value == match_value) {
return (slist->str);
}
}
return ((char *) NULL);
}
/*
* Dump a structure in hexadecimal and optionally, ascii,
* for diagnostic purposes
*/
#define BYTES_PER_LINE 16
void
dump(hdr, src, nbytes, format)
char *hdr;
caddr_t src;
int nbytes;
int format;
{
int i;
int n;
char *p;
char s[256];
assert(format == HEX_ONLY || format == HEX_ASCII);
strcpy(s, hdr);
for (p = s; *p; p++) {
*p = ' ';
}
p = hdr;
while (nbytes > 0) {
eprint("%s", p);
p = s;
n = min(nbytes, BYTES_PER_LINE);
for (i = 0; i < n; i++) {
eprint("%02x ", src[i] & 0xff);
}
if (format == HEX_ASCII) {
for (i = BYTES_PER_LINE-n; i > 0; i--) {
eprint(" ");
}
eprint(" ");
for (i = 0; i < n; i++) {
eprint("%c",
isprint(src[i]) ? src[i] : '.');
}
}
eprint("\n");
nbytes -= n;
src += n;
}
}
#define GETG0COUNT(cdb) ((cdb)->g0_count0 & 0xff)
#define GETG1COUNT(cdb) ((((cdb)->g1_count1 << 8) & 0xff) + \
((cdb)->g1_count0 & 0xff))
int
scsi_maptodkcmd(fd, ucmd, flags)
int fd;
struct uscsi_cmd *ucmd;
int flags;
{
union scsi_cdb *cdb;
struct dk_cmd dkcmd;
struct scsi_defect_hdr *hdr;
short g1;
cdb = (union scsi_cdb *) ucmd->uscsi_cdb;
(void) memset((char *)&dkcmd, 0, sizeof (struct dk_cmd));
dkcmd.dkc_cmd = cdb->scc_cmd;
dkcmd.dkc_bufaddr = ucmd->uscsi_bufaddr;
dkcmd.dkc_buflen = ucmd->uscsi_buflen;
switch (dkcmd.dkc_cmd) {
case SCMD_INQUIRY:
assert(ucmd->uscsi_cdblen == CDB_GROUP0);
break;
case SCMD_MODE_SELECT:
/*
* Sun4 driver interface does not allow us
* to control the MODE_SELECT_PF bit,
* unfortunately.
*/
assert(ucmd->uscsi_cdblen == CDB_GROUP0);
dkcmd.dkc_blkno = 0;
if (cdb->cdb_opaque[1] & MODE_SELECT_SP) {
dkcmd.dkc_blkno |= 0x80;
}
break;
case SCMD_MODE_SENSE:
assert(ucmd->uscsi_cdblen == CDB_GROUP0);
dkcmd.dkc_blkno = cdb->cdb_opaque[2] & 0xff;
break;
case SCMD_READ_DEFECT_LIST:
assert(ucmd->uscsi_cdblen == CDB_GROUP1);
hdr = (struct scsi_defect_hdr *) dkcmd.dkc_bufaddr;
hdr->descriptor = cdb->cdb_opaque[2] & 0x1f;
break;
case SCMD_REASSIGN_BLOCK:
assert(ucmd->uscsi_cdblen == CDB_GROUP0);
break;
case SCMD_READ:
case SCMD_WRITE:
case SCMD_READ|SCMD_GROUP1:
case SCMD_WRITE|SCMD_GROUP1:
dkcmd.dkc_cmd &= ~SCMD_GROUP1;
g1 = (ucmd->uscsi_cdblen == CDB_GROUP1);
dkcmd.dkc_secnt = (g1 ? GETG1COUNT(cdb) : GETG0COUNT(cdb));
dkcmd.dkc_blkno = (g1 ? GETG1ADDR(cdb) : GETG0ADDR(cdb));
assert (dkcmd.dkc_buflen == (SECSIZE * dkcmd.dkc_secnt));
break;
default:
eprint("Unsupported SCSI command (dkcmd): 0x%x\n",
dkcmd.dkc_cmd);
fullabort();
}
return (scsi_dkcmd(fd, &dkcmd, flags));
}
int
scsi_dkcmd(fd, dkcmd, flags)
int fd;
struct dk_cmd *dkcmd;
int flags;
{
struct dk_diag diag;
struct dk_err *errptr;
int status;
/*
* Set function flags for driver.
*/
dkcmd->dkc_flags = DK_ISOLATE | DK_DIAGNOSE;
if (flags & F_SILENT) {
dkcmd->dkc_flags |= DK_SILENT;
}
status = ioctl(fd, DKIOCSCMD, dkcmd);
if (status == 0) {
return (status);
}
status = ioctl(fd, DKIOCGDIAG, &diag);
if (status || (diag.dkd_severe == DK_NOERROR)) {
eprint("unexpected ioctl error from sd");
return (-1);
}
errptr = scsi_dk_finderr(diag.dkd_errno);
if (flags & F_ALLERRS) {
if (errptr->errtype == DK_ISMEDIA) {
media_error = 1;
} else {
media_error = 0;
}
}
if (!(flags & F_SILENT)) {
scsi_dk_printerr(diag.dkd_severe, errptr->errtype, errptr->errmsg,
dkcmd->dkc_cmd, (u_long) diag.dkd_errsect);
}
if ((diag.dkd_severe == DK_FATAL) || (flags & F_ALLERRS)) {
return (-1);
}
return (0);
}
void
scsi_dk_printerr(severity, type, msg, cmd, blkno)
u_char severity, type;
char *msg;
u_short cmd;
u_long blkno;
{
eprint("\015Block %d (",blkno);
pr_dblock(eprint, (daddr_t)blkno);
switch(severity) {
case DK_FATAL:
eprint("), Fatal ");
break;
case DK_RECOVERED:
eprint("), Recovered ");
break;
case DK_CORRECTED:
eprint("), Corrected ");
break;
default:
eprint("), Unknown severity value.\n");
fullabort();
break;
}
if (type == DK_ISMEDIA) {
eprint("media error");
} else {
eprint("non-media error");
}
eprint(" (%s) during %s\n", msg, scsi_find_command_name(cmd));
}
/*
* List of possible errors.
*/
struct dk_err dk_errors[] = {
SC_NO_SENSE, ERRLVL_FAULT, DK_NONMEDIA,
"no sense info",
SC_RECOVERABLE_ERROR, ERRLVL_RETRY, DK_ISMEDIA,
"soft error",
SC_NOT_READY, ERRLVL_RETRY, DK_NONMEDIA,
"not ready",
SC_MEDIUM_ERROR, ERRLVL_RETRY, DK_ISMEDIA,
"media error",
SC_HARDWARE_ERROR, ERRLVL_RETRY, DK_NONMEDIA,
"hardware error",
SC_ILLEGAL_REQUEST, ERRLVL_FATAL, DK_NONMEDIA,
"illegal request",
SC_UNIT_ATTENTION, ERRLVL_RETRY, DK_NONMEDIA,
"unit attention",
SC_WRITE_PROTECT, ERRLVL_FATAL, DK_NONMEDIA,
"write protected",
SC_BLANK_CHECK, ERRLVL_FATAL, DK_NONMEDIA,
"blank check",
SC_VENDOR_UNIQUE, ERRLVL_FATAL, DK_NONMEDIA,
"vendor unique",
SC_COPY_ABORTED, ERRLVL_FATAL, DK_NONMEDIA,
"copy aborted",
SC_ABORTED_COMMAND, ERRLVL_FATAL, DK_NONMEDIA,
"aborted command",
SC_EQUAL, ERRLVL_FATAL, DK_NONMEDIA,
"equal error",
SC_VOLUME_OVERFLOW, ERRLVL_FATAL, DK_NONMEDIA,
"volume overflow",
SC_MISCOMPARE, ERRLVL_FATAL, DK_NONMEDIA,
"miscompare error",
SC_RESERVED, ERRLVL_FATAL, DK_NONMEDIA,
"reserved",
SC_ERR_UNKNOWN, ERRLVL_FATAL, DK_NONMEDIA,
"unknown error",
};
/*
* Returns a pointer to the error struct for the error.
*/
struct dk_err *
scsi_dk_finderr(errno)
register u_char errno;
{
struct dk_err *e;
for (e= dk_errors; e->errno != SCMD_UNKNOWN; e++)
if (e->errno == errno)
break;
return (e);
}