/*
 *   libdi - CD Audio Player Device Interface Library
 *
 *   Copyright (C) 1993-1996  Ti Kan
 *   E-mail: ti@amb.org
 *
 *   This library is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU Library General Public
 *   License as published by the Free Software Foundation; either
 *   version 2 of the License, or (at your option) any later version.
 *
 *   This library 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
 *   Library General Public License for more details.
 *
 *   You should have received a copy of the GNU Library General Public
 *   License along with this library; if not, write to the Free
 *   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*
 *   Digital OpenVMS support
 *
 *   Contributing author: Rick Jones
 *   E-Mail: rjones@zko.dec.com
 *
 *   This software fragment contains code that interfaces the CD player
 *   application to the Digital OpenVMS operating system.
 *   The terms Digital and OpenVMS are used here for identification
 *   purposes only.
 */
#ifndef LINT
static char *_os_vms_c_ident_ = "@(#)os_vms.c	6.10 96/04/05";
#endif

#include "common_d/appenv.h"
#include "common_d/util.h"
#include "libdi_d/libdi.h"
#include "libdi_d/scsipt.h"

#if defined(__VMS) && defined(DI_SCSIPT) && !defined(DEMO_ONLY)

extern appdata_t	app_data;
extern bool_t		scsipt_notrom_error;
extern FILE		*errfp;

globalvalue
	IO$_DIAGNOSE;

STATIC int	pthru_chan;	/* Pass-through device channel */
STATIC int	context;	
STATIC char	*str;


/*
 * pthru_send
 *	Build SCSI send command to the device.
 *
 * Args:
 *	opcode - SCSI command opcode
 *	addr - The "address" portion of the SCSI CDB
 *	buf - Pointer to data buffer
 *	size - Number of bytes to transfer
 *	rsvd - The "reserved" portion of the SCSI CDB
 *	length - The "length" portion of the SCSI CDB
 *	param - The "param" portion of the SCSI CDB
 *	control - The "control" portion of the SCSI CDB
 *	rw - Data transfer direction flag (READ_OP or WRITE_OP)
 *	prnerr - Whether an error message should be displayed
 *		 when a command fails
 *
 * Return:
 *	TRUE - command completed successfully
 *	FALSE - command failed
 */
