/*
* This file has been modified for the cdrkit suite.
*
* The behaviour and appearence of the program code below can differ to a major
* extent from the version distributed by the original author(s).
*
* For details, see Changelog file distributed with the cdrkit package. If you
* received this file from another source then ask the distributing person for
* a log of modifications.
*
*/
/* @(#)scsitransp.c 1.91 04/06/17 Copyright 1988,1995,2000-2004 J. Schilling */
/*#ifndef lint*/
static char sccsid[] =
"@(#)scsitransp.c 1.91 04/06/17 Copyright 1988,1995,2000-2004 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 _usal_version and _usal_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-2004 J. Schilling
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; see the file COPYING. If not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <mconfig.h>
#include <stdio.h>
#include <standard.h>
#include <stdxlib.h>
#include <unixstd.h>
#include <errno.h>
#include <timedefs.h>
#include <strdefs.h>
#include <schily.h>
#include <usal/usalcmd.h>
#include <usal/scsireg.h>
#include <usal/scsitransp.h>
#include "usaltimes.h"
#include <stdarg.h>
/*
* Warning: you may change this source, but if you do that
* you need to change the _usal_version and _usal_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.
*/
static char _usal_version[] = CDRKIT_VERSION; /* The global libusal version */
static char _usal_auth_cdrkit[] = "Cdrkit"; /* The author for this module */
#define DEFTIMEOUT 20 /* Default timeout for SCSI command transport */
char *usal_version(SCSI *usalp, int what);
int usal__open(SCSI *usalp, char *device);
int usal__close(SCSI *usalp);
BOOL usal_havebus(SCSI *usalp, int);
int usal_initiator_id(SCSI *usalp);
int usal_isatapi(SCSI *usalp);
int usal_reset(SCSI *usalp, int what);
void *usal_getbuf(SCSI *usalp, long);
void usal_freebuf(SCSI *usalp);
long usal_bufsize(SCSI *usalp, long);
void usal_setnonstderrs(SCSI *usalp, const char **);
BOOL usal_yes(char *);
#ifdef nonono
static void usal_sighandler(int);
#endif
int usal_cmd(SCSI *usalp);
void usal_vhead(SCSI *usalp);
int usal_svhead(SCSI *usalp, char *buf, int maxcnt);
int usal_vtail(SCSI *usalp);
int usal_svtail(SCSI *usalp, int *retp, char *buf, int maxcnt);
void usal_vsetup(SCSI *usalp);
int usal_getresid(SCSI *usalp);
int usal_getdmacnt(SCSI *usalp);
BOOL usal_cmd_err(SCSI *usalp);
void usal_printerr(SCSI *usalp);
void usal_fprinterr(SCSI *usalp, FILE *f);
int usal_sprinterr(SCSI *usalp, char *buf, int maxcnt);
int usal__sprinterr(SCSI *usalp, char *buf, int maxcnt);
void usal_printcdb(SCSI *usalp);
int usal_sprintcdb(SCSI *usalp, char *buf, int maxcnt);
void usal_printwdata(SCSI *usalp);
int usal_sprintwdata(SCSI *usalp, char *buf, int maxcnt);
void usal_printrdata(SCSI *usalp);
int usal_sprintrdata(SCSI *usalp, char *buf, int maxcnt);
void usal_printresult(SCSI *usalp);
int usal_sprintresult(SCSI *usalp, char *buf, int maxcnt);
void usal_printstatus(SCSI *usalp);
int usal_sprintstatus(SCSI *usalp, char *buf, int maxcnt);
void usal_fprbytes(FILE *, char *, unsigned char *, int);
void usal_fprascii(FILE *, char *, unsigned char *, int);
void usal_prbytes(char *, unsigned char *, int);
void usal_prascii(char *, unsigned char *, int);
int usal_sprbytes(char *buf, int maxcnt, char *, unsigned char *, int);
int usal_sprascii(char *buf, int maxcnt, char *, unsigned char *, int);
void usal_fprsense(FILE *f, unsigned char *, int);
int usal_sprsense(char *buf, int maxcnt, unsigned char *, int);
void usal_prsense(unsigned char *, int);
int usal_cmd_status(SCSI *usalp);
int usal_sense_key(SCSI *usalp);
int usal_sense_code(SCSI *usalp);
int usal_sense_qual(SCSI *usalp);
unsigned char *usal_sense_table(SCSI *usalp);
void usal_fprintdev(FILE *, struct scsi_inquiry *);
void usal_printdev(struct scsi_inquiry *);
int usal_printf(SCSI *usalp, const char *form, ...);
int usal_errflush(SCSI *usalp);
int usal_errfflush(SCSI *usalp, 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 usalp is NULL, return general library version information.
* If usalp is != NULL, return version information for the low level transport.
*/
char *
usal_version(SCSI *usalp, int what)
{
if (usalp == (SCSI *)0) {
switch (what) {
case SCG_VERSION:
return (_usal_version);
/*
* If you changed this source, you are not allowed to
* return "schily" for the SCG_AUTHOR request.
*/
case SCG_AUTHOR:
return (_usal_auth_cdrkit);
case SCG_SCCS_ID:
return (sccsid);
default:
return ((char *)0);
}
}
return (SCGO_VERSION(usalp, what));
}
/*
* Call low level SCSI open routine from transport abstraction layer.
*/
int
usal__open(SCSI *usalp, char *device)
{
int ret;
usal_ops_t *ops;
extern usal_ops_t usal_std_ops;
usalp->ops = &usal_std_ops;
if (device && strncmp(device, "REMOTE", 6) == 0) {
ops = usal_remote();
if (ops != NULL)
usalp->ops = ops;
}
ret = SCGO_OPEN(usalp, device);
if (ret < 0)
return (ret);
/*
* Now make usalp->fd valid if possible.
* Note that usal_scsibus(usalp)/usal_target(usalp)/usal_lun(usalp) may have
* changed in SCGO_OPEN().
*/
usal_settarget(usalp, usal_scsibus(usalp), usal_target(usalp), usal_lun(usalp));
return (ret);
}
/*
* Call low level SCSI close routine from transport abstraction layer.
*/
int
usal__close(SCSI *usalp)
{
return (SCGO_CLOSE(usalp));
}
/*
* Retrieve max DMA count for this target.
*/
long
usal_bufsize(SCSI *usalp, long amt)
{
long maxdma;
maxdma = SCGO_MAXDMA(usalp, amt);
if (amt <= 0 || amt > maxdma)
amt = maxdma;
usalp->maxdma = maxdma; /* Max possible */
usalp->maxbuf = amt; /* Current value */
return (amt);
}
/*
* Allocate a buffer that may be used for DMA.
*/
void *
usal_getbuf(SCSI *usalp, long amt)
{
void *buf;
if (amt <= 0 || amt > usal_bufsize(usalp, amt))
return ((void *)0);
buf = SCGO_GETBUF(usalp, amt);
usalp->bufptr = buf;
return (buf);
}
/*
* Free DMA buffer.
*/
void
usal_freebuf(SCSI *usalp)
{
SCGO_FREEBUF(usalp);
usalp->bufptr = NULL;
}
/*
* Check if 'busno' is a valid SCSI bus number.
*/
BOOL
usal_havebus(SCSI *usalp, int busno)
{
return (SCGO_HAVEBUS(usalp, busno));
}
/*
* Return SCSI initiator ID for current SCSI bus if available.
*/
int
usal_initiator_id(SCSI *usalp)
{
return (SCGO_INITIATOR_ID(usalp));
}
/*
* Return a hint whether current SCSI target refers to a ATAPI device.
*/
int
usal_isatapi(SCSI *usalp)
{
return (SCGO_ISATAPI(usalp));
}
/*
* Reset SCSI bus or target.
*/
int
usal_reset(SCSI *usalp, int what)
{
return (SCGO_RESET(usalp, what));
}
/*
* Set up nonstd error vector for curren target.
* To clear additional error table, call usal_setnonstderrs(usalp, NULL);
* Note: do not use this when scanning the SCSI bus.
*/
void
usal_setnonstderrs(SCSI *usalp, const char **vec)
{
usalp->nonstderrs = vec;
}
/*
* Simple Yes/No answer checker.
*/
BOOL
usal_yes(char *msg)
{
char okbuf[10];
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
static void
usal_sighandler(int sig)
{
printf("\n");
if (scsi_running) {
printf("Running command: %s\n", scsi_command);
printf("Resetting SCSI - Bus.\n");
if (usal_reset(usalp) < 0)
errmsg("Cannot reset SCSI - Bus.\n");
}
if (usal_yes("EXIT ? "))
exit(sig);
}
#endif
/*
* Send a SCSI command.
* Do error checking and reporting depending on the values of
* usalp->verbose, usalp->debug and usalp->silent.
*/
int
usal_cmd(SCSI *usalp)
{
int ret;
register struct usal_cmd *scmd = usalp->scmd;
/*
* Reset old error messages in usalp->errstr
*/
usalp->errptr = usalp->errbeg = usalp->errstr;
scmd->kdebug = usalp->kdebug;
if (scmd->timeout == 0 || scmd->timeout < usalp->deftimeout)
scmd->timeout = usalp->deftimeout;
if (usalp->disre_disable)
scmd->flags &= ~SCG_DISRE_ENA;
if (usalp->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 (usalp->verbose) {
usal_vhead(usalp);
usal_errflush(usalp);
}
if (usalp->running) {
if (usalp->curcmdname) {
fprintf(stderr, "Currently running '%s' command.\n",
usalp->curcmdname);
}
raisecond("SCSI ALREADY RUNNING !!", 0L);
}
usalp->cb_fun = NULL;
gettimeofday(usalp->cmdstart, (struct timezone *)0);
usalp->curcmdname = usalp->cmdname;
usalp->running = TRUE;
ret = SCGO_SEND(usalp);
usalp->running = FALSE;
__usal_times(usalp);
if (ret < 0) {
/*
* Old /dev/usal versions will not allow to access targets > 7.
* Include a workaround to make this non fatal.
*/
if (usal_target(usalp) < 8 || geterrno() != EINVAL)
comerr("Cannot send SCSI cmd via ioctl\n");
if (scmd->ux_errno == 0)
scmd->ux_errno = geterrno();
if (scmd->error == SCG_NO_ERROR)
scmd->error = SCG_FATAL;
if (usalp->debug > 0) {
errmsg("ret < 0 errno: %d ux_errno: %d error: %d\n",
geterrno(), scmd->ux_errno, scmd->error);
}
}
ret = usal_vtail(usalp);
usal_errflush(usalp);
if (usalp->cb_fun != NULL)
(*usalp->cb_fun)(usalp->cb_arg);
return (ret);
}
/*
* Fill the head of verbose printing into the SCSI error buffer.
* Action depends on SCSI verbose status.
*/
void
usal_vhead(SCSI *usalp)
{
usalp->errptr += usal_svhead(usalp, usalp->errptr, usal_errrsize(usalp));
}
/*
* Fill the head of verbose printing into a buffer.
* Action depends on SCSI verbose status.
*/
int
usal_svhead(SCSI *usalp, char *buf, int maxcnt)
{
register char *p = buf;
register int amt;
if (usalp->verbose <= 0)
return (0);
amt = snprintf(p, maxcnt,
"\nExecuting '%s' command on Bus %d Target %d, Lun %d timeout %ds\n",
/* XXX Really this ??? */
/* usalp->cmdname, usal_scsibus(usalp), usal_target(usalp), usalp->scmd->cdb.g0_cdb.lun,*/
usalp->cmdname, usal_scsibus(usalp), usal_target(usalp), usal_lun(usalp),
usalp->scmd->timeout);
if (amt < 0)
return (amt);
p += amt;
maxcnt -= amt;
amt = usal_sprintcdb(usalp, p, maxcnt);
if (amt < 0)
return (amt);
p += amt;
maxcnt -= amt;
if (usalp->verbose > 1) {
amt = usal_sprintwdata(usalp, 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.
*/
int
usal_vtail(SCSI *usalp)
{
int ret;
usalp->errptr += usal_svtail(usalp, &ret, usalp->errptr, usal_errrsize(usalp));
return (ret);
}
/*
* Fill the tail of verbose printing into a buffer.
* Action depends on SCSI verbose status.
*/
int
usal_svtail(SCSI *usalp, int *retp, char *buf, int maxcnt)
{
register char *p = buf;
register int amt;
int ret;
ret = usal_cmd_err(usalp) ? -1 : 0;
if (retp)
*retp = ret;
if (ret) {
if (usalp->silent <= 0 || usalp->verbose) {
amt = usal__sprinterr(usalp, p, maxcnt);
if (amt < 0)
return (amt);
p += amt;
maxcnt -= amt;
}
}
if ((usalp->silent <= 0 || usalp->verbose) && usalp->scmd->resid) {
if (usalp->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 = snprintf(p, maxcnt, "DMA overrun, ");
if (amt < 0)
return (amt);
p += amt;
maxcnt -= amt;
}
amt = snprintf(p, maxcnt, "resid: %d\n", usalp->scmd->resid);
if (amt < 0)
return (amt);
p += amt;
maxcnt -= amt;
}
if (usalp->verbose > 0 || (ret < 0 && usalp->silent <= 0)) {
amt = usal_sprintresult(usalp, 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.
*/
void
usal_vsetup(SCSI *usalp)
{
usal_vhead(usalp);
usal_vtail(usalp);
}
/*
* Return the residual DMA count for last command.
* If this count is < 0, then a DMA overrun occured.
*/
int
usal_getresid(SCSI *usalp)
{
return (usalp->scmd->resid);
}
/*
* Return the actual DMA count for last command.
*/
int
usal_getdmacnt(SCSI *usalp)
{
register struct usal_cmd *scmd = usalp->scmd;
if (scmd->resid < 0)
return (scmd->size);
return (scmd->size - scmd->resid);
}
/*
* Test if last SCSI command got an error.
*/
BOOL
usal_cmd_err(SCSI *usalp)
{
register struct usal_cmd *cp = usalp->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.
*/
void
usal_printerr(SCSI *usalp)
{
usal_fprinterr(usalp, (FILE *)usalp->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.
*/
void
usal_fprinterr(SCSI *usalp, FILE *f)
{
char errbuf[SCSI_ERRSTR_SIZE];
int amt;
amt = usal_sprinterr(usalp, 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.
*/
int
usal_sprinterr(SCSI *usalp, char *buf, int maxcnt)
{
int amt;
int osilent = usalp->silent;
int overbose = usalp->verbose;
usalp->silent = 0;
usalp->verbose = 0;
amt = usal_svtail(usalp, NULL, buf, maxcnt);
usalp->silent = osilent;
usalp->verbose = overbose;
return (amt);
}
/*
* print the following SCSI codes:
*
* - command transport status
* - CDB
* - SCSI status byte
* - Sense Bytes
* - Decoded Sense data
*
* into a buffer.
*/
int
usal__sprinterr(SCSI *usalp, char *buf, int maxcnt)
{
register struct usal_cmd *cp = usalp->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 : snprintf(errbuf, sizeof (errbuf),
"cmd timeout after %ld.%03ld (%d) s",
(long)usalp->cmdstop->tv_sec,
(long)usalp->cmdstop->tv_usec/1000,
cp->timeout);
err = errbuf;
break;
default: snprintf(errbuf, sizeof (errbuf),
"error: %d", cp->error);
err = errbuf;
}
if (usalp->cmdname != NULL && usalp->cmdname[0] != '\0')
cmdname = usalp->cmdname;
/*amt = serrmsgno(cp->ux_errno, p, maxcnt, "%s: scsi sendcmd: %s\n", cmdname, err);
if (amt < 0)
return (amt);
*/
amt=snprintf(p, maxcnt, "Errno: %d (%s), %s scsi sendcmd: %s\n", cp->ux_errno, strerror(cp->ux_errno), cmdname, err);
if(amt>=maxcnt || amt<0)
return (amt);
p += amt;
maxcnt -= amt;
amt = usal_sprintcdb(usalp, p, maxcnt);
if (amt < 0)
return (amt);
p += amt;
maxcnt -= amt;
if (cp->error <= SCG_RETRYABLE) {
amt = usal_sprintstatus(usalp, p, maxcnt);
if (amt < 0)
return (amt);
p += amt;
maxcnt -= amt;
}
if (cp->scb.chk) {
amt = usal_sprsense(p, maxcnt, (Uchar *)&cp->sense, cp->sense_count);
if (amt < 0)
return (amt);
p += amt;
maxcnt -= amt;
amt = usal__errmsg(usalp, 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.
*/
void
usal_printcdb(SCSI *usalp)
{
usal_prbytes("CDB: ", usalp->scmd->cdb.cmd_cdb, usalp->scmd->cdb_len);
}
/*
* print the SCSI Command descriptor block into a buffer.
*/
int
usal_sprintcdb(SCSI *usalp, char *buf, int maxcnt)
{
int cnt;
cnt = usal_sprbytes(buf, maxcnt, "CDB: ", usalp->scmd->cdb.cmd_cdb, usalp->scmd->cdb_len);
if (cnt < 0)
cnt = 0;
return (cnt);
}
/*
* XXX Do we need this function?
* XXX usal_printrdata() is used.
* XXX We need to check if we should write to stderr or better to usal->errfile
*
* print the SCSI send data to stderr.
*/
void
usal_printwdata(SCSI *usalp)
{
register struct usal_cmd *scmd = usalp->scmd;
if (scmd->size > 0 && (scmd->flags & SCG_RECV_DATA) == 0) {
fprintf(stderr, "Sending %d (0x%X) bytes of data.\n",
scmd->size, scmd->size);
usal_prbytes("Write Data: ",
(Uchar *)scmd->addr,
scmd->size > 100 ? 100 : scmd->size);
}
}
/*
* print the SCSI send data into a buffer.
*/
int
usal_sprintwdata(SCSI *usalp, char *buf, int maxcnt)
{
register struct usal_cmd *scmd = usalp->scmd;
register char *p = buf;
register int amt;
if (scmd->size > 0 && (scmd->flags & SCG_RECV_DATA) == 0) {
amt = 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 = usal_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 usal->errfile
*
* print the SCSI received data to stderr.
*/
void
usal_printrdata(SCSI *usalp)
{
register struct usal_cmd *scmd = usalp->scmd;
register int trcnt = usal_getdmacnt(usalp);
if (scmd->size > 0 && (scmd->flags & SCG_RECV_DATA) != 0) {
fprintf(stderr, "Got %d (0x%X), expecting %d (0x%X) bytes of data.\n",
trcnt, trcnt,
scmd->size, scmd->size);
usal_prbytes("Received Data: ",
(Uchar *)scmd->addr,
trcnt > 100 ? 100 : trcnt);
}
}
/*
* print the SCSI received data into a buffer.
*/
int
usal_sprintrdata(SCSI *usalp, char *buf, int maxcnt)
{
register struct usal_cmd *scmd = usalp->scmd;
register char *p = buf;
register int amt;
register int trcnt = usal_getdmacnt(usalp);
if (scmd->size > 0 && (scmd->flags & SCG_RECV_DATA) != 0) {
amt = 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 = usal_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 usal->errfile
*
* print the SCSI timings and (depending on verbose) received data to stderr.
*/
void
usal_printresult(SCSI *usalp)
{
fprintf(stderr, "cmd finished after %ld.%03lds timeout %ds\n",
(long)usalp->cmdstop->tv_sec,
(long)usalp->cmdstop->tv_usec/1000,
usalp->scmd->timeout);
if (usalp->verbose > 1)
usal_printrdata(usalp);
flush();
}
/*
* print the SCSI timings and (depending on verbose) received data into a buffer.
*/
int
usal_sprintresult(SCSI *usalp, char *buf, int maxcnt)
{
register char *p = buf;
register int amt;
amt = snprintf(p, maxcnt,
"cmd finished after %ld.%03lds timeout %ds\n",
(long)usalp->cmdstop->tv_sec,
(long)usalp->cmdstop->tv_usec/1000,
usalp->scmd->timeout);
if (amt < 0)
return (amt);
p += amt;
maxcnt -= amt;
if (usalp->verbose > 1) {
amt = usal_sprintrdata(usalp, 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.
*/
void
usal_printstatus(SCSI *usalp)
{
char errbuf[SCSI_ERRSTR_SIZE];
int amt;
amt = usal_sprintstatus(usalp, errbuf, sizeof (errbuf));
if (amt > 0) {
filewrite((FILE *)usalp->errfile, errbuf, amt);
fflush((FILE *)usalp->errfile);
}
}
/*
* print the SCSI status byte in human readable form into a buffer.
*/
int
usal_sprintstatus(SCSI *usalp, char *buf, int maxcnt)
{
register struct usal_cmd *cp = usalp->scmd;
char *err;
char *err2 = "";
register char *p = buf;
register int amt;
amt = 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 = snprintf(p, maxcnt, "0x%x ", ((Uchar *)&cp->scb)[1]);
if (amt < 0)
return (amt);
p += amt;
maxcnt -= amt;
}
if (cp->scb.ext_st2) {
amt = 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 = 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.
*/
void
usal_fprbytes(FILE *f, char *s, register Uchar *cp, register int n)
{
fprintf(f, "%s", s);
while (--n >= 0)
fprintf(f, " %02X", *cp++);
fprintf(f, "\n");
}
/*
* print some bytes in ascii to a file.
*/
void
usal_fprascii(FILE *f, char *s, register Uchar *cp, register int n)
{
register int c;
fprintf(f, "%s", s);
while (--n >= 0) {
c = *cp++;
if (c >= ' ' && c < 0177)
fprintf(f, "%c", c);
else
fprintf(f, ".");
}
fprintf(f, "\n");
}
/*
* XXX We need to check if we should write to stderr or better to usal->errfile
*
* print some bytes in hex to stderr.
*/
void
usal_prbytes(char *s, register Uchar *cp, register int n)
{
usal_fprbytes(stderr, s, cp, n);
}
/*
* XXX We need to check if we should write to stderr or better to usal->errfile
*
* print some bytes in ascii to stderr.
*/
void
usal_prascii(char *s, register Uchar *cp, register int n)
{
usal_fprascii(stderr, s, cp, n);
}
/*
* print some bytes in hex into a buffer.
*/
int
usal_sprbytes(char *buf, int maxcnt, char *s, register Uchar *cp, register int n)
{
register char *p = buf;
register int amt;
amt = snprintf(p, maxcnt, "%s", s);
if (amt < 0)
return (amt);
p += amt;
maxcnt -= amt;
while (--n >= 0) {
amt = snprintf(p, maxcnt, " %02X", *cp++);
if (amt < 0)
return (amt);
p += amt;
maxcnt -= amt;
}
amt = snprintf(p, maxcnt, "\n");
if (amt < 0)
return (amt);
p += amt;
return (p - buf);
}
/*
* print some bytes in ascii into a buffer.
*/
int
usal_sprascii(char *buf, int maxcnt, char *s, register Uchar *cp, register int n)
{
register char *p = buf;
register int amt;
register int c;
amt = snprintf(p, maxcnt, "%s", s);
if (amt < 0)
return (amt);
p += amt;
maxcnt -= amt;
while (--n >= 0) {
c = *cp++;
if (c >= ' ' && c < 0177)
amt = snprintf(p, maxcnt, "%c", c);
else
amt = snprintf(p, maxcnt, ".");
if (amt < 0)
return (amt);
p += amt;
maxcnt -= amt;
}
amt = 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.
*/
void
usal_fprsense(FILE *f, Uchar *cp, int n)
{
usal_fprbytes(f, "Sense Bytes:", cp, n);
}
/*
* XXX We need to check if we should write to stderr or better to usal->errfile
*
* print the SCSI sense data for last command in hex to stderr.
*/
void
usal_prsense(Uchar *cp, int n)
{
usal_fprsense(stderr, cp, n);
}
/*
* print the SCSI sense data for last command in hex into a buffer.
*/
int
usal_sprsense(char *buf, int maxcnt, Uchar *cp, int n)
{
return (usal_sprbytes(buf, maxcnt, "Sense Bytes:", cp, n));
}
/*
* Return the SCSI status byte for last command.
*/
int
usal_cmd_status(SCSI *usalp)
{
struct usal_cmd *cp = usalp->scmd;
int cmdstatus = *(Uchar *)&cp->scb;
return (cmdstatus);
}
/*
* Return the SCSI sense key for last command.
*/
int
usal_sense_key(SCSI *usalp)
{
register struct usal_cmd *cp = usalp->scmd;
int key = -1;
if (!usal_cmd_err(usalp))
return (0);
if (cp->sense.code >= 0x70)
key = ((struct scsi_ext_sense *)&(cp->sense))->key;
return (key);
}
/*
* Return all the SCSI sense table last command.
*/
unsigned char *
usal_sense_table(SCSI *usalp)
{
register struct usal_cmd *cp = usalp->scmd;
if(!usal_cmd_err(usalp))
return (0);
/* if (cp->sense.code >= 0x70) */
return (char *) &(cp->sense);
}
/*
* Return the SCSI sense code for last command.
*/
int
usal_sense_code(SCSI *usalp)
{
register struct usal_cmd *cp = usalp->scmd;
int code = -1;
if (!usal_cmd_err(usalp))
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.
*/
int
usal_sense_qual(SCSI *usalp)
{
register struct usal_cmd *cp = usalp->scmd;
if (!usal_cmd_err(usalp))
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.
*/
void
usal_fprintdev(FILE *f, struct scsi_inquiry *ip)
{
if (ip->removable)
fprintf(f, "Removable ");
if (ip->data_format >= 2) {
switch (ip->qualifier) {
case INQ_DEV_PRESENT:
break;
case INQ_DEV_NOTPR:
fprintf(f, "not present ");
break;
case INQ_DEV_RES:
fprintf(f, "reserved ");
break;
case INQ_DEV_NOTSUP:
if (ip->type == INQ_NODEV) {
fprintf(f, "unsupported\n"); return;
}
fprintf(f, "unsupported ");
break;
default:
fprintf(f, "vendor specific %d ",
(int)ip->qualifier);
}
}
switch (ip->type) {
case INQ_DASD:
fprintf(f, "Disk"); break;
case INQ_SEQD:
fprintf(f, "Tape"); break;
case INQ_PRTD:
fprintf(f, "Printer"); break;
case INQ_PROCD:
fprintf(f, "Processor"); break;
case INQ_WORM:
fprintf(f, "WORM"); break;
case INQ_ROMD:
fprintf(f, "CD-ROM"); break;
case INQ_SCAN:
fprintf(f, "Scanner"); break;
case INQ_OMEM:
fprintf(f, "Optical Storage"); break;
case INQ_JUKE:
fprintf(f, "Juke Box"); break;
case INQ_COMM:
fprintf(f, "Communication"); break;
case INQ_IT8_1:
fprintf(f, "IT8 1"); break;
case INQ_IT8_2:
fprintf(f, "IT8 2"); break;
case INQ_STARR:
fprintf(f, "Storage array"); break;
case INQ_ENCL:
fprintf(f, "Enclosure services"); break;
case INQ_SDAD:
fprintf(f, "Simple direct access"); break;
case INQ_OCRW:
fprintf(f, "Optical card r/w"); break;
case INQ_BRIDGE:
fprintf(f, "Bridging expander"); break;
case INQ_OSD:
fprintf(f, "Object based storage"); break;
case INQ_ADC:
fprintf(f, "Automation/Drive Interface"); break;
case INQ_WELLKNOWN:
fprintf(f, "Well known lun"); break;
case INQ_NODEV:
if (ip->data_format >= 2) {
fprintf(f, "unknown/no device");
break;
} else if (ip->qualifier == INQ_DEV_NOTSUP) {
fprintf(f, "unit not present");
break;
}
default:
fprintf(f, "unknown device type 0x%x",
(int)ip->type);
}
fprintf(f, "\n");
}
/*
* Print the device type from the SCSI inquiry buffer to stdout.
*/
void
usal_printdev(struct scsi_inquiry *ip)
{
usal_fprintdev(stdout, ip);
}
/*
* print into the SCSI error buffer, adjust the next write pointer.
*/
/* VARARGS2 */
int
usal_printf(SCSI *usalp, const char *form, ...)
{
int cnt;
va_list args;
va_start(args, form);
cnt = vsnprintf(usalp->errptr, usal_errrsize(usalp), form, args);
va_end(args);
if (cnt < 0) {
usalp->errptr[0] = '\0';
} else {
usalp->errptr += cnt;
}
return (cnt);
}
/*
* Flush the SCSI error buffer to SCSI errfile.
* Clear error buffer after flushing.
*/
int
usal_errflush(SCSI *usalp)
{
if (usalp->errfile == NULL)
return (0);
return (usal_errfflush(usalp, (FILE *)usalp->errfile));
}
/*
* Flush the SCSI error buffer to a file.
* Clear error buffer after flushing.
*/
int
usal_errfflush(SCSI *usalp, FILE *f)
{
int cnt;
cnt = usalp->errptr - usalp->errbeg;
if (cnt > 0) {
filewrite(f, usalp->errbeg, cnt);
fflush(f);
usalp->errbeg = usalp->errptr;
}
return (cnt);
}
syntax highlighted by Code2HTML, v. 0.9.1