ctlr_scsi.c revision cf6efa001eeb91c509543073a0e1e32ecc5d2c62
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
/*
* This file contains the routines for embedded scsi disks
*/
#include "global.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 /* DAD_MODE_CACHE_CCS */
/* format defect header bits */
#define FDH_FOV 0x80
#define FDH_IMMED 0x02
#define SENSE_LEN 20
#define RETRY_DELAY 5
#define PROGRESS_INDICATION_BASE 65536
#ifdef __STDC__
/*
* Local prototypes for ANSI C compilers
*/
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
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);
#else /* __STDC__ */
static int scsi_format();
static int scsi_raw_format();
static int scsi_ms_page8();
static int scsi_ms_page38();
static void scsi_convert_list_to_new();
static char *scsi_find_command_name();
static int chg_list_affects_page();
static void scsi_printerr();
static diskaddr_t scsi_extract_sense_info_descr();
static void scsi_print_extended_sense();
static void scsi_print_descr_sense();
static int test_until_ready();
static int uscsi_reserve_release();
static int check_support_for_defects();
static int scsi_format_without_defects();
static int scsi_ms_page1();
static int scsi_ms_page2();
static int scsi_ms_page3();
static int scsi_ms_page4();
static int scsi_repair();
static int scsi_read_defect_data();
static int scsi_ck_format();
#endif /* __STDC__ */
0,
};
#define SCMD_UNKNOWN 0xff
/*
* Names of commands. Must have SCMD_UNKNOWN at end of list.
*/
static struct scsi_command_name {
char *name;
} scsi_command_names[] = {
SCMD_FORMAT, "format",
SCMD_READ, "read",
SCMD_WRITE, "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"
};
/*
* Strings for printing mode sense page control values
*/
static slist_t page_control_strings[] = {
};
/*
* Strings for printing the mode select options
*/
static slist_t mode_select_strings[] = {
{ "", "", 0 },
};
static int scsi_format_revolutions = 5;
/*
* READ DEFECT DATA commands is optional as per SCSI-2 spec.
* Hence check if the read_defect_data command fails with
* Invalid Opcode so that we can give a more meaningful message
* to the user.
*/
#define INVALID_OPCODE 0x20
/*
* Read or write the disk.
*/
int
int dir;
int fd;
int secnt;
int flags;
int *xfercntp;
{
int max_sectors;
int rc = 0;
/*
* If the max xfercnt hasn't been determined start with BUF_SECTS
* (currently 126 == 63K), otherwise use the xfercnt value
* my caller saved from the previous invocation.
*/
} else if (*xfercntp == 0) {
*xfercntp = max_sectors;
} else {
max_sectors = *xfercntp;
}
/*
* Build and execute the uscsi ioctl. We build a group0
* or group1 command as necessary, since some targets
* do not support group1 commands.
*/
while (secnt) {
int nsectors;
} else {
if (blkno > 0xffffffff) {
} else {
}
}
if (rc != 0)
break;
/*
* check if partial DMA breakup required
* if so, reduce the request size by half and retry
* the last request
*/
max_sectors >>= 1;
if (max_sectors <= 0) {
rc = -1;
break;
}
continue;
}
if (ucmd.uscsi_resid != 0) {
rc = -1;
break;
}
}
/*
* If the xfercnt wasn't previously saved or if the
* new value is smaller than the old value, save the
* current value in my caller's save area.
*/
if (diag_msg)
err_print("reducing xfercnt %d %d\n",
*xfercntp, max_sectors);
*xfercntp = max_sectors;
}
return (rc);
}
/*
* 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.
*/
#ifdef i386
static int
#else /* i386 */
static int
#endif /* i386 */
scsi_ck_format(void)
{
int status;
/*
* Try to read the first four blocks.
*/
return (!status);
}
/*
* Format the disk, the whole disk, and nothing but the disk.
*/
/*ARGSUSED*/
static int
struct defect_list *list;
{
int status;
int flag;
char rawbuf[MAX_MODE_SENSE_SIZE];
struct scsi_inquiry *inq;
/*
* Determine if the target appears to be SCSI-2
*/
err_print("Inquiry failed\n");
return (-1);
}
/*
* Reserve the scsi disk before performing mode select and
* format operations. This will keep other hosts, if any, from
* touching the disk while we are here.
*/
err_print("Reserve failed\n");
return (-1);
}
/*
* Set up the various SCSI parameters specified before
* formatting the disk. Each routine handles the
* parameters relevant 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.
*/
return (-1);
}
/*
* If we're debugging the drive, dump every page
* the device supports, for thorough analysis.
*/
if (option_msg && diag_msg) {
err_print("\n");
}
/*
* Determine the FMTPINFO field in format cdb, and the
* PROTECTION FIELD USAGE in the long parameter list, via
* the protection type input by users.
*/
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);
}
/*
* Construct the uscsi format ioctl. The form depends
* 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..
*/
/*
* Use the long parameter header in format command,
* and set the FMTPINFO field., when type 1, 2, 3.
*/
(void) memset((char *)fmt_long_param_header, 0,
sizeof (fmt_long_param_header));
/*
* Set the PROTECTION FIELD USAGE field in the long
* parameter list header, which combines with FMTINFO to
* determine the protection type.
* The PROTECTION INTERVAL EXPONET field is set default 0.
* So only one protection information interval is used
* in type 1, 2, 3.
*/
/*
* No G list. The empty list we send down
* is the complete list.
*/
}
/*
* Issue the format ioctl
*/
fmt_print("Formatting...\n");
/* check if format with immed was successfully accepted */
if (status == 0) {
/* immed accepted poll to completion */
} else {
/* clear FOV and try again */
(void) memset((char *)fmt_long_param_header, 0,
sizeof (fmt_long_param_header));
if (status == 0) {
/* immed accepted, poll for progress */
} else {
/*
* clear defect header and try basecase format
* command will hang until format complete
*/
(void) memset((char *)fmt_long_param_header, 0,
sizeof (fmt_long_param_header));
}
}
/* format failure check */
if (status != 0) {
/*
* formatting failed with fmtdata = 1.
* Check if defects list command is supported, if it
* is not supported then use fmtdata = 0.
* From SCSI Spec
* A FmtData bit of zero indicates, the
* source of defect information is not specified.
* else
* proceed to format using with mode selects.
*/
if (!(check_support_for_defects())) {
}
if (status != 0) {
fmt_print("Format failed\n");
status = scsi_raw_format();
}
}
return (status);
}
/*
* Format without any of the standard mode selects ignoring Grown defects list.
*/
static int
scsi_raw_format(void)
{
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);
}
/*
* Construct the uscsi format ioctl.
* To format with the P and G list, we set the fmtData
* and cmpLst bits to zero. To format with just the
* P list, we set the fmtData bit (meaning that we will
* send down a defect list in the data phase) and the
* cmpLst bit (meaning that the list we send is the
* complete G list), and a defect list header with
* a defect list length of zero.
*/
/* No G list. Send empty defect list to replace it */
/*
* Issue the format ioctl
*/
fmt_print("Formatting...\n");
/* check if format with immed was successfully accepted */
if (status == 0) {
/* immed accepted pool to completion */
} else {
/* clear defect header and try basecase format */
}
/* fmt_print(status ? "Format failed\n\n" : "Format ok\n\n"); */
return (status);
}
/*
* Estimate the time required for format operation (See 1163770).
* format time = (5_revs * p4_heads * p4_cylinders) / p4_rpm
* 5 revolutions (correspond to format_time keyword in format.dat file) are:
* 1 rev. for positioning
* 2 rev. for writing the track
* 1 rev. for positioning
* 1 rev. for cerifying the data integrity of the track
* The return value is a good estimate on the formatting time in minutes.
* Caller should add 50% margin to cover defect management overhead.
*/
int
{
struct mode_geometry *page4;
struct scsi_ms_header header;
int status;
int length;
int format_time;
union {
struct mode_geometry page4;
char rawbuf[MAX_MODE_SENSE_SIZE];
} u_page4;
/*
* Issue a mode sense to determine the default parameters
* If it fail, try to use the saved or current instead.
*/
if (status) {
}
if (status) {
}
if (status) {
return (0);
}
/*
* We only need the common subset between the CCS
* and SCSI-2 structures, so we can treat both
* cases identically.
*/
if (length < MIN_PAGE4_LEN) {
return (0);
}
/*
* Some drives report 0 for page4->rpm, adjust it to AVG_RPM, 3600.
*/
err_print("Mode sense page(4) reports rpm value as %d,"
}
if (p4_cylinders <= 0 || p4_heads <= 0)
return (0);
if (option_msg && diag_msg) {
}
return (format_time);
}
/*
* Check disk error recovery parameters via mode sense.
* Issue a mode select if we need to change something.
*/
/*ARGSUSED*/
static int
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 flag;
int length;
int sp_flags;
union {
struct mode_err_recov page1;
char rawbuf[MAX_MODE_SENSE_SIZE];
/*
* If debugging, issue mode senses on the default and
* current values.
*/
if (option_msg && diag_msg) {
}
/*
* Issue a mode sense to determine the saved parameters
* If the saved values fail, use the current instead.
*/
if (status) {
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.
*/
if (length < MIN_PAGE1_LEN) {
return (0);
}
/*
* Ask for changeable parameters.
*/
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;
fixed->read_retry_count != 0) {
}
if (length > 8) {
fixed->write_retry_count != 0) {
}
}
/*
* Report any changes so far...
*/
if (flag && option_msg) {
"PAGE 1: read retries= %d (%d) write retries= %d (%d)\n",
}
/*
* Apply any changes requested via the change list method
*/
/*
* 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.
*/
}
/* If failed, try not saving mode select params. */
sp_flags &= ~MODE_SELECT_SP;
}
if (status && option_msg) {
err_print("\
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) {
}
return (0);
}
/*
* Check disk disconnect/reconnect parameters via mode sense.
* Issue a mode select if we need to change something.
*/
/*ARGSUSED*/
static int
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];
/*
* If debugging, issue mode senses on the default and
* current values.
*/
if (option_msg && diag_msg) {
}
/*
* Issue a mode sense to determine the saved parameters
* If the saved values fail, use the current instead.
*/
if (status) {
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.
*/
if (length < MIN_PAGE2_LEN) {
return (0);
}
/*
* Ask for changeable parameters.
*/
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
*/
/*
* 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.
*/
}
/* If failed, try not saving mode select params. */
sp_flags &= ~MODE_SELECT_SP;
}
if (status && option_msg) {
err_print("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) {
}
return (0);
}
/*
* Check disk format parameters via mode sense.
* Issue a mode select if we need to change something.
*/
/*ARGSUSED*/
static int
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 flag;
int length;
int sp_flags;
union {
struct mode_format page3;
char rawbuf[MAX_MODE_SENSE_SIZE];
/*
* If debugging, issue mode senses on the default and
* current values.
*/
if (option_msg && diag_msg) {
}
/*
* Issue a mode sense to determine the saved parameters
* If the saved values fail, use the current instead.
*/
if (status) {
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.
*/
if (length < MIN_PAGE3_LEN) {
return (0);
}
/*
* Ask for changeable parameters.
*/
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.
*/
fixed->cylinder_skew != 0) {
}
fixed->track_skew != 0) {
}
fixed->sect_track != 0) {
}
fixed->tracks_per_zone != 0) {
}
fixed->alt_sect_zone != 0) {
}
fixed->alt_tracks_vol != 0) {
}
/*
* Notify user of any changes so far
*/
if (flag && option_msg) {
fmt_print("PAGE 3: trk skew= %d (%d) cyl skew= %d (%d) ",
}
/*
* Apply any changes requested via the change list method
*/
/*
* 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.
*/
}
/* If failed, try not saving mode select params. */
sp_flags &= ~MODE_SELECT_SP;
}
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 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) {
}
return (0);
}
/*
* Check disk geometry parameters via mode sense.
* Issue a mode select if we need to change something.
*/
/*ARGSUSED*/
static int
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 flag;
int length;
int sp_flags;
union {
struct mode_geometry page4;
char rawbuf[MAX_MODE_SENSE_SIZE];
/*
* If debugging, issue mode senses on the default and
* current values.
*/
if (option_msg && diag_msg) {
}
/*
* Issue a mode sense to determine the saved parameters
* If the saved values fail, use the current instead.
*/
if (status) {
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.
*/
if (length < MIN_PAGE4_LEN) {
return (0);
}
/*
* Ask for changeable parameters.
*/
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;
}
/*
* Notify user of changes so far
*/
if (flag && option_msg) {
fmt_print("PAGE 4: cylinders= %d heads= %d (%d)\n",
}
/*
* Apply any changes requested via the change list method
*/
/*
* 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.
*/
}
/* If failed, try not saving mode select params. */
sp_flags &= ~MODE_SELECT_SP;
}
if (status && option_msg) {
err_print("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) {
}
return (0);
}
/*
* Check SCSI-2 disk cache parameters via mode sense.
* Issue a mode select if we need to change something.
*/
/*ARGSUSED*/
static int
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];
/*
* 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) {
}
/*
* Issue a mode sense to determine the saved parameters
* If the saved values fail, use the current instead.
*/
if (status) {
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.
*/
if (length < MIN_PAGE8_LEN) {
return (0);
}
/*
* Ask for changeable parameters.
*/
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
*/
/*
* 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.
*/
}
/* If failed, try not saving mode select params. */
sp_flags &= ~MODE_SELECT_SP;
}
if (status && option_msg) {
err_print("\
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) {
}
return (0);
}
/*
* Check CCS disk cache parameters via mode sense.
* Issue a mode select if we need to change something.
*/
/*ARGSUSED*/
static int
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 flag;
int length;
int sp_flags;
union {
struct mode_cache_ccs page38;
char rawbuf[MAX_MODE_SENSE_SIZE];
/*
* First, determine if we need to look at page 38 at all.
* Not all devices support it.
*/
SUP_CACHE_MIN | SUP_CACHE_MAX)) == 0) &&
0x38))) {
return (0);
}
/*
* If debugging, issue mode senses on the default and
* current values.
*/
if (option_msg && diag_msg) {
}
/*
* Issue a mode sense to determine the saved parameters
* If the saved values fail, use the current instead.
*/
if (status) {
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.
*/
if (length < MIN_PAGE38_LEN) {
return (0);
}
/*
* Ask for changeable parameters.
*/
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;
cur_dtype->dtype_cache) {
}
}
}
}
/*
* Notify the user of changes up to this point
*/
if (flag && option_msg) {
fmt_print("PAGE 38: cache mode= 0x%x (0x%x)\n",
fmt_print(" min. prefetch multiplier= %d ",
fmt_print("max. prefetch multiplier= %d\n",
fmt_print(" threshold= %d (%d) ",
fmt_print("min. prefetch= %d (%d) ",
fmt_print("max. prefetch= %d (%d)\n",
}
/*
* Apply any changes requested via the change list method
*/
/*
* 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.
*/
}
/* If failed, try not saving mode select params. */
sp_flags &= ~MODE_SELECT_SP;
}
if (status && option_msg) {
err_print("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) {
}
return (0);
}
/*
* Extract the manufacturer's defect list.
*/
int
struct defect_list *list;
{
int i;
if (i != 0)
return (i);
return (0);
}
/*
* Extract the current defect list.
* For embedded scsi drives, this means both the manufacturer's (P)
* and the grown (G) lists.
*/
int
struct defect_list *list;
{
int i;
if (i != 0)
return (i);
return (0);
}
/*
* Extract the grown list only
*/
int
struct defect_list *list;
{
int i;
if (i != 0)
return (i);
return (0);
}
static int
struct defect_list *list;
int pglist_flags;
{
char rqbuf[255];
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 */
struct scsi_extended_sense *rq;
/*
* First get length of list by asking for the header only.
*/
/*
* Build and execute the uscsi ioctl
*/
if (status != 0) {
/*
* check if read_defect_list_is_supported.
*/
err_print("\nWARNING: Current Disk does not support"
" defect lists. \n");
} else
if (option_msg) {
err_print("No %s defect list.\n",
"grown" : "manufacturer's");
}
return (-1);
}
/*
* Read the full list the second time
*/
if (status) {
err_print("can't read defect list 2nd time");
destroy_data((char *)defects);
return (-1);
}
err_print("not enough defects");
destroy_data((char *)defects);
return (-1);
}
destroy_data((char *)defects);
return (0);
}
/*
* Map a block.
*/
/*ARGSUSED*/
static int
int flag;
{
struct scsi_reassign_blk defect_list;
/*
* Build and execute the uscsi ioctl
*/
(void) memset((char *)&defect_list, 0,
sizeof (struct scsi_reassign_blk));
}
/*
* Convert a SCSI-style defect list to our generic format.
* We can handle different format lists.
*/
static void
struct defect_list *list;
struct scsi_defect_list *def_list;
int list_format;
{
register struct defect_entry *new_defect;
register int i;
unsigned char *cp;
switch (list_format) {
case DLD_BFI_FORMAT:
/*
* Allocate space for the rest of the list.
*/
new_defect = (struct defect_entry *)
cp = (unsigned char *)old_defect;
old_defect1 = old_defect++;
i++;
/*
* Since we reached the end of the list, old_defect
* now points to an invalid reference, since it got
* incremented in the above operation. So we don't
* need to proceed further. new_len needs to be
* incremented to account for the last element.
*/
if (i == len) {
new_len++;
break;
}
cp = (unsigned char *)old_defect;
/*
* Merge adjacent contiguous defect entries into one
* and update the length of the defect
*/
while ((i < len) &&
old_defect1 = old_defect++;
cp = (unsigned char *)old_defect;
i++;
}
}
break;
default:
err_print("scsi_convert_list_to_new: can't deal with it\n");
exit(0);
/*NOTREACHED*/
}
}
/*
* Execute a command and determine the result.
* Uses the "uscsi" ioctl interface, which is
* fully supported.
*
* If the user wants request sense data to be returned
* in case of error then , the "uscsi_cmd" structure
* should have the request sense buffer allocated in
* uscsi_rqbuf.
*
*/
int
int fd;
int flags;
{
struct scsi_extended_sense *rq;
char rqbuf[255];
int status;
int rqlen;
int timeout = 0;
/*
* Set function flags for driver.
*/
}
if (flags & F_RQENABLE) {
}
/*
* If this command will perform a read, set the USCSI_READ flag
*/
if (ucmd->uscsi_buflen > 0) {
/*
* uscsi_cdb is declared as a caddr_t, so any CDB
* command byte with the MSB set will result in a
* compiler error unless we cast to an unsigned value.
*/
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:
break;
}
}
/*
* Set timeout: 30 seconds for all commands except format
*/
case SCMD_FORMAT:
if (ucmd->uscsi_timeout == 0) {
/*
* Get the timeout value computed using page4 geometry.
* add 50% margin to cover defect management overhead.
* add another 50% margin to have a safe timeout.
* If it exceeds 2 hours then use this value.
*/
if ((timeout = scsi_format_time()) > 0) {
/*
* formatting drives with huge capacity
* will cause these heuristics to come
* up with times that overflow ~9 hours
*/
if (timeout > scsi_format_timeout)
}
}
if (option_msg && diag_msg) {
err_print("format_timeout set to %d seconds, %d"
}
break;
default:
break;
}
/*
* Set up Request Sense buffer
*/
}
/*
* Clear global error state
*/
media_error = 0;
/*
* Execute the ioctl
*/
return (status);
}
/*
* Check the status and return appropriate errors if the disk is
* unavailable (could be formatting) or reserved (by other host).
* In either case we can not talk to the disk now.
*/
return (DSK_UNAVAILABLE);
}
return (DSK_RESERVED);
}
/*
* Check for physically removed or completely unresponsive drive
*/
return (DSK_UNAVAILABLE);
}
/*
* If an automatic Request Sense gave us valid
* info about the error, we may be able to use
* that to print a reasonable error msg.
*/
if (option_msg && diag_msg) {
err_print("No request sense for command %s\n",
}
return (-1);
}
if (option_msg && diag_msg) {
err_print("Request sense status for command %s: 0x%x\n",
}
return (-1);
}
if (option_msg) {
err_print("Request sense for command %s failed\n",
}
if (option_msg && diag_msg) {
err_print("Sense data:\n");
}
return (DSK_UNAVAILABLE);
} else {
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.
*/
rq->es_qual_code == 0) {
return (0);
}
}
case KEY_NOT_READY:
break;
case KEY_DATA_PROTECT:
break;
}
}
}
return (-1);
}
return (DSK_UNAVAILABLE);
}
return (0);
}
/*
* Execute a uscsi mode sense command.
* This can only be used to return one page at a time.
* page data separately - this allows us to support
* devices which return either 0 or 1 block descriptors.
* will be returned to it upon subsequent mode selects.
*/
int
int fd; /* file descriptor */
int page_code; /* requested page number */
int page_control; /* current, changeable, etc. */
int page_size; /* size of page_data */
{
struct mode_header *hdr;
int nbytes;
int status;
int maximum;
/*
* Allocate a buffer for the mode sense headers
* and mode sense data itself.
*/
nbytes = sizeof (struct block_descriptor) +
sizeof (struct mode_header) + page_size;
return (-1);
}
/*
* Build and execute the uscsi ioctl
*/
if (status) {
if (option_msg) {
err_print("Mode sense page 0x%x failed\n",
}
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->bdesc_length != 0) {
if (option_msg) {
err_print("\
\nMode sense page 0x%x: block descriptor length %d incorrect\n",
if (diag_msg)
}
return (-1);
}
if (option_msg) {
err_print("\
\nMode sense page 0x%x: incorrect page code 0x%x\n",
if (diag_msg)
}
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.
*/
if (option_msg) {
err_print("\
Mode sense page 0x%x: incorrect page length %d - expected max %d\n",
if (diag_msg)
}
return (-1);
}
if (option_msg && diag_msg) {
sizeof (struct scsi_ms_header), HEX_ONLY);
}
return (0);
}
/*
* Execute a uscsi mode select command.
*/
int
int fd; /* file descriptor */
int page_code; /* mode select page */
int page_size; /* size of page_data */
{
int nbytes;
int status;
/*
* Allocate a buffer for the mode select header and data
*/
nbytes = sizeof (struct block_descriptor) +
sizeof (struct mode_header) + page_size;
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.
*/
nbytes = sizeof (struct mode_header);
sizeof (struct block_descriptor)) {
nbytes += sizeof (struct block_descriptor);
}
/*
* Dump the structures if anyone's interested
*/
if (option_msg && diag_msg) {
char *s;
s != NULL ? s : "");
}
/*
* Fix the code for byte ordering
*/
switch (page_code) {
case DAD_MODE_ERR_RECOV:
{
struct mode_err_recov *pd;
break;
}
case MODEPAGE_DISCO_RECO:
{
struct mode_disco_reco *pd;
break;
}
case DAD_MODE_FORMAT:
{
struct mode_format *pd;
break;
}
case DAD_MODE_GEOMETRY:
{
struct mode_geometry *pd;
break;
}
case DAD_MODE_CACHE:
{
struct mode_cache *pd;
break;
}
case MODEPAGE_PDEVICE:
{
struct mode_pdevice *pd;
break;
}
case MODEPAGE_CTRL_MODE:
{
struct mode_control *pd;
break;
}
}
/*
* Put the header and data together
*/
/*
* Build and execute the uscsi ioctl
*/
if (status && option_msg) {
}
return (status);
}
/*
* Execute a uscsi inquiry command and return the
* resulting data.
*/
int
int fd;
int inqbufsiz;
{
struct scsi_inquiry *inq;
int n;
int status;
inqbufsiz < 256);
/*
* Build and execute the uscsi ioctl
*/
if (status) {
if (option_msg) {
err_print("Inquiry failed\n");
}
} else if (option_msg && diag_msg) {
/*
* Dump the inquiry data if anyone's interested
*/
err_print("Inquiry:\n");
}
return (status);
}
/*
* Execute a uscsi inquiry command with page code 86h
*/
int
int fd;
int inqbufsiz;
{
int status;
inqbufsiz < 256);
/*
* Build and execute uscsi ioctl
*/
if (status) {
if (option_msg) {
err_print("Inquriy with page_86h failed\n");
}
}
return (status);
}
/*
* Return the Read Capacity information
*/
int
int fd;
struct scsi_capacity_16 *capacity;
{
int status;
/*
* Read Capacity (16) is a Service Action In command. One
* command byte (0x9E) is overloaded for multiple operations,
* with the second CDB byte specifying the desired operation
*/
/*
* Fill in allocation length field
*/
if (status) {
if (option_msg) {
err_print("Read capacity 16 failed\n");
}
} else if (option_msg && diag_msg) {
/*
* Dump the capacity data if anyone's interested
*/
sizeof (struct scsi_capacity_16), HEX_ONLY);
}
return (status);
}
int
int fd;
struct scsi_capacity_16 *capacity;
{
int status;
struct scsi_capacity cap_old;
/*
* Build and execute the uscsi ioctl
*/
/*
* A capacity of 0xffffffff in response to a
* READ CAPACITY 10 indicates that the lun
* is too large to report the size in a 32 bit
* value, and a READ CAPACITY 16 is required
* to get the correct size.
*/
sizeof (union scsi_cdb));
/*
* Read Capacity (16) is a Service Action In command. One
* command byte (0x9E) is overloaded for multiple operations,
* with the second CDB byte specifying the desired operation
*/
/*
* Fill in allocation length field
*/
}
if (status) {
if (option_msg) {
/*
* Indicate which of the commands failed
*/
err_print("Read capacity failed\n");
} else {
err_print("Read capacity 16 failed\n");
}
}
} else if (option_msg && diag_msg) {
/*
* Dump the capacity data if anyone's interested
*/
sizeof (struct scsi_capacity_16), HEX_ONLY);
} else {
sizeof (struct scsi_capacity), HEX_ONLY);
}
}
} else {
}
return (status);
}
/*
* Reserve the current disk
*/
static int
{
int status = 0;
#ifdef sparc
/*
* Build and execute the uscsi ioctl
*/
if (status) {
/*
* return success.
*/
if (status) {
if (option_msg) {
"Reserve" : "Release");
}
}
}
#else /* not sparc */
#ifdef lint
#endif /* lint */
#endif /* not sparc */
return (status);
}
int
int page_control;
{
char *msbuf;
int nbytes;
char *pc_str;
int status;
struct mode_header *mh;
char *p;
int n;
char s[16];
int result = 0;
/*
* Allocate memory for the mode sense buffer.
*/
nbytes = 255;
/*
* Build and execute the uscsi ioctl
*/
if (status) {
err_print("\nMode sense page 0x3f (%s) failed\n",
pc_str);
result = 1;
} else {
p = msbuf + sizeof (struct mode_header) +
while (nbytes > 0) {
nbytes -= n;
if (nbytes < 0)
break;
p += n;
}
if (nbytes < 0) {
err_print(" Sense data formatted incorrectly:\n");
result = 1;
}
err_print("\n");
}
return (result);
}
static void
struct scsi_extended_sense *rq;
int rqlen;
{
struct scsi_descr_sense_hdr *sdsp =
(struct scsi_descr_sense_hdr *)rq;
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;
}
/*
* Get asc, ascq and info field from sense data. There are two
* possible formats (fixed sense data and descriptor sense data)
* depending on the value of es_code.
*/
case CODE_FMT_DESCR_CURRENT:
case CODE_FMT_DESCR_DEFERRED:
blkno =
err_print(")");
}
err_print("\n");
err_print("ASC: 0x%x ASCQ: 0x%x\n",
break;
case CODE_FMT_FIXED_CURRENT:
case CODE_FMT_FIXED_DEFERRED:
default:
err_print(")");
}
err_print("\n");
err_print("ASC: 0x%x ASCQ: 0x%x\n",
}
break;
}
if (option_msg && diag_msg) {
}
}
if (option_msg) {
case CODE_FMT_DESCR_CURRENT:
case CODE_FMT_DESCR_DEFERRED:
break;
case CODE_FMT_FIXED_CURRENT:
case CODE_FMT_FIXED_DEFERRED:
default:
break;
}
}
}
/*
* Retrieve "information" field from descriptor format
* sense data. Iterates through each sense descriptor
* looking for the information descriptor and returns
* the information field from that descriptor.
*/
static diskaddr_t
{
int valid_sense_length;
struct scsi_information_sense_descr *isd;
/*
* Initialize result to -1 indicating there is no information
* descriptor
*/
/*
* The first descriptor will immediately follow the header
*/
/*
* Calculate the amount of valid sense data
*/
min((sizeof (struct scsi_descr_sense_hdr) +
rqlen);
/*
* Iterate through the list of descriptors, stopping when we
* run out of sense data
*/
while ((descr_offset + sizeof (struct scsi_information_sense_descr)) <=
/*
* Check if this is an information descriptor. We can
* use the scsi_information_sense_descr structure as a
* template sense the first two fields are always the
* same
*/
/*
* Found an information descriptor. Copy the
* information field. There will only be one
* information descriptor so we can stop looking.
*/
result =
break;
}
/*
* Get pointer to the next descriptor. The "additional
* length" field holds the length of the descriptor except
* for the "type" and "additional length" fields, so
* we need to add 2 to get the total length.
*/
}
return (result);
}
/*
* Return a pointer to a string telling us the name of the command.
*/
static char *
{
struct scsi_command_name *c;
break;
return (c->name);
}
/*
* Return true if we support a particular mode page
*/
int
scsi_supported_page(int page) {
}
int
{
uchar_t c;
int i;
int m;
int delta;
int changed = 0;
c = curbits[i];
case CHG_MODE_SET:
break;
case CHG_MODE_CLR:
break;
case CHG_MODE_ABS:
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.
*/
for (m = 0x01; m < 0x100; m <<= 1) {
curbits[i] ^= m;
changed = 1;
}
}
}
}
return (changed);
}
/*
* Return whether a given page is affected by an item on
* the change list.
*/
static int
int pageno;
{
return (1);
}
}
return (0);
}
/*
* Labels for the various fields of the scsi_extended_sense structure
*/
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: "
};
/*
* Display the full scsi_extended_sense as returned by the device
*/
static void
struct scsi_extended_sense *rq;
int rqlen;
{
char **p;
/*
* target should be capable of returning at least 18
* bytes of data, i.e upto rq->es_skey_specific field.
* The additional sense bytes (2 or more ...) are optional.
*/
return;
}
}
fmt_print("\n");
}
/*
* Labels for the various fields of the scsi_descr_sense_hdr structure
*/
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: "
};
/*
* Display the full descriptor sense data as returned by the device
*/
static void
struct scsi_descr_sense_hdr *rq;
int rqlen;
{
char **p;
int valid_sense_length;
struct scsi_information_sense_descr *isd;
if (rqlen < sizeof (struct scsi_descr_sense_hdr)) {
/*
* target must return at least 8 bytes of data
*/
return;
}
/* Print descriptor sense header */
fmt_print("\n");
/*
* Now print any sense descriptors. The first descriptor will
* immediately follow the header
*/
/*
* Calculate the amount of valid sense data
*/
min((sizeof (struct scsi_descr_sense_hdr) +
/*
* Iterate through the list of descriptors, stopping when we
* run out of sense data. Descriptor format is:
*
* <Descriptor type> <Descriptor length> <Descriptor data> ...
*/
/*
* Determine descriptor type. We can use the
* scsi_information_sense_descr structure as a
* template since the first two fields are always the
* same.
*/
switch (isd->isd_descr_type) {
case DESCR_INFORMATION: {
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;
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;
fmt_print("Sense-key specific: "
"0x%02x 0x%02x 0x%02x\n", sk_spec_ptr[0],
break;
}
case DESCR_FRU: {
struct scsi_fru_sense_descr *fsd =
(struct scsi_fru_sense_descr *)isd;
fmt_print("Field replaceable unit 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: "
break;
}
default:
/* Ignore */
break;
}
/*
* Get pointer to the next descriptor. The "additional
* length" field holds the length of the descriptor except
* for the "type" and "additional length" fields, so
* we need to add 2 to get the total length.
*/
}
fmt_print("\n");
}
/*
* Function checks if READ DEFECT DATA command is supported
* on the current disk.
*/
static int
{
struct scsi_defect_list def_list;
struct scsi_defect_hdr *hdr;
int status;
char rqbuf[255];
struct scsi_extended_sense *rq;
/*
* First get length of list by asking for the header only.
*/
/*
* Build and execute the uscsi ioctl
*/
if (status != 0) {
/*
* check if read_defect_list_is_supported.
*/
return (0);
}
return (1);
}
/*
* Format the disk, the whole disk, and nothing but the disk.
* Function will be called only for disks
* which do not support read defect list command.
*/
static int
{
struct scsi_defect_hdr defect_hdr;
int status;
/*
* Construct the uscsi format ioctl.
* Use fmtdata = 0 , indicating the no source of
* defects information is provided .
* Function will be called only for disks
* which do not support read defect list command.
*/
/*
* Issue the format ioctl
*/
return (status);
}
/*
* Name: test_until_ready
*
* Description: This function is used by scsi_format and
* scsi_format_raw to poll the device while the FORMAT
* UNIT cdb in in progress.
*
* Parameters:
* file descriptor to poll
*
* Returns:
* 0 - good status
* !0 - bad status
*/
static int test_until_ready(int fd) {
int status = 1;
struct scsi_extended_sense sense;
/* Loop sending TEST UNIT READY until format is complete */
while (status) {
/* clear last request sense data */
ucmd.uscsi_rqstatus = 0;
ucmd.uscsi_rqresid = 0;
/* issue test unit ready */
| F_RQENABLE);
/* If device returns not ready we get EIO */
/* Check SKSV if progress indication is avail */
/* Store progress indication */
es_skey_specific[2];
(float)PROGRESS_INDICATION_BASE)*100);
fmt_print("\015");
/*
* check to see if we can estimate
* time remaining - wait until the format
* is at least 5 percent complete to avoid
* wildly-fluctuating time estimates
*/
/* unable to estimate */
fmt_print(" %02d%% complete ",
progress);
} else {
/* display with estimated time */
(float)(100 - progress));
fmt_print(" %02d%% complete "
"(%02d:%02d:%02d remaining) ",
}
/* flush or the screen will not update */
}
} else {
/* format not in progress */
if (option_msg) {
fmt_print("\nRequest Sense ASC=0x%x ASCQ=0x%x",
}
break;
}
/* delay so we don't waste cpu time */
(void) sleep(RETRY_DELAY);
}
return (status);
}
/*
* Get the current protection type from the PROT_EN and P_TYPE
*/
{
if (prot_en == 0) {
p_type = 0;
} else {
p_type += 1;
}
return (p_type);
}