/*
 * DATlib   Copyright (c) 1995-1996 Marcus Meissner &
 *          Friedrich-Alexander Universitaet Erlangen-Nuernberg (FAU), IMMD4
 *          All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    This product includes software developed by Marcus Meissner
 *    at the Friedrich-Alexander Universitaet Erlangen-Nuernberg (FAU), IMMD4
 *
 * 4. The name of the University or the author may not be used
 *    to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND THE AUTHOR ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE UNIVERSITY OR THE AUTHOR BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/* $Id: generic_scsi.c,v 1.2 1997/06/02 18:40:18 msmeissn Exp $
 *
 * $Log: generic_scsi.c,v $
 * Revision 1.2  1997/06/02 18:40:18  msmeissn
 * generic scsi support for SCO and FreeBSD by Arne Ludwig.
 *
 * Revision 1.1  1997/03/16 20:29:04  msmeissn
 * Initial revision
 *
 */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#if 0
#include <sys/mtio.h>
#endif
#ifdef sun
# include <sys/mtio.h> /* some defines needed by stdef.h */
# include <sys/condvar.h>
# include <sys/scsi/impl/uscsi.h>
# include <sys/scsi/generic/commands.h>
# include <sys/scsi/generic/inquiry.h>
# include <sys/scsi/targets/stdef.h>
#endif
#ifdef linux
# ifndef SCSI_IOCTL_SEND_COMMAND	
#  define SCSI_IOCTL_SEND_COMMAND	1
# endif
#endif

#include "generic_scsi.h"

static int
rq_sense_out(unsigned char* rqbuf) {
        fprintf(stderr,"Request Sense reports:\n");
        if ((rqbuf[0]&0x7f)!=0x70) {
                fprintf(stderr,"\tInvalid header.\n");
                return -1;
        }
        fprintf(stderr,"\tCurrent command read filemark: %s\n",(rqbuf[2]&0x80)?"yes":"no");
        fprintf(stderr,"\tEarly warning passed: %s\n",(rqbuf[2]&0x40)?"yes":"no");
        fprintf(stderr,"\tIncorrect blocklengt: %s\n",(rqbuf[2]&0x20)?"yes":"no");
        fprintf(stderr,"\tSense Key: %d\n",rqbuf[2]&0xf);
        if (rqbuf[0]&0x80)
                fprintf(stderr,"\tResidual Length: %d\n",rqbuf[3]*0x1000000+rqbuf[4]*0x10000+rqbuf[5]*0x100+rqbuf[6]);
        fprintf(stderr,"\tAdditional Sense Length: %d\n",rqbuf[7]);
        fprintf(stderr,"\tAdditional Sense Code: %d\n",rqbuf[12]);
        fprintf(stderr,"\tAdditional Sense Code Qualifier: %d\n",rqbuf[13]);
        if (rqbuf[15]&0x80) {
                fprintf(stderr,"\tIllegal Param is in %s\n",(rqbuf[15]&0x40)?"the CDB":"the Data Out Phase");
                if (rqbuf[15]&0x8) {
                        fprintf(stderr,"Pointer at %d, bit %d\n",rqbuf[16]*256+rqbuf[17],rqbuf[15]&0x7);
                }
        }
        return 0;
}
#ifdef linux
int
send_scsi_command(	int scsifd,
			unsigned char *cmd,int cmdlen,
			unsigned char *inbuf,int inbuflen,
			unsigned char *outbuf,int outbuflen,
			unsigned char *rqbuf,int rqbuflen
) {
	unsigned char	*bufp,*buf;
	int		ret,xlen;

	if (outbuf==NULL) outbuflen=0;
	if (inbuf==NULL) inbuflen=0;
	xlen=0;
	if (inbuflen>xlen) xlen=inbuflen;
	if (outbuflen-cmdlen>xlen) xlen=outbuflen-cmdlen;
	if (rqbuflen-cmdlen>xlen) xlen=rqbuflen-cmdlen;
	bufp=buf=(unsigned char*)malloc(cmdlen+xlen+8);
	*(int*)bufp	= outbuflen;	bufp+=sizeof(int);
	*(int*)bufp	= inbuflen;	bufp+=sizeof(int);
	memcpy(bufp,cmd,cmdlen);	bufp+=cmdlen;
	if (outbuf!=NULL && outbuflen!=0)	{
		memcpy(bufp,outbuf,outbuflen);
		bufp+=outbuflen;
	}
	ret=ioctl(scsifd,SCSI_IOCTL_SEND_COMMAND,buf);
	/* quote: 
	 * the output area is filled then starting from the command 
	 * byte, --unquote
	 */
	if (ret!=-1) {
		if (inbuf!=NULL && inbuflen!=0)
			memcpy(inbuf,buf+2*sizeof(int),inbuflen);
	} else {
		perror("SCSI_IOCTL_SEND_COMMAND");
		fprintf(stderr,"cmd was 0x%02x\n",cmd[0]);
		rq_sense_out(buf+2*sizeof(int));
		if (rqbuf!=NULL && rqbuflen!=0)
			memcpy(rqbuf,buf+2*sizeof(int),rqbuflen);
	}
	free(buf);
	return ret;
}

