/*
 * |-----------------------------------------------------------|
 * | Copyright (c) 1990 MIPS Computer Systems, Inc.            |
 * | All Rights Reserved                                       |
 * |-----------------------------------------------------------|
 * |          Restricted Rights Legend                         |
 * | Use, duplication, or disclosure by the Government is      |
 * | subject to restrictions as set forth in                   |
 * | subparagraph (c)(1)(ii) of the Rights in Technical        |
 * | Data and Computer Software Clause of DFARS 52.227-7013.   |
 * |         MIPS Computer Systems, Inc.                       |
 * |         928 Arques Avenue                                 |
 * |         Sunnyvale, CA 94086                               |
 * |-----------------------------------------------------------|
 */
#ident	"$Header: dmodes.c,v 1.3.2.1 92/06/24 13:28:21 marker Exp $"

#include <stdio.h>
#include <sys/types.h>
#include <sys/fcntl.h>
#include <sys/gen_ioctl.h>
#include <sys/utsname.h>
#include <sys/errno.h>
#include <sys/param.h>
#include <sys/buf.h>
#include <sys/dvh.h>
#include <sys/scsi.h>
#include <sys/dkio.h>

#define MY_MAX_DEFECTS	1024

typedef struct my_defects {
	DEFECT_HEADER header;
	DEFECT_ENTRY  entry[MY_MAX_DEFECTS];
} MY_DEFECTS;

MY_DEFECTS pdefects, gdefects;

int fd, heads = 0;
struct gioctl gioc;
struct io_arg arg;
struct utsname unstr;
int readcap[2];

SCSI_MS_ERROR error_pg;
SCSI_MS_DISREC disrec_pg;
SCSI_MS_FORMAT format_pg;
SCSI_MS_GEOM geom_pg;
SCSI_MS_CACHE cache_pg;
SCSI_MS_RDAHD rdahd_pg;

struct page_info {
	int page_no;
	int page_size;
	int page_buf;
} pages[] = {
	{PD_ERROR,sizeof(SCSI_MS_ERROR),(int)&error_pg},
	{PD_DISREC,sizeof(SCSI_MS_DISREC),(int)&disrec_pg},
	{PD_FORMAT,sizeof(SCSI_MS_FORMAT),(int)&format_pg},
	{PD_GEOM,sizeof(SCSI_MS_GEOM),(int)&geom_pg},
	{PD_RDAHD,sizeof(SCSI_MS_RDAHD),(int)&rdahd_pg},
	{PD_CACHE,sizeof(SCSI_MS_CACHE),(int)&cache_pg},
};

int pd_error, pd_disrec, pd_format, pd_geom, pd_cache, pd_rdahd;
int qflag = 0;

void check_defects();

extern int errno;

