/*
 * Copyright (c) 1998,1999,2000
 *	Traakan, Inc., Los Altos, CA
 *	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 unmodified, 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 AUTHOR OR CONTRIBUTORS 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.
 */

/*
 * Project:  NDMJOB
 * Ident:    $Id: $
 *
 * Description:
 *
 */


#include "smc_priv.h"
#include "scsiconst.h"


int
smc_scsi_xa (struct smc_ctrl_block *smc)
{
	int		try = 0;
	int		rc;
	int		sense_key;
	unsigned char *	sense_data = smc->scsi_req.sense_data;

	for (try = 0; try < 2; try++) {
		rc = (*smc->issue_scsi_req)(smc);
		if (rc || smc->scsi_req.completion_status != SMCSR_CS_GOOD) {
			strcpy (smc->errmsg, "SCSI request failed");
			if (rc == 0) rc = -1;
			continue;	/* retry */
		}

		switch (SCSI_STATUS_BYTE_CODE(smc->scsi_req.status_byte)) {
		case SCSI_STATUS_GOOD:
			return 0;

		case SCSI_STATUS_CHECK_CONDITION:
			/* sense data processed below */
			break;

		default:
			strcpy (smc->errmsg, "SCSI unexpected status");
			return -1;
		}

		sense_key = sense_data[2] & SCSI_SENSE_SENSE_KEY_MASK;

		if (sense_key == SCSI_SENSE_KEY_UNIT_ATTENTION) {
			int	valid;
			int	asc, ascq, asq, cmd;
			long	info;

			valid = sense_data[0] & SCSI_SENSE_VALID_BIT;
			info = SMC_GET4(&sense_data[3]);
			asc = sense_data[12];
			ascq = sense_data[13];
			asq = _ASQ(asc,ascq);
			cmd = smc->scsi_req.cmd[0];

			sprintf (smc->errmsg,
				"SCSI attn s0=%x asq=%x,%x cmd=%x info=%lx",
				sense_data[0],
				asc, ascq, cmd, info);

			rc = 1;
		} else {
			strcpy (smc->errmsg, "SCSI check condition");
			rc = 1;
			break;		/* don't retry, investigate */
		}
	}

	if (!rc) rc = -1;
	return rc;
}


#define SINQ_MEDIA_CHANGER		0x08

int
smc_inquire (struct smc_ctrl_block *smc)
{
	struct smc_scsi_req *	sr = &smc->scsi_req;
	unsigned char		data[128];
	int			rc;
	int			i;

	bzero (sr, sizeof *sr);
	bzero (data, sizeof data);

	sr->n_cmd = 6;
	sr->cmd[0] = SCSI_CMD_INQUIRY;
	sr->cmd[4] = sizeof data;		/* allocation length */

	sr->data = data;
	sr->n_data_avail = sizeof data;
	sr->data_dir = SMCSR_DD_IN;

	rc = smc_scsi_xa (smc);
	if (rc != 0) return rc;

	if (data[0] != SINQ_MEDIA_CHANGER) {
		strcpy (smc->errmsg, "Not a media changer");
		return -1;
	}

	for (i = 28-1; i >= 0; i--) {
		int		c = data[8+i];

		if (c != ' ')
			break;
	}

	for (; i >= 0; i--) {
		int		c = data[8+i];

		if (! (' ' <= c && c < 0x7F))
			c = '*';
		smc->ident[i] = c;
	}

	return 0;
}

int
smc_test_unit_ready (struct smc_ctrl_block *smc)
{
	struct smc_scsi_req *	sr = &smc->scsi_req;
	int			rc;

	bzero (sr, sizeof *sr);

	sr->n_cmd = 6;
	sr->cmd[0] = SCSI_CMD_TEST_UNIT_READY;

	rc = smc_scsi_xa (smc);

	return rc;
}

