/*
 * array-info - Array Controllers Informations
 * Copyright (C) 2002  Benoit Gaussen (ben@trez42.net)
 * Copyright (c) 2009  Google, Inc (ragnark@google.com)
 *
 * $Log: cciss_info.c,v $
 * Revision 1.3  2009/09/18 17:40:22  ragnark
 * Add information about spare disks for cciss plugin
 * Make array-info exit with exit code 1 if it detects problems with any of the logical volumes.
 *
 * Revision 1.2  2007/02/03 10:04:05  pere
 * Convert the cciss and ida plugin to the new shared library framework.  Some code cleanup.
 *
 * Revision 1.1  2002/07/29 16:50:19  trez42
 * Add plugin support
 * Change directory structure
 *
 * Revision 1.4  2002/07/25 16:51:14  trez42
 * CVS Fixes
 *
 *
 * 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; either version 2 of the License, or
 * (at your option) any later version.
 *
 * 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; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "array_info.h"
#include "array_plugin.h"
#include "cciss_info.h"
#include "compaq_info.h"

static cciss_ctrl_data_t cciss_ctrl_data;

#include "xmalloc.ic"

static int
cciss_sense_ldrv(cciss_ctrl_data_t * ctrl_data, u_int8_t ldrv)
{
	struct id_log_drv ldrv_info;
	struct sense_log_drv ldrv_status;
	int ret;

	if ((ret =
	     cciss_bmic_cmd(ctrl_data, OPCODE_BMIC_IDLOG, CCISS_CMD_READ,
			    &ldrv_info, sizeof (ldrv_info))) != CMD_SUCCESS) {
		printf
		    ("Error while getting CCISS logical drive %d informations (err=%d)...\n",
		     ldrv, ret);
		return -1;
	}

	if ((ret =
	     cciss_bmic_cmd(ctrl_data, OPCODE_BMIC_SENSELOG,
			    CCISS_CMD_READ, &ldrv_status,
			    sizeof (ldrv_status))) != CMD_SUCCESS) {
		printf
		    ("Error while getting CCISS logical drive %d status (err=%d)...\n",
		     ldrv, ret);
		return -1;
	}

	ctrl_data->ctrl.ldrvs[ldrv].log_drive = ldrv;
	ctrl_data->ctrl.ldrvs[ldrv].nr_blks = ldrv_info.nr_blks;
	ctrl_data->ctrl.ldrvs[ldrv].blk_size = ldrv_info.blk_size;
	ctrl_data->ctrl.ldrvs[ldrv].fault_tol = ldrv_info.fault_tol;
	ctrl_data->ctrl.ldrvs[ldrv].status = ldrv_status.status;
	ctrl_data->ctrl.ldrvs[ldrv].blks_to_recover =
	    ldrv_status.blks_to_recover;
	ctrl_data->ctrl.ldrvs[ldrv].spare_stat = ldrv_status.spare_stat;

	return 0;
}

static int
cciss_ctrl_req(driver_data_ptr_t data)
{
	struct id_ctrl ctrl_info;
	int ret;
	cciss_ctrl_data_t *ctrl_data = (cciss_ctrl_data_t *) data;
	if ((ret = cciss_bmic_cmd(ctrl_data, OPCODE_BMIC_IDCTRL,
				  CCISS_CMD_READ, &ctrl_info,
				  sizeof (ctrl_info))) != CMD_SUCCESS) {
		printf
		    ("Error while getting CCISS controller informations (err=%d)...\n",
		     ret);
		return -1;
	}

	ctrl_data->ctrl.board_id = ctrl_info.board_id;
	memcpy(ctrl_data->ctrl.firm_rev, ctrl_info.firm_rev, 4);
	memcpy(ctrl_data->ctrl.rom_rev, ctrl_info.rom_rev, 4);
	ctrl_data->ctrl.nr_ldrvs = ctrl_info.nr_drvs;

	if (ctrl_data->query_flags == QUERY_ALL_LDRV) {
		int i;

		ctrl_data->ctrl.ldrvs =
		    xmalloc(sizeof (compaq_log_drive_t) *
			    ctrl_data->ctrl.nr_ldrvs);

		for (i = 0; i < ctrl_data->ctrl.nr_ldrvs; i++)
			cciss_sense_ldrv(ctrl_data, i);
	} else {
		ctrl_data->ctrl.ldrvs = xmalloc(sizeof (compaq_log_drive_t));
		cciss_sense_ldrv(ctrl_data, ctrl_data->log_drive);
	}

	return 0;
}

static device_major_t cciss_device_majors[] = {
	{BLOCK_DEVICE, COMPAQ_CISS_MAJOR, -1},
	{BLOCK_DEVICE, COMPAQ_CISS_MAJOR1, -1},
	{BLOCK_DEVICE, COMPAQ_CISS_MAJOR2, -1},
	{BLOCK_DEVICE, COMPAQ_CISS_MAJOR3, -1},
	{BLOCK_DEVICE, COMPAQ_CISS_MAJOR4, -1},
	{BLOCK_DEVICE, COMPAQ_CISS_MAJOR5, -1},
	{BLOCK_DEVICE, COMPAQ_CISS_MAJOR6, -1},
	{BLOCK_DEVICE, COMPAQ_CISS_MAJOR7, -1},
	{0, 0, 0},
};

static driver_data_ptr_t
cciss_ctrl_open(array_data_t * array_data)
{
	cciss_ctrl_data.fd = array_data->fd;
	cciss_ctrl_data.log_drive = array_data->device_major.minor / 16;
	cciss_ctrl_data.query_flags = array_data->query_flags;

	return &cciss_ctrl_data;
}

static int
cciss_ctrl_close(driver_data_ptr_t data)
{
	cciss_ctrl_data_t *ctrl_data = (cciss_ctrl_data_t *) data;
	return 0;
}

static char *
cciss_description(void)
{
	static char description[35];

	snprintf(description, 35, "CCISS Linux driver plugin v%d.%d.%d",
		 CCISS_INFO_VERSION >> 16 & 0xff,
		 CCISS_INFO_VERSION >> 8 & 0xff, CCISS_INFO_VERSION & 0xff);
	return description;
}

static void
cciss_ctrl_free(driver_data_ptr_t data)
{
	cciss_ctrl_data_t *ctrl_data = (cciss_ctrl_data_t *) data;
	if (ctrl_data->ctrl.ldrvs) {
		free(ctrl_data->ctrl.ldrvs);
		ctrl_data->ctrl.ldrvs = NULL;
	}
}

static array_infos_t *
cciss_ctrl_infos(driver_data_ptr_t data, array_plugin_callbacks_t * callbacks,
		 int * errors)
{
	cciss_ctrl_data_t *ctrl_data = (cciss_ctrl_data_t *) data;
	compaq_ctrl_t *ctrl = &(ctrl_data->ctrl);
	return compaq_ctrl_infos(ctrl, callbacks, ctrl_data->query_flags,
				 ctrl_data->log_drive, errors);
}

array_plugin_t array_plugin = {
	.device_majors = cciss_device_majors,
	.plugin_version = CCISS_INFO_VERSION,
	.array_info_version = ARRAY_INFO_VERSION,
	.plugin_description = cciss_description,
	.ctrl_open = cciss_ctrl_open,
	.ctrl_close = cciss_ctrl_close,
	.ctrl_req = cciss_ctrl_req,
	.ctrl_infos = cciss_ctrl_infos,
	.ctrl_free = cciss_ctrl_free
};