#endif
#ifdef sun
int
send_scsi_command(	int scsifd,
			unsigned char *cmd,int cmdlen,
			unsigned char *inbuf,int inbuflen,
			unsigned char *outbuf,int outbuflen,
			unsigned char *rqbuf,int rqbuflen
) {
	struct	uscsi_cmd	ucmd;

	ucmd.uscsi_flags		= USCSI_DIAGNOSE;
	ucmd.uscsi_timeout	= 1000;
	ucmd.uscsi_cdb		= (caddr_t)cmd;
	ucmd.uscsi_cdblen	= cmdlen;
	if (inbuf) {
		ucmd.uscsi_flags|= USCSI_READ|USCSI_ISOLATE;
		ucmd.uscsi_bufaddr=(caddr_t)inbuf;
		ucmd.uscsi_buflen= inbuflen;
	} else {
		ucmd.uscsi_flags|= USCSI_WRITE;
		ucmd.uscsi_bufaddr= (caddr_t)outbuf;
		ucmd.uscsi_buflen = outbuflen;
	}
	if (rqbuf)
		ucmd.uscsi_flags|=USCSI_RQENABLE;
	ucmd.uscsi_rqbuf=(caddr_t)rqbuf;
	ucmd.uscsi_rqlen=rqbuflen;
	ucmd.uscsi_resid=0;
	return ioctl(scsifd,USCSICMD,&ucmd);
}
#endif

#if defined(sco) || defined(M_UNIX)
#include <sys/ioctl.h>
#include <sys/scsicmd.h>