int
smc_get_elem_aa (struct smc_ctrl_block *smc)
{
	struct smc_scsi_req *	sr = &smc->scsi_req;
	unsigned char		data[256];
	int			rc;

	bzero (sr, sizeof *sr);
	bzero (data, sizeof data);
	bzero (&smc->elem_aa, sizeof smc->elem_aa);
	smc->valid_elem_aa = 0;

	sr->n_cmd = 6;
	sr->cmd[0] = SCSI_CMD_MODE_SENSE_6;
	sr->cmd[1] = 0x08;			/* DBD */
	sr->cmd[2] = 0x1D;			/* current elem addrs */
	sr->cmd[3] = 0;				/* reserved */
	sr->cmd[4] = 255;			/* allocation length */
	sr->cmd[5] = 0;				/* reserved */

	sr->data = data;
	sr->n_data_avail = 255;
	sr->data_dir = SMCSR_DD_IN;

	rc = smc_scsi_xa (smc);
	if (rc != 0) return rc;

	if (data[0] < 18) {
		strcpy (smc->errmsg, "short sense data");
		return -1;
	}


	rc = smc_parse_element_address_assignment ((void*)&data[4],
					&smc->elem_aa);
	if (rc) {
		strcpy (smc->errmsg, "elem_addr_assignment format error");
		return -1;
	}

	smc->valid_elem_aa = 1;

	return 0;
}

/*
 * 17.2.2 INITIALIZE ELEMENT STATUS command
 *
 * The INITIALIZE ELEMENT STATUS command (see table 329) will cause the
 * medium changer to check all elements for medium and any other status
 * relevant to that element. The intent of this command is to enable the
 * initiator to get a quick response from a following READ ELEMENT STATUS
 * command. It may be useful to issue this command after a power failure,
 * or if medium has been changed by an operator, or if configurations have
 * been changed.
 *
 *                 Table 329 - INITIALIZE ELEMENT STATUS command
 * +====-=======-========-========-========-========-========-========-======+
 * | Bit|  7    |   6    |   5    |   4    |   3    |   2    |   1    |   0  |
 * |Byte|       |        |        |        |        |        |        |      |
 * |====+====================================================================|
 * | 0  |                          Operation code (07h)                      |
 * |----+--------------------------------------------------------------------|
 * | 1  |Logical unit number      |                Reserved                  |
 * |----+--------------------------------------------------------------------|
 * | 2  |                          Reserved                                  |
 * |----+--------------------------------------------------------------------|
 * | 3  |                          Reserved                                  |
 * |----+--------------------------------------------------------------------|
 * | 4  |                          Reserved                                  |
 * |----+--------------------------------------------------------------------|
 * | 5  |                          Control                                   |
 * +=========================================================================+
 */


int
smc_init_elem_status (struct smc_ctrl_block *smc)
{
	struct smc_scsi_req *	sr = &smc->scsi_req;
	int			rc;

	bzero (sr, sizeof *sr);

	sr->n_cmd = 6;
	sr->cmd[0] = SCSI_CMD_INITIALIZE_ELEMENT_STATUS;

	sr->data_dir = SMCSR_DD_NONE;

	rc = smc_scsi_xa (smc);
	if (rc != 0) return rc;

	return 0;
}



/*
 * 17.2.5 READ ELEMENT STATUS command
 *
 * The READ ELEMENT STATUS command (see table 332) requests that the
 * target report the status of its internal elements to the initiator.
 *
 *                    Table 332 - READ ELEMENT STATUS command
 * +====-=======-========-========-========-========-========-========-=======+
 * | Bit|  7    |   6    |   5    |   4    |   3    |   2    |   1    |   0   |
 * |Byte|       |        |        |        |        |        |        |       |
 * |====+=====================================================================|
 * | 0  |                          Operation code (B8h)                       |
 * |----+---------------------------------------------------------------------|
 * | 1  |Logical unit number      | VolTag |        Element type code         |
 * |----+---------------------------------------------------------------------|
 * | 2  |(MSB)                                                                |
 * |----+--                        Starting element address                 --|
 * | 3  |                                                                (LSB)|
 * |----+---------------------------------------------------------------------|
 * | 4  |(MSB)                                                                |
 * |----+--                        Number of elements                       --|
 * | 5  |                                                                (LSB)|
 * |----+---------------------------------------------------------------------|
 * | 6  |                          Reserved                                   |
 * |----+---------------------------------------------------------------------|
 * | 7  |(MSB)                                                                |
 * |----+--                                                                 --|
 * | 8  |                          Allocation length                          |
 * |----+--                                                                 --|
 * | 9  |                                                                (LSB)|
 * |----+---------------------------------------------------------------------|
 * |10  |                          Reserved                                   |
 * |----+---------------------------------------------------------------------|
 * |11  |                          Control                                    |
 * +==========================================================================+
 *
 *
 * A volume tag (VolTag) bit of one indicates that the target shall report
 * volume tag information if this feature is supported. A value of zero
 * indicates that volume tag information shall not be reported. If the
 * volume tag feature is not supported this field shall be treated as
 * reserved.
 *
 * The element type code field specifies the particular element type(s)
 * selected for reporting by this command.  A value of zero specifies that
 * status for all element types shall be reported.  The element type codes
 * are defined in table 333.
 *
 *                         Table 333 - Element type code
 *      +=============-===================================================+
 *      |    Code     |  Description                                      |
 *      |-------------+---------------------------------------------------|
 *      |      0h     |  All element types reported, (valid in CDB only)  |
 *      |      1h     |  Medium transport element                         |
 *      |      2h     |  Storage element                                  |
 *      |      3h     |  Import export element                            |
 *      |      4h     |  Data transfer element                            |
 *      |   5h - Fh   |  Reserved                                         |
 *      +=================================================================+
 *
 *
 * The starting element address specifies the minimum element address to
 * report. Only elements with an element type code permitted by the
 * element type code specification, and an element address greater than or
 * equal to the starting element address shall be reported. Element
 * descriptor blocks are not generated for undefined element addresses.
 *
 * The number of elements specifies the maximum number of element
 * descriptors to be created by the target for this command. The value
 * specified by this field is not the range of element addresses to be
 * considered for reporting but rather the number of defined elements to
 * report. If the allocation length is not sufficient to transfer all the
 * element descriptors, the target shall transfer all those descriptors
 * that can be completely transferred and this shall not be considered an
 * error.
 */

