/*
 *  scst_raid.c
 *
 *  Copyright (C) 2004 - 2018 Vladislav Bolkhovitin <vst@vlnb.net>
 *  Copyright (C) 2004 - 2005 Leonid Stoljar
 *  Copyright (C) 2007 - 2018 Western Digital Corporation
 *
 *  SCSI raid(controller) (type 0xC) dev handler
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation, version 2
 *  of the License.
 *
 *  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.
 */

#define LOG_PREFIX      "dev_raid"

#include <scsi/scsi_host.h>
#include <linux/slab.h>

#ifdef INSIDE_KERNEL_TREE
#include <scst/scst.h>
#else
#include "scst.h"
#endif
#include "scst_dev_handler.h"

#define RAID_NAME	"dev_raid"

#define RAID_RETRIES		2

static int raid_attach(struct scst_device *);
/* static void raid_detach(struct scst_device *); */
static int raid_parse(struct scst_cmd *);
/* static int raid_done(struct scst_cmd *); */

static struct scst_dev_type raid_devtype = {
	.name =			RAID_NAME,
	.type =			TYPE_RAID,
	.threads_num =		1,
	.parse_atomic =		1,
/*	.dev_done_atomic =	1,*/
	.attach =		raid_attach,
/*	.detach =		raid_detach,*/
	.parse =		raid_parse,
/*	.dev_done =		raid_done,*/
#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
	.default_trace_flags =	SCST_DEFAULT_DEV_LOG_FLAGS,
	.trace_flags =		&trace_flag,
#endif
};

static int raid_attach(struct scst_device *dev)
{
	int res, rc;
	int retries;

	TRACE_ENTRY();

	if (dev->scsi_dev == NULL ||
	    dev->scsi_dev->type != dev->type) {
		PRINT_ERROR("%s", "SCSI device not define or illegal type");
		res = -ENODEV;
		goto out;
	}

	/*
	 * If the device is offline, don't try to read capacity or any
	 * of the other stuff
	 */
	if (dev->scsi_dev->sdev_state == SDEV_OFFLINE) {
		TRACE_DBG("%s", "Device is offline");
		res = -ENODEV;
		goto out;
	}

	retries = SCST_DEV_RETRIES_ON_UA;
	do {
		TRACE_DBG("%s", "Doing TEST_UNIT_READY");
		rc = scsi_test_unit_ready(dev->scsi_dev,
			SCST_GENERIC_RAID_TIMEOUT, RAID_RETRIES, NULL);
		TRACE_DBG("TEST_UNIT_READY done: %x", rc);
	} while ((--retries > 0) && rc);

	if (rc) {
		PRINT_WARNING("Unit not ready: %x", rc);
		/* Let's try not to be too smart and continue processing */
	}

	res = scst_obtain_device_parameters(dev, NULL);
	if (res != 0) {
		PRINT_ERROR("Failed to obtain control parameters for device "
			"%s", dev->virt_name);
		goto out;
	}

out:
	TRACE_EXIT();
	return res;
}

#if 0
void raid_detach(struct scst_device *dev)
{
	TRACE_ENTRY();

	TRACE_EXIT();
	return;
}
#endif

static int raid_parse(struct scst_cmd *cmd)
{
	int res = SCST_CMD_STATE_DEFAULT, rc;

	rc = scst_raid_generic_parse(cmd);
	if (rc != 0) {
		res = scst_get_cmd_abnormal_done_state(cmd);
		goto out;
	}

	cmd->retries = SCST_PASSTHROUGH_RETRIES;
out:
	return res;
}

#if 0
int raid_done(struct scst_cmd *cmd)
{
	int res = SCST_CMD_STATE_DEFAULT;

	TRACE_ENTRY();

	/*
	 * SCST sets good defaults for cmd->is_send_status and
	 * cmd->resp_data_len based on cmd->status and cmd->data_direction,
	 * therefore change them only if necessary.
	 */

#if 0
	switch (cmd->cdb[0]) {
	default:
		/* It's all good */
		break;
	}
#endif

	TRACE_EXIT();
	return res;
}
#endif

static int __init raid_init(void)
{
	int res = 0;

	TRACE_ENTRY();

	raid_devtype.module = THIS_MODULE;

	res = scst_register_dev_driver(&raid_devtype);
	if (res < 0)
		goto out;


out:
	TRACE_EXIT_RES(res);
	return res;

}

static void __exit raid_exit(void)
{
	TRACE_ENTRY();
	scst_unregister_dev_driver(&raid_devtype);
	TRACE_EXIT();
	return;
}

module_init(raid_init);
module_exit(raid_exit);

MODULE_AUTHOR("Vladislav Bolkhovitin & Leonid Stoljar");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("SCSI raid(controller) (type 0xC) dev handler for SCST");
MODULE_VERSION(SCST_VERSION_STRING);
MODULE_IMPORT_NS(SCST);