int
send_scsi_command(	int scsifd,
			unsigned char *cmd,int cmdlen,
			unsigned char *inbuf,int inbuflen,
			unsigned char *outbuf,int outbuflen,
			unsigned char *rqbuf,int rqbuflen
) {
	struct  scsicmd       ucmd;
	char reqbuf[256];
	int r, r2;

	memset(&ucmd.cdb, '\0', sizeof (ucmd.cdb));

	if (cmdlen <= SCSICMDLEN)
		memcpy (ucmd.cdb, (caddr_t)cmd, cmdlen);
	else {
		memcpy(ucmd.cdb,(caddr_t)cmd,SCSICMDLEN);
		fprintf(stderr,"send_scsi_command: command too long for CDB\n");
	}

	ucmd.cdb_len       = cmdlen;
	if (inbuf) {
		ucmd.is_write = 0;
		ucmd.data_ptr=(caddr_t)inbuf;
		ucmd.data_len= inbuflen;
	} else if (outbuf) {
		ucmd.is_write = 1;
		ucmd.data_ptr= (caddr_t)outbuf;
		ucmd.data_len = outbuflen;
	} else {
		fprintf (stderr, "send_scsi_command: null buffers\n");
		return -1;
	}

	ucmd.host_sts = 0;
	ucmd.target_sts = 0;

#ifdef DEBUG
	fprintf (stderr, "SCSI %2d (", ucmd.cdb_len);
	for (r=0; r < ucmd.cdb_len; r++)
		fprintf (stderr, "%02x ", ucmd.cdb[r]);
	fprintf (stderr, ")\n");
#endif
	r = ioctl(scsifd,SCSIUSERCMD,&ucmd);

	if (r == -1) {
		fprintf (stderr, "send_scsi_command: address = %x, len = %d\n",
		ucmd.data_ptr, ucmd.data_len);

		fprintf (stderr, "send_scsi_command: failed, host=%x targ=%x\n",
		ucmd.host_sts, ucmd.target_sts);

		memset (&ucmd.cdb, '\0', sizeof (ucmd.cdb));
		ucmd.cdb[0] = 0x03; /* REQUEST SENSE */
		ucmd.cdb[1] = 0x00;
		ucmd.cdb[2] = 0x00;
		ucmd.cdb[3] = 0x00;
		ucmd.cdb[4] = 0xFF;
		ucmd.cdb[5] = 0x00;
		ucmd.cdb_len = 6;
		ucmd.is_write = 0;
		ucmd.data_ptr=(caddr_t)reqbuf;
		ucmd.data_len= sizeof(reqbuf);
		ucmd.host_sts = 0;
		ucmd.target_sts = 0;
		r2 = ioctl(scsifd,SCSIUSERCMD,&ucmd);

		if (r2 == -1)
			fprintf (stderr, "request sense failed!!!\n");

		fprintf (stderr, "REQUEST SENSE: state is %s\n",
			reqbuf[0]&127 == 0x70 ? "current" :
			reqbuf[0]&127 == 0x71 ? "deferred" :
			"unknown"
		);

		fprintf (stderr, "sense key = %x\n", reqbuf[2]&15);
		if (reqbuf[0]&0x80)
			fprintf (stderr, "info bytes = %x %x %x %x\n", reqbuf[3], reqbuf[4], reqbuf[5], reqbuf[6]);
		fprintf(stderr, "additional sense = %x %x\n", reqbuf[12], reqbuf[13]);
		fprintf(stderr, "byte 15 = %x\n", reqbuf[15]);

		if (reqbuf[15]&128 && reqbuf[17])
			fprintf(stderr,"field ptr = %x\n",reqbuf[16]*256+reqbuf[17]);
		else
			fprintf(stderr,"drive error %x\n",reqbuf[17]);

		if (reqbuf[21]&8)
			fprintf (stderr, "cleaning required\n");
	}
	if (rqbuf)
		memcpy (rqbuf, reqbuf, rqbuflen);
	return r;
} /* send_scsi_command */
#endif

#ifdef __FreeBSD__    /* doesn't look like generic BSD */
#include <sys/scsiio.h>
int
send_scsi_command(    int scsifd,
		unsigned char *cmd,int cmdlen,
		unsigned char *inbuf,int inbuflen,
		unsigned char *outbuf,int outbuflen,
		unsigned char *rqbuf,int rqbuflen
) {
	scsireq_t	ucmd;
	int		r;

	memset (&ucmd, '\0', sizeof (ucmd));

	ucmd.flags      = 0;
	ucmd.timeout    = 1000;
	memcpy (ucmd.cmd, (caddr_t)cmd, cmdlen);
	ucmd.cmdlen     = cmdlen;

	if (inbuf) {
		ucmd.flags |= SCCMD_READ;
		ucmd.databuf =(caddr_t)inbuf;
		ucmd.datalen = inbuflen;
	} else {
		ucmd.flags |= SCCMD_WRITE;
		ucmd.databuf = (caddr_t)outbuf;
		ucmd.datalen = outbuflen;
	}
	if (rqbuf)
		ucmd.flags|=0; /* FIXME: Huh? Arne? */

	r = ioctl(scsifd,SCIOCCOMMAND,&ucmd);

	if (rqbuf)
		memcpy (rqbuf, ucmd.sense, rqbuflen);
	return r;
} /* send_scsi_command */
#endif