int
smc_read_elem_status (struct smc_ctrl_block *smc)
{
	struct smc_scsi_req *	sr = &smc->scsi_req;
	unsigned char		data[8192];
	int			rc;

  retry:
	bzero (sr, sizeof *sr);
	bzero (data, sizeof data);
	bzero (&smc->elem_desc, sizeof smc->elem_desc);
	smc->n_elem_desc = 0;
	smc->valid_elem_desc = 0;

	sr->n_cmd = 12;
	sr->cmd[0] = SCSI_CMD_READ_ELEMENT_STATUS;
	if (!smc->dont_ask_for_voltags) {
		sr->cmd[1] = 0x10;		/* VolTag, all types */
	} else {
		sr->cmd[1] = 0x00;		/* !VolTag, all types */
	}
	sr->cmd[2] = 0;				/* starting elem MSB */
	sr->cmd[3] = 0;				/* starting elem LSB */
	sr->cmd[4] = 0;				/* number of elem MSB */
	sr->cmd[5] = SMC_MAX_ELEMENT;		/* number of elem LSB */
	sr->cmd[6] = 0;				/* reserved */
	SMC_PUT3 (&sr->cmd[7], sizeof data);
	sr->cmd[10] = 0;			/* reserved */

	sr->data = data;
	sr->n_data_avail = sizeof data;
	sr->data_dir = SMCSR_DD_IN;

	rc = smc_scsi_xa (smc);
	if (rc != 0) {
		if (smc->dont_ask_for_voltags)
			return rc;
		smc->dont_ask_for_voltags = 1;
		goto retry;
	}

	rc = smc_parse_element_status_data ((void*)data, sr->n_data_done,
				smc->elem_desc, SMC_MAX_ELEMENT);
	if (rc < 0) {
		strcpy (smc->errmsg, "elem_status format error");
		return -1;
	}

	smc->n_elem_desc = rc;

	smc->valid_elem_aa = 1;

	return 0;
}


int
smc_move (struct smc_ctrl_block *smc, unsigned from_addr,
  unsigned to_addr, int invert, unsigned chs_addr)
{
	struct smc_scsi_req *	sr = &smc->scsi_req;
	int			rc;

	bzero (sr, sizeof *sr);

	sr->n_cmd = 12;
	sr->cmd[0] = SCSI_CMD_MOVE_MEDIUM;
	SMC_PUT2(&sr->cmd[2], chs_addr);
	SMC_PUT2(&sr->cmd[4], from_addr);
	SMC_PUT2(&sr->cmd[6], to_addr);
	/* TODO: invert */

	sr->data_dir = SMCSR_DD_NONE;

	rc = smc_scsi_xa (smc);
	if (rc != 0) return rc;

	return 0;
}

int
smc_position (struct smc_ctrl_block *smc, unsigned to_addr, int invert)
{
	return -1;
}


int
smc_handy_move_to_drive (struct smc_ctrl_block *smc, unsigned from_se_ix)
{
	return -1;
}

int
smc_handy_move_from_drive (struct smc_ctrl_block *smc, unsigned to_se_ix)
{
	return -1;
}
