/* @(#)scsitransp.c 1.100 16/01/21 Copyright 1988,1995,2000-2016 J. Schilling */ /*#ifndef lint*/ static char sccsid[] = "@(#)scsitransp.c 1.100 16/01/21 Copyright 1988,1995,2000-2016 J. Schilling"; /*#endif*/ /* * SCSI user level command transport routines (generic part). * * Warning: you may change this source, but if you do that * you need to change the _scg_version and _scg_auth* string below. * You may not return "schily" for an SCG_AUTHOR request anymore. * Choose your name instead of "schily" and make clear that the version * string is related to a modified source. * * Copyright (c) 1988,1995,2000-2016 J. Schilling */ /* * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * See the file CDDL.Schily.txt in this distribution for details. * A copy of the CDDL is also available via the Internet at * http://www.opensource.org/licenses/cddl1.txt * * The following exceptions apply: * CDDL ยง3.6 needs to be replaced by: "You may create a Larger Work by * combining Covered Software with other code if all other code is governed by * the terms of a license that is OSI approved (see www.opensource.org) and * you may distribute the Larger Work as a single product. In such a case, * You must make sure the requirements of this License are fulfilled for * the Covered Software." * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file CDDL.Schily.txt from this distribution. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "scgtimes.h" /* * Warning: you may change this source, but if you do that * you need to change the _scg_version and _scg_auth* string below. * You may not return "schily" for an SCG_AUTHOR request anymore. * Choose your name instead of "schily" and make clear that the version * string is related to a modified source. */ LOCAL char _scg_version[] = "0.9"; /* The global libscg version */ LOCAL char _scg_auth_schily[] = "schily"; /* The author for this module */ #define DEFTIMEOUT 20 /* Default timeout for SCSI command transport */ EXPORT char *scg_version __PR((SCSI *scgp, int what)); EXPORT int scg__open __PR((SCSI *scgp, char *device)); EXPORT int scg__close __PR((SCSI *scgp)); EXPORT int scg_numbus __PR((SCSI *scgp)); EXPORT BOOL scg_havebus __PR((SCSI *scgp, int)); EXPORT int scg_initiator_id __PR((SCSI *scgp)); EXPORT int scg_isatapi __PR((SCSI *scgp)); EXPORT int scg_reset __PR((SCSI *scgp, int what)); EXPORT void *scg_getbuf __PR((SCSI *scgp, long)); EXPORT void scg_freebuf __PR((SCSI *scgp)); EXPORT long scg_bufsize __PR((SCSI *scgp, long)); EXPORT void scg_setnonstderrs __PR((SCSI *scgp, const char **)); EXPORT BOOL scg_yes __PR((char *)); #ifdef nonono LOCAL void scg_sighandler __PR((int)); #endif EXPORT int scg_cmd __PR((SCSI *scgp)); EXPORT void scg_vhead __PR((SCSI *scgp)); EXPORT int scg_svhead __PR((SCSI *scgp, char *buf, int maxcnt)); EXPORT int scg_vtail __PR((SCSI *scgp)); EXPORT int scg_svtail __PR((SCSI *scgp, int *retp, char *buf, int maxcnt)); EXPORT void scg_vsetup __PR((SCSI *scgp)); EXPORT int scg_getresid __PR((SCSI *scgp)); EXPORT int scg_getdmacnt __PR((SCSI *scgp)); EXPORT BOOL scg_cmd_err __PR((SCSI *scgp)); EXPORT void scg_printerr __PR((SCSI *scgp)); EXPORT void scg_fprinterr __PR((SCSI *scgp, FILE *f)); EXPORT int scg_sprinterr __PR((SCSI *scgp, char *buf, int maxcnt)); EXPORT int scg__sprinterr __PR((SCSI *scgp, char *buf, int maxcnt)); EXPORT void scg_printcdb __PR((SCSI *scgp)); EXPORT int scg_sprintcdb __PR((SCSI *scgp, char *buf, int maxcnt)); EXPORT void scg_printwdata __PR((SCSI *scgp)); EXPORT int scg_sprintwdata __PR((SCSI *scgp, char *buf, int maxcnt)); EXPORT void scg_printrdata __PR((SCSI *scgp)); EXPORT int scg_sprintrdata __PR((SCSI *scgp, char *buf, int maxcnt)); EXPORT void scg_printresult __PR((SCSI *scgp)); EXPORT int scg_sprintresult __PR((SCSI *scgp, char *buf, int maxcnt)); EXPORT void scg_printstatus __PR((SCSI *scgp)); EXPORT int scg_sprintstatus __PR((SCSI *scgp, char *buf, int maxcnt)); EXPORT void scg_fprbytes __PR((FILE *, char *, unsigned char *, int)); EXPORT void scg_fprascii __PR((FILE *, char *, unsigned char *, int)); EXPORT void scg_prbytes __PR((char *, unsigned char *, int)); EXPORT void scg_prascii __PR((char *, unsigned char *, int)); EXPORT int scg_sprbytes __PR((char *buf, int maxcnt, char *, unsigned char *, int)); EXPORT int scg_sprascii __PR((char *buf, int maxcnt, char *, unsigned char *, int)); EXPORT void scg_fprsense __PR((FILE *f, unsigned char *, int)); EXPORT int scg_sprsense __PR((char *buf, int maxcnt, unsigned char *, int)); EXPORT void scg_prsense __PR((unsigned char *, int)); EXPORT int scg_cmd_status __PR((SCSI *scgp)); EXPORT int scg_sense_key __PR((SCSI *scgp)); EXPORT int scg_sense_code __PR((SCSI *scgp)); EXPORT int scg_sense_qual __PR((SCSI *scgp)); EXPORT void scg_fprintdev __PR((FILE *, struct scsi_inquiry *)); EXPORT void scg_printdev __PR((struct scsi_inquiry *)); EXPORT int scg_printf __PR((SCSI *scgp, const char *form, ...)); EXPORT int scg_errflush __PR((SCSI *scgp)); EXPORT int scg_errfflush __PR((SCSI *scgp, FILE *f)); /* * Return version information for the SCSI transport code. * This has been introduced to make it easier to trace down problems * in applications. * * If scgp is NULL, return general library version information. * If scgp is != NULL, return version information for the low level transport. */ EXPORT char * scg_version(scgp, what) SCSI *scgp; int what; { if (scgp == (SCSI *)0) { switch (what) { case SCG_VERSION: return (_scg_version); /* * If you changed this source, you are not allowed to * return "schily" for the SCG_AUTHOR request. */ case SCG_AUTHOR: return (_scg_auth_schily); case SCG_SCCS_ID: return (sccsid); default: return ((char *)0); } } return (SCGO_VERSION(scgp, what)); } /* * Call low level SCSI open routine from transport abstraction layer. */ EXPORT int scg__open(scgp, device) SCSI *scgp; char *device; { int ret; scg_ops_t *ops; extern scg_ops_t scg_std_ops; /* * Begin restricted code for quality assurance. * * Warning: you are not allowed to modify the quality ensurance code below. * * This restiction is introduced because this way, I hope that people * contribute to the project instead of creating branches. */ #if !defined(IS_SCHILY_XCONFIG) printf("\nWarning: This version of libscg has not been configured via the standard\n"); printf("autoconfiguration method of the Schily makefile system. There is a high risk\n"); printf("that the code is not configured correctly and for this reason will not behave\n"); printf("as expected.\n"); #endif /* * End restricted code for quality assurance. */ scgp->ops = &scg_std_ops; if (device && strncmp(device, "REMOTE", 6) == 0) { ops = scg_remote(); if (ops != NULL) scgp->ops = ops; } ret = SCGO_OPEN(scgp, device); if (ret < 0) return (ret); /* * Now make scgp->fd valid if possible. * Note that scg_scsibus(scgp)/scg_target(scgp)/scg_lun(scgp) may have * changed in SCGO_OPEN(). */ scg_settarget(scgp, scg_scsibus(scgp), scg_target(scgp), scg_lun(scgp)); return (ret); } /* * Call low level SCSI close routine from transport abstraction layer. */ EXPORT int scg__close(scgp) SCSI *scgp; { return (SCGO_CLOSE(scgp)); } /* * Retrieve max DMA count for this target. */ EXPORT long scg_bufsize(scgp, amt) SCSI *scgp; long amt; { long maxdma; maxdma = SCGO_MAXDMA(scgp, amt); if (amt <= 0 || amt > maxdma) amt = maxdma; scgp->maxdma = maxdma; /* Max possible */ scgp->maxbuf = amt; /* Current value */ return (amt); } /* * Allocate a buffer that may be used for DMA. */ EXPORT void * scg_getbuf(scgp, amt) SCSI *scgp; long amt; { void *buf; if (amt <= 0 || amt > scg_bufsize(scgp, amt)) return ((void *)0); buf = SCGO_GETBUF(scgp, amt); scgp->bufptr = buf; return (buf); } /* * Free DMA buffer. */ EXPORT void scg_freebuf(scgp) SCSI *scgp; { SCGO_FREEBUF(scgp); scgp->bufptr = NULL; } /* * Return the max. number of SCSI busses. */ EXPORT BOOL scg_numbus(scgp) SCSI *scgp; { return (SCGO_NUMBUS(scgp)); } /* * Check if 'busno' is a valid SCSI bus number. */ EXPORT BOOL scg_havebus(scgp, busno) SCSI *scgp; int busno; { return (SCGO_HAVEBUS(scgp, busno)); } /* * Return SCSI initiator ID for current SCSI bus if available. */ EXPORT int scg_initiator_id(scgp) SCSI *scgp; { return (SCGO_INITIATOR_ID(scgp)); } /* * Return a hint whether current SCSI target refers to a ATAPI device. */ EXPORT int scg_isatapi(scgp) SCSI *scgp; { return (SCGO_ISATAPI(scgp)); } /* * Reset SCSI bus or target. */ EXPORT int scg_reset(scgp, what) SCSI *scgp; int what; { return (SCGO_RESET(scgp, what)); } /* * Set up nonstd error vector for curren target. * To clear additional error table, call scg_setnonstderrs(scgp, NULL); * Note: do not use this when scanning the SCSI bus. */ EXPORT void scg_setnonstderrs(scgp, vec) SCSI *scgp; const char **vec; { scgp->nonstderrs = vec; } /* * Simple Yes/No answer checker. */ EXPORT BOOL scg_yes(msg) char *msg; { char okbuf[10]; js_printf("%s", msg); flush(); if (getline(okbuf, sizeof (okbuf)) == EOF) exit(EX_BAD); if (streql(okbuf, "y") || streql(okbuf, "yes") || streql(okbuf, "Y") || streql(okbuf, "YES")) return (TRUE); else return (FALSE); } #ifdef nonono LOCAL void scg_sighandler(sig) int sig; { js_printf("\n"); if (scsi_running) { js_printf("Running command: %s\n", scsi_command); js_printf("Resetting SCSI - Bus.\n"); if (scg_reset(scgp) < 0) errmsg("Cannot reset SCSI - Bus.\n"); } if (scg_yes("EXIT ? ")) exit(sig); } #endif /* * Send a SCSI command. * Do error checking and reporting depending on the values of * scgp->verbose, scgp->debug and scgp->silent. */ EXPORT int scg_cmd(scgp) SCSI *scgp; { int ret; register struct scg_cmd *scmd = scgp->scmd; /* * Reset old error messages in scgp->errstr */ scgp->errptr = scgp->errbeg = scgp->errstr; scmd->kdebug = scgp->kdebug; if (scmd->timeout == 0 || scmd->timeout < scgp->deftimeout) scmd->timeout = scgp->deftimeout; if (scgp->disre_disable) scmd->flags &= ~SCG_DISRE_ENA; if (scgp->noparity) scmd->flags |= SCG_NOPARITY; scmd->u_sense.cmd_sense[0] = 0; /* Paranioa */ if (scmd->sense_len > SCG_MAX_SENSE) scmd->sense_len = SCG_MAX_SENSE; else if (scmd->sense_len < 0) scmd->sense_len = 0; if (scgp->verbose) { scg_vhead(scgp); scg_errflush(scgp); } if (scgp->running) { if (scgp->curcmdname) { error("Currently running '%s' command.\n", scgp->curcmdname); } raisecond("SCSI ALREADY RUNNING !!", 0L); } scgp->cb_fun = NULL; gettimeofday(scgp->cmdstart, (struct timezone *)0); scgp->curcmdname = scgp->cmdname; scgp->running = TRUE; ret = SCGO_SEND(scgp); scgp->running = FALSE; __scg_times(scgp); if (scgp->flags & SCGF_IGN_RESID) scmd->resid = 0; if (ret < 0) { if (scmd->ux_errno == 0) scmd->ux_errno = geterrno(); if (scmd->error == SCG_NO_ERROR) scmd->error = SCG_FATAL; if (scgp->debug > 0) { errmsg("ret < 0 errno: %d ux_errno: %d error: %d\n", geterrno(), scmd->ux_errno, scmd->error); } if (scmd->ux_errno == EPERM && scgp->flags & SCGF_PERM_PRINT) { char errbuf[SCSI_ERRSTR_SIZE]; int amt; amt = scg__sprinterr(scgp, errbuf, sizeof (errbuf)); if (amt > 0) { FILE *f = scgp->errfile; if (f == NULL) f = stderr; filewrite(f, errbuf, amt); ferrmsgno(f, scmd->ux_errno, "Cannot send SCSI cmd via ioctl.\n"); fflush(f); } } /* * Old /dev/scg versions will not allow to access targets > 7. * Include a workaround to make this non fatal. */ if (scg_target(scgp) < 8 || scmd->ux_errno != EINVAL) { if (scmd->ux_errno != EPERM || (scgp->flags & SCGF_PERM_PRINT) == 0) { errmsgno(scmd->ux_errno, "Cannot send SCSI cmd via ioctl.\n"); } if (scgp->flags & SCGF_PERM_EXIT) comexit(scmd->ux_errno); } } ret = scg_vtail(scgp); scg_errflush(scgp); if (scgp->cb_fun != NULL) (*scgp->cb_fun)(scgp->cb_arg); return (ret); } /* * Fill the head of verbose printing into the SCSI error buffer. * Action depends on SCSI verbose status. */ EXPORT void scg_vhead(scgp) SCSI *scgp; { scgp->errptr += scg_svhead(scgp, scgp->errptr, scg_errrsize(scgp)); } /* * Fill the head of verbose printing into a buffer. * Action depends on SCSI verbose status. */ EXPORT int scg_svhead(scgp, buf, maxcnt) SCSI *scgp; char *buf; int maxcnt; { register char *p = buf; register int amt; if (scgp->verbose <= 0) return (0); amt = js_snprintf(p, maxcnt, "\nExecuting '%s' command on Bus %d Target %d, Lun %d timeout %ds\n", /* XXX Really this ??? */ /* scgp->cmdname, scg_scsibus(scgp), scg_target(scgp), scgp->scmd->cdb.g0_cdb.lun,*/ scgp->cmdname, scg_scsibus(scgp), scg_target(scgp), scg_lun(scgp), scgp->scmd->timeout); if (amt < 0) return (amt); p += amt; maxcnt -= amt; amt = scg_sprintcdb(scgp, p, maxcnt); if (amt < 0) return (amt); p += amt; maxcnt -= amt; if (scgp->verbose > 1) { amt = scg_sprintwdata(scgp, p, maxcnt); if (amt < 0) return (amt); p += amt; maxcnt -= amt; } return (p - buf); } /* * Fill the tail of verbose printing into the SCSI error buffer. * Action depends on SCSI verbose status. */ EXPORT int scg_vtail(scgp) SCSI *scgp; { int ret; scgp->errptr += scg_svtail(scgp, &ret, scgp->errptr, scg_errrsize(scgp)); return (ret); } /* * Fill the tail of verbose printing into a buffer. * Action depends on SCSI verbose status. */ EXPORT int scg_svtail(scgp, retp, buf, maxcnt) SCSI *scgp; int *retp; char *buf; int maxcnt; { register char *p = buf; register int amt; int ret; ret = scg_cmd_err(scgp) ? -1 : 0; if (retp) *retp = ret; if (ret) { if (scgp->silent <= 0 || scgp->verbose) { amt = scg__sprinterr(scgp, p, maxcnt); if (amt < 0) return (amt); p += amt; maxcnt -= amt; } } if ((scgp->silent <= 0 || scgp->verbose) && scgp->scmd->resid) { if (scgp->scmd->resid < 0) { /* * An operating system that does DMA the right way * will not allow DMA overruns - it will stop DMA * before bad things happen. * A DMA residual count < 0 (-1) is a hint for a DMA * overrun but does not affect the transfer count. */ amt = js_snprintf(p, maxcnt, "DMA overrun, "); if (amt < 0) return (amt); p += amt; maxcnt -= amt; } amt = js_snprintf(p, maxcnt, "resid: %d\n", scgp->scmd->resid); if (amt < 0) return (amt); p += amt; maxcnt -= amt; } if (scgp->verbose > 0 || (ret < 0 && scgp->silent <= 0)) { amt = scg_sprintresult(scgp, p, maxcnt); if (amt < 0) return (amt); p += amt; maxcnt -= amt; } return (p - buf); } /* * Set up SCSI error buffer with verbose print data. * Action depends on SCSI verbose status. */ EXPORT void scg_vsetup(scgp) SCSI *scgp; { scg_vhead(scgp); scg_vtail(scgp); } /* * Return the residual DMA count for last command. * If this count is < 0, then a DMA overrun occured. */ EXPORT int scg_getresid(scgp) SCSI *scgp; { return (scgp->scmd->resid); } /* * Return the actual DMA count for last command. */ EXPORT int scg_getdmacnt(scgp) SCSI *scgp; { register struct scg_cmd *scmd = scgp->scmd; if (scmd->resid < 0) return (scmd->size); return (scmd->size - scmd->resid); } /* * Test if last SCSI command got an error. */ EXPORT BOOL scg_cmd_err(scgp) SCSI *scgp; { register struct scg_cmd *cp = scgp->scmd; if (cp->error != SCG_NO_ERROR || cp->ux_errno != 0 || *(Uchar *)&cp->scb != 0 || cp->u_sense.cmd_sense[0] != 0) /* Paranioa */ return (TRUE); return (FALSE); } /* * Used to print error messges if the command itself has been run silently. * * print the following SCSI codes: * * - command transport status * - CDB * - SCSI status byte * - Sense Bytes * - Decoded Sense data * - DMA status * - SCSI timing * * to SCSI errfile. */ EXPORT void scg_printerr(scgp) SCSI *scgp; { scg_fprinterr(scgp, (FILE *)scgp->errfile); } /* * print the following SCSI codes: * * - command transport status * - CDB * - SCSI status byte * - Sense Bytes * - Decoded Sense data * - DMA status * - SCSI timing * * to a file. */ EXPORT void scg_fprinterr(scgp, f) SCSI *scgp; FILE *f; { char errbuf[SCSI_ERRSTR_SIZE]; int amt; amt = scg_sprinterr(scgp, errbuf, sizeof (errbuf)); if (amt > 0) { filewrite(f, errbuf, amt); fflush(f); } } /* * print the following SCSI codes: * * - command transport status * - CDB * - SCSI status byte * - Sense Bytes * - Decoded Sense data * - DMA status * - SCSI timing * * into a buffer. */ EXPORT int scg_sprinterr(scgp, buf, maxcnt) SCSI *scgp; char *buf; int maxcnt; { int amt; int osilent = scgp->silent; int overbose = scgp->verbose; scgp->silent = 0; scgp->verbose = 0; amt = scg_svtail(scgp, NULL, buf, maxcnt); scgp->silent = osilent; scgp->verbose = overbose; return (amt); } /* * print the following SCSI codes: * * - command transport status * - CDB * - SCSI status byte * - Sense Bytes * - Decoded Sense data * * into a buffer. */ EXPORT int scg__sprinterr(scgp, buf, maxcnt) SCSI *scgp; char *buf; int maxcnt; { register struct scg_cmd *cp = scgp->scmd; register char *err; char *cmdname = "SCSI command name not set by caller"; char errbuf[64]; register char *p = buf; register int amt; switch (cp->error) { case SCG_NO_ERROR : err = "no error"; break; case SCG_RETRYABLE: err = "retryable error"; break; case SCG_FATAL : err = "fatal error"; break; /* * We need to cast timeval->* to long because * of the broken sys/time.h in Linux. */ case SCG_TIMEOUT : js_snprintf(errbuf, sizeof (errbuf), "cmd timeout after %ld.%03ld (%d) s", (long)scgp->cmdstop->tv_sec, (long)scgp->cmdstop->tv_usec/1000, cp->timeout); err = errbuf; break; default: js_snprintf(errbuf, sizeof (errbuf), "error: %d", cp->error); err = errbuf; } if (scgp->cmdname != NULL && scgp->cmdname[0] != '\0') cmdname = scgp->cmdname; amt = serrmsgno(cp->ux_errno, p, maxcnt, "%s: scsi sendcmd: %s\n", cmdname, err); if (amt < 0) return (amt); p += amt; maxcnt -= amt; amt = scg_sprintcdb(scgp, p, maxcnt); if (amt < 0) return (amt); p += amt; maxcnt -= amt; if (cp->error <= SCG_RETRYABLE) { amt = scg_sprintstatus(scgp, p, maxcnt); if (amt < 0) return (amt); p += amt; maxcnt -= amt; } if (cp->scb.chk) { amt = scg_sprsense(p, maxcnt, (Uchar *)&cp->sense, cp->sense_count); if (amt < 0) return (amt); p += amt; maxcnt -= amt; amt = scg__errmsg(scgp, p, maxcnt, &cp->sense, &cp->scb, -1); if (amt < 0) return (amt); p += amt; maxcnt -= amt; } return (p - buf); } /* * XXX Do we need this function? * * print the SCSI Command descriptor block to XXX stderr. */ EXPORT void scg_printcdb(scgp) SCSI *scgp; { scg_prbytes("CDB: ", scgp->scmd->cdb.cmd_cdb, scgp->scmd->cdb_len); } /* * print the SCSI Command descriptor block into a buffer. */ EXPORT int scg_sprintcdb(scgp, buf, maxcnt) SCSI *scgp; char *buf; int maxcnt; { int cnt; cnt = scg_sprbytes(buf, maxcnt, "CDB: ", scgp->scmd->cdb.cmd_cdb, scgp->scmd->cdb_len); if (cnt < 0) cnt = 0; return (cnt); } /* * XXX Do we need this function? * XXX scg_printrdata() is used. * XXX We need to check if we should write to stderr or better to scg->errfile * * print the SCSI send data to stderr. */ EXPORT void scg_printwdata(scgp) SCSI *scgp; { register struct scg_cmd *scmd = scgp->scmd; if (scmd->size > 0 && (scmd->flags & SCG_RECV_DATA) == 0) { js_fprintf(stderr, "Sending %d (0x%X) bytes of data.\n", scmd->size, scmd->size); scg_prbytes("Write Data: ", (Uchar *)scmd->addr, scmd->size > 100 ? 100 : scmd->size); } } /* * print the SCSI send data into a buffer. */ EXPORT int scg_sprintwdata(scgp, buf, maxcnt) SCSI *scgp; char *buf; int maxcnt; { register struct scg_cmd *scmd = scgp->scmd; register char *p = buf; register int amt; if (scmd->size > 0 && (scmd->flags & SCG_RECV_DATA) == 0) { amt = js_snprintf(p, maxcnt, "Sending %d (0x%X) bytes of data.\n", scmd->size, scmd->size); if (amt < 0) return (amt); p += amt; maxcnt -= amt; amt = scg_sprbytes(p, maxcnt, "Write Data: ", (Uchar *)scmd->addr, scmd->size > 100 ? 100 : scmd->size); if (amt < 0) return (amt); p += amt; } return (p - buf); } /* * XXX We need to check if we should write to stderr or better to scg->errfile * * print the SCSI received data to stderr. */ EXPORT void scg_printrdata(scgp) SCSI *scgp; { register struct scg_cmd *scmd = scgp->scmd; register int trcnt = scg_getdmacnt(scgp); if (scmd->size > 0 && (scmd->flags & SCG_RECV_DATA) != 0) { js_fprintf(stderr, "Got %d (0x%X), expecting %d (0x%X) bytes of data.\n", trcnt, trcnt, scmd->size, scmd->size); scg_prbytes("Received Data: ", (Uchar *)scmd->addr, trcnt > 100 ? 100 : trcnt); } } /* * print the SCSI received data into a buffer. */ EXPORT int scg_sprintrdata(scgp, buf, maxcnt) SCSI *scgp; char *buf; int maxcnt; { register struct scg_cmd *scmd = scgp->scmd; register char *p = buf; register int amt; register int trcnt = scg_getdmacnt(scgp); if (scmd->size > 0 && (scmd->flags & SCG_RECV_DATA) != 0) { amt = js_snprintf(p, maxcnt, "Got %d (0x%X), expecting %d (0x%X) bytes of data.\n", trcnt, trcnt, scmd->size, scmd->size); if (amt < 0) return (amt); p += amt; maxcnt -= amt; amt = scg_sprbytes(p, maxcnt, "Received Data: ", (Uchar *)scmd->addr, trcnt > 100 ? 100 : trcnt); if (amt < 0) return (amt); p += amt; } return (p - buf); } /* * XXX We need to check if we should write to stderr or better to scg->errfile * * print the SCSI timings and (depending on verbose) received data to stderr. */ EXPORT void scg_printresult(scgp) SCSI *scgp; { js_fprintf(stderr, "cmd finished after %ld.%03lds timeout %ds\n", (long)scgp->cmdstop->tv_sec, (long)scgp->cmdstop->tv_usec/1000, scgp->scmd->timeout); if (scgp->verbose > 1) scg_printrdata(scgp); flush(); } /* * print the SCSI timings and (depending on verbose) received data into a buffer. */ EXPORT int scg_sprintresult(scgp, buf, maxcnt) SCSI *scgp; char *buf; int maxcnt; { register char *p = buf; register int amt; amt = js_snprintf(p, maxcnt, "cmd finished after %ld.%03lds timeout %ds\n", (long)scgp->cmdstop->tv_sec, (long)scgp->cmdstop->tv_usec/1000, scgp->scmd->timeout); if (amt < 0) return (amt); p += amt; maxcnt -= amt; if (scgp->verbose > 1) { amt = scg_sprintrdata(scgp, p, maxcnt); if (amt < 0) return (amt); p += amt; } return (p - buf); } /* * XXX Do we need this function? * * print the SCSI status byte in human readable form to the SCSI error file. */ EXPORT void scg_printstatus(scgp) SCSI *scgp; { char errbuf[SCSI_ERRSTR_SIZE]; int amt; amt = scg_sprintstatus(scgp, errbuf, sizeof (errbuf)); if (amt > 0) { filewrite((FILE *)scgp->errfile, errbuf, amt); fflush((FILE *)scgp->errfile); } } /* * print the SCSI status byte in human readable form into a buffer. */ EXPORT int scg_sprintstatus(scgp, buf, maxcnt) SCSI *scgp; char *buf; int maxcnt; { register struct scg_cmd *cp = scgp->scmd; char *err; char *err2 = ""; register char *p = buf; register int amt; amt = js_snprintf(p, maxcnt, "status: 0x%x ", *(Uchar *)&cp->scb); if (amt < 0) return (amt); p += amt; maxcnt -= amt; #ifdef SCSI_EXTENDED_STATUS if (cp->scb.ext_st1) { amt = js_snprintf(p, maxcnt, "0x%x ", ((Uchar *)&cp->scb)[1]); if (amt < 0) return (amt); p += amt; maxcnt -= amt; } if (cp->scb.ext_st2) { amt = js_snprintf(p, maxcnt, "0x%x ", ((Uchar *)&cp->scb)[2]); if (amt < 0) return (amt); p += amt; maxcnt -= amt; } #endif switch (*(Uchar *)&cp->scb & 036) { case 0 : err = "GOOD STATUS"; break; case 02 : err = "CHECK CONDITION"; break; case 04 : err = "CONDITION MET/GOOD"; break; case 010: err = "BUSY"; break; case 020: err = "INTERMEDIATE GOOD STATUS"; break; case 024: err = "INTERMEDIATE CONDITION MET/GOOD"; break; case 030: err = "RESERVATION CONFLICT"; break; default : err = "Reserved"; break; } #ifdef SCSI_EXTENDED_STATUS if (cp->scb.ext_st1 && cp->scb.ha_er) err2 = " host adapter detected error"; #endif amt = js_snprintf(p, maxcnt, "(%s%s)\n", err, err2); if (amt < 0) return (amt); p += amt; return (p - buf); } /* * print some bytes in hex to a file. */ EXPORT void scg_fprbytes(f, s, cp, n) FILE *f; char *s; register Uchar *cp; register int n; { js_fprintf(f, "%s", s); while (--n >= 0) js_fprintf(f, " %02X", *cp++); js_fprintf(f, "\n"); } /* * print some bytes in ascii to a file. */ EXPORT void scg_fprascii(f, s, cp, n) FILE *f; char *s; register Uchar *cp; register int n; { register int c; js_fprintf(f, "%s", s); while (--n >= 0) { c = *cp++; if (c >= ' ' && c < 0177) js_fprintf(f, "%c", c); else js_fprintf(f, "."); } js_fprintf(f, "\n"); } /* * XXX We need to check if we should write to stderr or better to scg->errfile * * print some bytes in hex to stderr. */ EXPORT void scg_prbytes(s, cp, n) char *s; register Uchar *cp; register int n; { scg_fprbytes(stderr, s, cp, n); } /* * XXX We need to check if we should write to stderr or better to scg->errfile * * print some bytes in ascii to stderr. */ EXPORT void scg_prascii(s, cp, n) char *s; register Uchar *cp; register int n; { scg_fprascii(stderr, s, cp, n); } /* * print some bytes in hex into a buffer. */ EXPORT int scg_sprbytes(buf, maxcnt, s, cp, n) char *buf; int maxcnt; char *s; register Uchar *cp; register int n; { register char *p = buf; register int amt; amt = js_snprintf(p, maxcnt, "%s", s); if (amt < 0) return (amt); p += amt; maxcnt -= amt; while (--n >= 0) { amt = js_snprintf(p, maxcnt, " %02X", *cp++); if (amt < 0) return (amt); p += amt; maxcnt -= amt; } amt = js_snprintf(p, maxcnt, "\n"); if (amt < 0) return (amt); p += amt; return (p - buf); } /* * print some bytes in ascii into a buffer. */ EXPORT int scg_sprascii(buf, maxcnt, s, cp, n) char *buf; int maxcnt; char *s; register Uchar *cp; register int n; { register char *p = buf; register int amt; register int c; amt = js_snprintf(p, maxcnt, "%s", s); if (amt < 0) return (amt); p += amt; maxcnt -= amt; while (--n >= 0) { c = *cp++; if (c >= ' ' && c < 0177) amt = js_snprintf(p, maxcnt, "%c", c); else amt = js_snprintf(p, maxcnt, "."); if (amt < 0) return (amt); p += amt; maxcnt -= amt; } amt = js_snprintf(p, maxcnt, "\n"); if (amt < 0) return (amt); p += amt; return (p - buf); } /* * print the SCSI sense data for last command in hex to a file. */ EXPORT void scg_fprsense(f, cp, n) FILE *f; Uchar *cp; int n; { scg_fprbytes(f, "Sense Bytes:", cp, n); } /* * XXX We need to check if we should write to stderr or better to scg->errfile * * print the SCSI sense data for last command in hex to stderr. */ EXPORT void scg_prsense(cp, n) Uchar *cp; int n; { scg_fprsense(stderr, cp, n); } /* * print the SCSI sense data for last command in hex into a buffer. */ EXPORT int scg_sprsense(buf, maxcnt, cp, n) char *buf; int maxcnt; Uchar *cp; int n; { return (scg_sprbytes(buf, maxcnt, "Sense Bytes:", cp, n)); } /* * Return the SCSI status byte for last command. */ EXPORT int scg_cmd_status(scgp) SCSI *scgp; { struct scg_cmd *cp = scgp->scmd; int cmdstatus = *(Uchar *)&cp->scb; return (cmdstatus); } /* * Return the SCSI sense key for last command. */ EXPORT int scg_sense_key(scgp) SCSI *scgp; { register struct scg_cmd *cp = scgp->scmd; int key = -1; if (!scg_cmd_err(scgp)) return (0); if (cp->sense.code >= 0x70) key = ((struct scsi_ext_sense *)&(cp->sense))->key; return (key); } /* * Return the SCSI sense code for last command. */ EXPORT int scg_sense_code(scgp) SCSI *scgp; { register struct scg_cmd *cp = scgp->scmd; int code = -1; if (!scg_cmd_err(scgp)) return (0); if (cp->sense.code >= 0x70) code = ((struct scsi_ext_sense *)&(cp->sense))->sense_code; else code = cp->sense.code; return (code); } /* * Return the SCSI sense qualifier for last command. */ EXPORT int scg_sense_qual(scgp) SCSI *scgp; { register struct scg_cmd *cp = scgp->scmd; if (!scg_cmd_err(scgp)) return (0); if (cp->sense.code >= 0x70) return (((struct scsi_ext_sense *)&(cp->sense))->qual_code); else return (0); } /* * Print the device type from the SCSI inquiry buffer to file. */ EXPORT void scg_fprintdev(f, ip) FILE *f; struct scsi_inquiry *ip; { if (ip->removable) js_fprintf(f, "Removable "); if (ip->data_format >= 2) { switch (ip->qualifier) { case INQ_DEV_PRESENT: break; case INQ_DEV_NOTPR: js_fprintf(f, "not present "); break; case INQ_DEV_RES: js_fprintf(f, "reserved "); break; case INQ_DEV_NOTSUP: if (ip->type == INQ_NODEV) { js_fprintf(f, "unsupported\n"); return; } js_fprintf(f, "unsupported "); break; default: js_fprintf(f, "vendor specific %d ", (int)ip->qualifier); } } switch (ip->type) { case INQ_DASD: js_fprintf(f, "Disk"); break; case INQ_SEQD: js_fprintf(f, "Tape"); break; case INQ_PRTD: js_fprintf(f, "Printer"); break; case INQ_PROCD: js_fprintf(f, "Processor"); break; case INQ_WORM: js_fprintf(f, "WORM"); break; case INQ_ROMD: js_fprintf(f, "CD-ROM"); break; case INQ_SCAN: js_fprintf(f, "Scanner"); break; case INQ_OMEM: js_fprintf(f, "Optical Storage"); break; case INQ_JUKE: js_fprintf(f, "Juke Box"); break; case INQ_COMM: js_fprintf(f, "Communication"); break; case INQ_IT8_1: js_fprintf(f, "IT8 1"); break; case INQ_IT8_2: js_fprintf(f, "IT8 2"); break; case INQ_STARR: js_fprintf(f, "Storage array"); break; case INQ_ENCL: js_fprintf(f, "Enclosure services"); break; case INQ_SDAD: js_fprintf(f, "Simple direct access"); break; case INQ_OCRW: js_fprintf(f, "Optical card r/w"); break; case INQ_BRIDGE: js_fprintf(f, "Bridging expander"); break; case INQ_OSD: js_fprintf(f, "Object based storage"); break; case INQ_ADC: js_fprintf(f, "Automation/Drive Interface"); break; case INQ_WELLKNOWN: js_fprintf(f, "Well known lun"); break; case INQ_NODEV: if (ip->data_format >= 2) { js_fprintf(f, "unknown/no device"); break; } else if (ip->qualifier == INQ_DEV_NOTSUP) { js_fprintf(f, "unit not present"); break; } default: js_fprintf(f, "unknown device type 0x%x", (int)ip->type); } js_fprintf(f, "\n"); } /* * Print the device type from the SCSI inquiry buffer to stdout. */ EXPORT void scg_printdev(ip) struct scsi_inquiry *ip; { scg_fprintdev(stdout, ip); } #include /* * print into the SCSI error buffer, adjust the next write pointer. */ /* VARARGS2 */ EXPORT int #ifdef PROTOTYPES scg_printf(SCSI *scgp, const char *form, ...) #else scg_printf(scgp, form, va_alist) SCSI *scgp; char *form; va_dcl #endif { int cnt; va_list args; #ifdef PROTOTYPES va_start(args, form); #else va_start(args); #endif cnt = js_snprintf(scgp->errptr, scg_errrsize(scgp), "%r", form, args); va_end(args); if (cnt < 0) { scgp->errptr[0] = '\0'; } else { scgp->errptr += cnt; } return (cnt); } /* * Flush the SCSI error buffer to SCSI errfile. * Clear error buffer after flushing. */ EXPORT int scg_errflush(scgp) SCSI *scgp; { if (scgp->errfile == NULL) return (0); return (scg_errfflush(scgp, (FILE *)scgp->errfile)); } /* * Flush the SCSI error buffer to a file. * Clear error buffer after flushing. */ EXPORT int scg_errfflush(scgp, f) SCSI *scgp; FILE *f; { int cnt; cnt = scgp->errptr - scgp->errbeg; if (cnt > 0) { filewrite(f, scgp->errbeg, cnt); fflush(f); scgp->errbeg = scgp->errptr; } return (cnt); }