bool_t
pthru_send(
	byte_t		opcode,
	word32_t	addr,
	byte_t		*buf,
	word32_t	size,
	byte_t		rsvd,
	word32_t	length,
	byte_t		param,
	byte_t		control,
	byte_t		rw,
	bool_t		prnerr
)
{
	int		ret,
			pthru_iosb[2],		/* Pass-through iosb */
			pthru_desc[15];		/* Pass-through command block */

	byte_t		cdb[12];		/* CDB data */
	char 		scsi_status;

	if (pthru_chan <= 0 || scsipt_notrom_error)
		return FALSE;

	/* set up SCSI CDB */
	switch (opcode & 0xf0) {
	case 0xa0:
	case 0xe0:
		/* 12-byte commands */
		cdb[0] = opcode;
		cdb[1] = param;
		cdb[2] = (addr >> 24) & 0xff;
		cdb[3] = (addr >> 16) & 0xff;
		cdb[4] = (addr >> 8) & 0xff;
		cdb[5] = (addr & 0xff);
		cdb[6] = (length >> 24) & 0xff;
		cdb[7] = (length >> 16) & 0xff;
		cdb[8] = (length >> 8) & 0xff;
		cdb[9] = length & 0xff;
		cdb[10] = rsvd;
		cdb[11] = control;

		pthru_desc[3] = 12;
		break;

	case 0xc0:
	case 0xd0:
	case 0x20:
	case 0x30:
	case 0x40:
		/* 10-byte commands */
		cdb[0] = opcode;
		cdb[1] = param;
		cdb[2] = (addr >> 24) & 0xff;
		cdb[3] = (addr >> 16) & 0xff;
		cdb[4] = (addr >> 8) & 0xff;
		cdb[5] = addr & 0xff;
		cdb[6] = rsvd;
		cdb[7] = (length >> 8) & 0xff;
		cdb[8] = length & 0xff;
		cdb[9] = control;

		pthru_desc[3] = 10;
		break;

	case 0x00:
	case 0x10:
		/* 6-byte commands */
		cdb[0] = opcode;
		cdb[1] = param;
		cdb[2] = (addr >> 8) & 0xff;
		cdb[3] = addr & 0xff;
		cdb[4] = length & 0xff;
		cdb[5] = control;

		pthru_desc[3] = 6;
		break;

	default:
		if (app_data.scsierr_msg && prnerr)
			fprintf(errfp, "0x%02x: Unknown SCSI opcode\n",
				opcode);
		return FALSE;
	}

	/* build command desc. for driver call */

	pthru_desc[0] = 1;	/* Opcode */
	pthru_desc[1] = (rw ? 0 : 1) | FLAGS_DISCONNECT;	/* Flags */
	pthru_desc[2] = cdb;	/* Command address */

	pthru_desc[4] = buf;	/* Data address */
	pthru_desc[5] = size;	/* Data length */
	pthru_desc[6] = 0;	/* Pad length */
	pthru_desc[7] = 0;	/* Phase timeout */
	pthru_desc[8] = 10;	/* Disconnect timeout */
	pthru_desc[9] = 0;
	pthru_desc[10] = 0;
	pthru_desc[11] = 0;
	pthru_desc[12] = 0;
	pthru_desc[13] = 0;
	pthru_desc[14] = 0;

	DBGDUMP("SCSI CDB bytes", cdb, pthru_desc[3]);

	/* Send the command to the driver */
	ret = sys$qiow(1, pthru_chan, IO$_DIAGNOSE, pthru_iosb, 0, 0,
		       &pthru_desc[0], 15*4, 0, 0, 0, 0);

	if (!(ret & 1))
		sys$exit(ret);

	if (!(pthru_iosb[0] & 1))
		sys$exit(pthru_iosb[0] & 0xffff);

	scsi_status = (pthru_iosb[1] >> 24) & SCSI_STATUS_MASK;

	if (scsi_status != GOOD_SCSI_STATUS) {
		if (app_data.scsierr_msg && prnerr) {
			fprintf(errfp, "CD audio: %s %s:\n%s=0x%x %s=0x%x\n",
				"SCSI command fault on",
				app_data.device,
				"Opcode",
				opcode,
				"Status",
				scsi_status);
		}
		return FALSE;
	}	

	return TRUE;
}


/*
 * pthru_open
 *	Open SCSI pass-through device
 *
 * Args:
 *	path - device path name string
 *
 * Return:
 *	TRUE - open successful
 *	FALSE - open failed
 */
bool_t
pthru_open(char *path)
{

	int	i,
		status,
		pthru_desc[2];	/* Pass-through string desc */
	bool_t	ret;

	if (path == NULL) {
		fprintf(errfp, "CD Audio: CD-ROM Device not defined.\n");
		sys$exit(SS$_NODEVAVL);
	}

	pthru_desc[0] = strlen(path);
	pthru_desc[1] = path;

	status = sys$assign(&pthru_desc[0], &pthru_chan, 0, 0);

	if (!(status & 1)) {
		fprintf(errfp, "CD Audio: Error assigning to device %s\n",
			path);
		sys$exit(status);
	}

	/* Hack:  The driver allows the assign to succeed even if
	 * there is no CD loaded.  We test for the existence of a disc
	 * with scsipt_tst_unit_rdy().
	 */
	for (i = 0; i < 3; i++) {
		if ((ret = scsipt_tst_unit_rdy()) == TRUE)
			break;
	}
	if (!ret) {
		/* No CD loaded */
		pthru_close();
		return FALSE;
	}

	return TRUE;
}


/*
 * pthru_close
 *	Close SCSI pass-through device
 *
 * Args:
 *	Nothing.
 *
 * Return:
 *	Nothing.
 */
void
pthru_close(void)
{
	int	ret;

	if (pthru_chan > 0) {
		ret = sys$dassgn(pthru_chan);
		if (!(ret & 1)) {
			fprintf(errfp, "CD Audio: %s\n",
				"Cannot deassign channel from device");
		}
		pthru_chan = 0;
	}
}


/*
 * pthru_vers
 *	Return OS Interface Module version string
 *
 * Args:
 *	Nothing.
 *
 * Return:
 *	Module version text string.
 */
char *
pthru_vers(void)
{
	return ("OS Interface module for Digital OpenVMS\n");
}

#endif	/* __VMS DI_SCSIPT DEMO_ONLY */