main(int argc,char **argv)
{
    extern int optind;
    extern char *optarg;
    register SCSI_MS_ERROR *err;
    register SCSI_MS_DISREC *dis;
    register SCSI_MS_FORMAT *fmt;
    register SCSI_MS_GEOM *gm;
    register SCSI_MS_CACHE *ch;
    register SCSI_MS_RDAHD *rd;
    char string[512];
    int c, i, temp;
    int eflag=0, pflag=0, cflag=0, post=0, early=0, cache=0;
    int dflag=0, rflag=0, disrec=0, reconn=0;
    int omode = 0;

    while ((c = getopt(argc,argv,"qoc:d:r:e:p:")) != EOF)
        switch (c) {
            case 'e':
		++omode;
		eflag = 1;
                early = atoi(optarg);
                break;
            case 'p':
		++omode;
		pflag = 1;
                post = atoi(optarg);
                break;
            case 'q':
		qflag = 1;
                break;
            case 'c':
		++omode;
		cflag = 1;
                cache = atoi(optarg);
                break;
            case 'd':
		++omode;
		dflag = 1;
                disrec = atoi(optarg);
                break;
            case 'r':
		++omode;
		rflag = 1;
                reconn = atoi(optarg);
                break;
            default:
syntax:
                printf(
"SYNTAX: dmodes [-q] [-o] [-e #] [-p #] [-c #] [-d #] [-r #] <device_name>\n");
		printf(
"\tq\tquiet mode - only print pages which you are changing\n");
		printf(
"\te #\tearly correction - 0 = disable early ecc correction  1 = enable\n");
		printf(
"\tp #\tpost errors - 0 = disable report soft errors 1 = enable\n");
		printf(
"\tc #\tcache mode - 0 = disable drive caching 1 = enable\n");
		printf(
"\td #\tdisconnect parameters - set disconnect ratio to #\n");
		printf(
"\tr #\treconnect parameters - set reconnect ratio to #\n");
                exit(1);
        }
    if ((argc-optind) != 1)
        goto syntax;
    argv = &argv[optind];
    omode = omode ? O_RDWR : O_RDONLY;
    if ((fd = open(argv[0],omode,0)) < 0) {
	perror(argv[0]);
	exit(1);
    }

    if(ioctl(fd,GIOCPRSTR,&gioc)) 
	printf("Cannot get device identifictaion string\n");
    else {
	printf("%s\n",gioc.gi_ident);
	temp = IDENT_VENDOR + IDENT_PRODUCT + IDENT_REVISION + IDENT_MICRO +
		IDENT_SERIAL + 5;
	if (strncmp("DISK",&gioc.gi_ident[temp],4))
	    return(0);
    }

    arg.memaddr = (unsigned long)readcap;
    arg.datasz = sizeof(readcap);
    if(ioctl(fd, DIOCRDCAP, &arg) < 0){
	printf("Unable to read drive capacity\n");
    }
    printf("Maxblock = %d, blocksize = %d\n",readcap[0],readcap[1]);

    for (i = 0; i < sizeof(pages)/sizeof(struct page_info); ++i) {
	if (pages[i].page_no == PD_CACHE && pd_rdahd)
	    continue;
	gioc.gi_page = pages[i].page_no;
	gioc.gi_addr = pages[i].page_buf;
	gioc.gi_size = pages[i].page_size;
	if(!ioctl(fd,GIOCGETVAL,&gioc)) {
	    switch (pages[i].page_no) {
	    case PD_ERROR:
		err = (SCSI_MS_ERROR *)gioc.gi_addr;
		if (err->msense.hdr.blk_desc_len == 0)
		    err = (SCSI_MS_ERROR *)((int)err-sizeof(SCSI_MS_BLK_DESC));
		if (err->error_hdr.page_length)
		    pd_error = 1;
		break;
	    case PD_DISREC:
		dis = (SCSI_MS_DISREC *)gioc.gi_addr;
		if (dis->msense.hdr.blk_desc_len == 0)
		    dis = (SCSI_MS_DISREC *)((int)dis-sizeof(SCSI_MS_BLK_DESC));
		if (dis->disrec_hdr.page_length)
		    pd_disrec = 1;
		break;
	    case PD_FORMAT:
		fmt = (SCSI_MS_FORMAT *)gioc.gi_addr;
		if (fmt->msense.hdr.blk_desc_len == 0)
		    fmt = (SCSI_MS_FORMAT *)((int)fmt-sizeof(SCSI_MS_BLK_DESC));
		if (fmt->format_hdr.page_length)
		    pd_format = 1;
		break;
	    case PD_GEOM:
		gm = (SCSI_MS_GEOM *)gioc.gi_addr;
		if (gm->msense.hdr.blk_desc_len == 0)
		    gm = (SCSI_MS_GEOM *)((int)gm-sizeof(SCSI_MS_BLK_DESC));
		if (gm->geom_hdr.page_length)
		    pd_geom = 1;
		break;
	    case PD_CACHE:
		ch = (SCSI_MS_CACHE *)gioc.gi_addr;
		if (ch->msense.hdr.blk_desc_len == 0)
		    ch = (SCSI_MS_CACHE *)((int)ch-sizeof(SCSI_MS_BLK_DESC));
		if (ch->cache_hdr.page_length)
		    pd_cache = 1;
		break;
	    case PD_RDAHD:
		rd = (SCSI_MS_RDAHD *)gioc.gi_addr;
		if (rd->msense.hdr.blk_desc_len == 0)
		    rd = (SCSI_MS_RDAHD *)((int)rd-sizeof(SCSI_MS_BLK_DESC));
		if (rd->rdahd_hdr.page_length)
		    pd_rdahd = 1;
		break;
	    }
	}
    } 
    if (pd_geom) {
	heads = geom_pg.heads;
	check_defects();
    }
    if ((eflag || pflag) && pd_error) {
	gioc.gi_addr = (int)&error_pg;
	temp = error_pg.error_hdr.page_length + sizeof(SCSI_MODE_SENSE) + 
		sizeof(SCSI_MS_PAGE_HDR);
	if (temp > sizeof(SCSI_MS_ERROR)) {
		printf("No space for error recovery parameters\n");
	} else {
	    gioc.gi_size = temp;
	    gioc.gi_page = PD_ERROR;
	    if (pflag)
		error_pg.per = post ? 1 : 0;
	    if (eflag)
		error_pg.eec = early ? 1 : 0;
	    if(ioctl(fd,GIOCSETVAL,&gioc)) {
		printf("Cannot set Error Recovery modes\n");
		if (errno == EINVAL)
		    printf("\tIoctl not supported\n");
		else if (gioc.gi_retval == GIOC_OPERR)
		    printf("\tNot supported by drive\n");
	    } 
	    if(ioctl(fd,GIOCGETVAL,&gioc)) {
		printf("Cannot get new Error Recovery modes\n");
		if (errno == EINVAL)
		    printf("\tIoctl not supported\n");
		else if (gioc.gi_retval == GIOC_OPERR)
		    printf("\tNot supported by drive\n");
	    } 
	} 
    } 
    if (cflag) {
	arg.sectst = (unsigned long)(cache ? 1 : 0);
	if(ioctl(fd, DIOCDISKCACHE, &arg) < 0){
	    printf("Unable to enable drive caching\n");
	    if (errno == EINVAL)
		printf("\tIoctl not supported\n");
	    else if (arg.retval == DIOC_OPERR)
		printf("\tNot supported by drive\n");
	} 
	if (pd_rdahd || pd_cache) {
	    if (pd_rdahd) {
		gioc.gi_page = PD_RDAHD;
		gioc.gi_size = sizeof(SCSI_MS_RDAHD);
		gioc.gi_addr = (int)&rdahd_pg;
	    } else if (pd_cache) {
		gioc.gi_page = PD_CACHE;
		gioc.gi_size = sizeof(SCSI_MS_CACHE);
		gioc.gi_addr = (int)&cache_pg;
	    }
	    if(ioctl(fd,GIOCGETVAL,&gioc)) {
		printf("Cannot get new cache control modes\n");
		if (errno == EINVAL)
		    printf("\tIoctl not supported\n");
		else if (gioc.gi_retval == GIOC_OPERR)
		    printf("\tNot supported by drive\n");
	    } 
	} 
    } 
    if ((dflag || rflag) && pd_disrec) {
	gioc.gi_addr = (int)&disrec_pg;
	temp = disrec_pg.disrec_hdr.page_length + sizeof(SCSI_MODE_SENSE) + 
		sizeof(SCSI_MS_PAGE_HDR);
	if (temp > sizeof(SCSI_MS_DISREC)) {
		printf("No space for disconnect/reconnect parameters\n");
	} else {
	    gioc.gi_size = temp;
	    gioc.gi_page = PD_DISREC;
	    if (dflag)
		disrec_pg.bfr = disrec;
	    if (rflag)
		disrec_pg.ber = reconn;
	    if(ioctl(fd,GIOCSETVAL,&gioc)) {
		printf("Cannot set disconnect/reconnect modes\n");
		if (errno == EINVAL)
		    printf("\tIoctl not supported\n");
		else if (gioc.gi_retval == GIOC_OPERR)
		    printf("\tNot supported by drive\n");
	    } 
	    if(ioctl(fd,GIOCGETVAL,&gioc)) {
		printf("Cannot get new disconnect/reconnect modes\n");
		if (errno == EINVAL)
		    printf("\tIoctl not supported\n");
		else if (gioc.gi_retval == GIOC_OPERR)
		    printf("\tNot supported by drive\n");
	    } 
	} 
    } 
    if (pd_error && (!qflag || eflag || pflag)) {
	printf("\nERROR RECOVERY PAGE\n");
	printf("\tAuto Write Realloc Enable = %d\n",error_pg.awre);
	printf("\tAuto Read Realloc Enable  = %d\n",error_pg.arre);
	printf("\tTransfer Failing Block    = %d\n",error_pg.tb);
	printf("\tRead Continuous           = %d\n",error_pg.rc);
	printf("\tEnable Early Correction   = %d\n",error_pg.eec);
	printf("\tPost Error                = %d\n",error_pg.per);
	printf("\tDisable Transfer on Error = %d\n",error_pg.dte);
	printf("\tDisable Correction        = %d\n",error_pg.dcr);
	printf("\tRetry Count               = %d\n",error_pg.retry_cnt);
	printf("\tCorrection Span           = %d\n",error_pg.corr_span);
	printf("\tHead Offset Count         = %d\n",error_pg.head_off);
	printf("\tData Strobe Offset        = %d\n", error_pg.dstrobe_off);
	printf("\tRecovery Time Limit       = %d\n",error_pg.rtime);
    }
    if (pd_disrec && (!qflag || dflag || rflag)) {
	printf("\nDISCONNECT/RECONNECT CONTROL PAGE\n");
	printf("\tBuffer Full Ratio     = %d\n",disrec_pg.bfr);
	printf("\tBuffer Empty Ratio    = %d\n",disrec_pg.ber);
	printf("\tBus Inactivity Limit  = %d\n",disrec_pg.bil);
	printf("\tDisconnect Time Limit = %d\n",disrec_pg.dtl);
	printf("\tConnect Time Limit    = %d\n",disrec_pg.ctl);
    }
    if (pd_format && !qflag) {
	printf("\nFORMAT PARAMETER PAGE\n");
	printf("\tTracks Per Zone       = %d\n",format_pg.tpz);
	printf("\tAlt Sectors Per Zone  = %d\n",format_pg.aspz);
	printf("\tAlt Tracks Per Zone   = %d\n",format_pg.atpz);
	printf("\tAlt Tracks Per Volume = %d\n",format_pg.atpv);
	printf("\tSectors Per Track     = %d\n",format_pg.spt);
	printf("\tBytes Per Sector      = %d\n",format_pg.bps);
	printf("\tInterleave            = %d\n",format_pg.ilv);
	printf("\tTrack Skew Factor     = %d\n",format_pg.tsf);
	printf("\tCylinder Skew Factor  = %d\n",format_pg.csf);
	printf("\tSoft Sectoring        = %d\n",format_pg.ssec);
	printf("\tHard Sectoring        = %d\n",format_pg.hsec);
	printf("\tRemovable Media       = %d\n",format_pg.rmb);
	printf("\tSurface Map           = %d\n",format_pg.surf);
	printf("\tInhibit Save          = %d\n",format_pg.ins);
    }
    if (pd_geom && !qflag) {
	printf("\nDISK DRIVE GEOMETRY PARAMETER PAGE\n");
	temp = geom_pg.cyls_hb << 16 | geom_pg.cyls_mb << 8 | geom_pg.cyls_lb;
	printf("\tNumber of Cylinders            = %d\n",temp);
	printf("\tNumber of Heads                = %d\n",geom_pg.heads);
	temp = geom_pg.wpc_hb << 16 | geom_pg.wpc_mb << 8 | geom_pg.wpc_lb;
	printf("\tStart of Write Precomp         = %d\n",temp);
	temp = geom_pg.rwc_hb << 16 | geom_pg.rwc_mb << 8 | geom_pg.rwc_lb;
	printf("\tStart of Reduced Write Current = %d\n",temp);
	printf("\tDrive Step Rate                = %d\n",geom_pg.dsr);
	temp = geom_pg.lz_hb << 16 | geom_pg.lz_mb << 8 | geom_pg.lz_lb;
	printf("\tLanding Zone Cylinder          = %d\n",temp);
    }
    if (pd_rdahd && (!qflag || cflag)) {
	printf("\nREAD-AHEAD CONTROL PARAMETER PAGE\n");
	printf("\tWrite Cache Enable      = %d\n",rdahd_pg.wce);
	printf("\tMultiple Selection      = %d\n",rdahd_pg.ms);
	printf("\tRead Cache Disable      = %d\n",rdahd_pg.rcd);
	printf("\tRead Retention Priority = %d\n",rdahd_pg.rrp);
	printf("\tWrite Retention Priority= %d\n",rdahd_pg.wrp);
	printf("\tDisable Prefetch Length = %d\n",rdahd_pg.dptl);
	printf("\tMinimum Prefetch        = %d\n",rdahd_pg.minpf);
	printf("\tMaximum Prefetch        = %d\n",rdahd_pg.maxpf);
	printf("\tMax Prefetch Ceiling    = %d\n",rdahd_pg.maxpfc);
    }
    if (pd_cache && (!qflag || cflag)) {
	printf("\nCACHE CONTROL PARAMETER PAGE\n");
	printf("\tWrite Index Enable      = %d\n",cache_pg.wie);
	printf("\tCache Enable            = %d\n",cache_pg.ce);
	printf("\tCache Table Size        = %d\n",cache_pg.cts);
	printf("\tPrefetch Threshold      = %d\n",cache_pg.pft);
	printf("\tMaximum Prefetch        = %d\n",cache_pg.maxpf);
	printf("\tMax Prefetch Multiplier = %d\n",cache_pg.maxpfm);
	printf("\tMinimum Prefetch        = %d\n",cache_pg.minpf);
	printf("\tMin Prefetch Multiplier = %d\n",cache_pg.minpfm);
    }
    close(fd);
}

void
check_defects()
{
    DEFECT_ENTRY *dp;
    register int i;
    int head_defects[20];
    int tentries, pentries, gentries;

    for (i = 0; i < heads; ++i)
	head_defects[i] =0;

    arg.sectst = PRIMARY_DEFECTS;
    arg.memaddr = (unsigned long)&pdefects;
    arg.datasz = sizeof(pdefects);
    if(ioctl(fd, DIOCRDEFECTS, &arg) < 0){
	printf("Unable to read drive primary defects\n");
	pentries = 0;
    } else
	pentries = pdefects.header.list_size / 8;
    arg.sectst = GROWTH_DEFECTS;
    arg.memaddr = (unsigned long)&gdefects;
    arg.datasz = sizeof(gdefects);
    if(ioctl(fd, DIOCRDEFECTS, &arg) < 0){
	printf("Unable to read drive growth defects\n");
	gentries = 0;
    } else
	gentries = gdefects.header.list_size / 8;
    if (pentries > MY_MAX_DEFECTS) {
	printf("Could not read total primary defects (%d)\n",pentries);
	pentries = MY_MAX_DEFECTS;
    }
    if (gentries > MY_MAX_DEFECTS) {
	printf("Could not read total growth defects (%d)\n",gentries);
	gentries = MY_MAX_DEFECTS;
    }
    tentries = pentries + gentries;
    printf("Total number of defects = %d\n",tentries);
    if (qflag)
	return;
    printf("\nPRIMARY DEFECT LIST (%d entries)\n",pentries);
    dp = &pdefects.entry[0];
    while (pentries) {
	printf("Cyl = %d head = %d sect = %d\n",dp->def_cyl,
	    dp->def_head,dp->def_sect);
	if ((i = dp->def_head) >= heads)
	    printf("Bad head number\n");
	else
	    head_defects[i]++;
	++dp;
	--pentries;
    }
    printf("\nGROWTH DEFECT LIST (%d entries)\n",gentries);
    dp = &gdefects.entry[0];
    while (gentries) {
	printf("Cyl = %d head = %d sect = %d\n",dp->def_cyl,
	    dp->def_head,dp->def_sect);
	if ((i = dp->def_head) >= heads)
	    printf("Bad head number\n");
	else
	    head_defects[i]++;
	++dp;
	--gentries;
    }
    printf("\nSUMMARY\n");
    for (i = 0; i < heads; ++i) {
	printf("Number of defects on head %d = %d\n",i,head_defects[i]);
    }
}
