/*
 * SNIA DDF1 v1.0 metadata format handler.
 *
 * Copyright (C) 2005-2006 IBM, All rights reserved.
 * Written by Darrick Wong <djwong@us.ibm.com>
 *
 * Copyright (C) 2006 Heinz Mauelshagen, Red Hat GmbH
 *                    All rights reserved.
 *
 * See file LICENSE at the top of this source tree for license information.
 */

#ifdef DMRAID_NATIVE_LOG

#include "internal.h"
#define FORMAT_HANDLER
#include "ddf1.h"
#include "ddf1_lib.h"

/* Print DDF GUIDs. */
#ifdef  NATIVE_LOG_OFFSET

#define DP_BUF(name, basevar, x, len) do { \
	_dp_guid(lc, name, P_OFF(x, basevar, x), len);\
} while (0)

#define DP_GUID(name, basevar, x) do {\
_dp_guid(lc, name, P_OFF(x, basevar, x), DDF1_GUID_LENGTH);\
} while (0)

static void
_dp_guid(struct lib_context *lc, const char *name,
	 unsigned int offset, void *data, unsigned int len)
{
	char *p;
	int i;

	p = data;
	log_print_nnl(lc, "0x%03x %s\"", offset, name);
	for (i = 0; i < len; i++)
		log_print_nnl(lc, "%c",
			      (isgraph(p[i]) || p[i] == ' ' ? p[i] : '.'));

	log_print_nnl(lc, "\" [");
	for (i = 0; i < len; i++)
		log_print_nnl(lc, "%s%02x", (i != 0 ? " " : ""), p[i] & 0xFF);

	log_print_nnl(lc, "]\n");
}
#else
#define DP_BUF(name, basevar, x, len)
#define DP_GUID(name, basevar, x)
#endif

/* Dump top */
static void
dump_top(struct lib_context *lc, struct dev_info *di,
	 struct ddf1 *ddf1, const char *handler)
{
	log_print(lc, "%s (%s):", di->path, handler);
	log_print(lc, "DDF1 anchor at %llu with tables in %s-endian format.",
		  ddf1->anchor_offset / DDF1_BLKSIZE,
		  (ddf1->disk_format == LITTLE_ENDIAN ? "little" : "big"));
}

/* Dump DDF tables. */
static void
dump_header(struct lib_context *lc, struct ddf1_header *dh)
{
	if (!dh)
		return;

	log_print(lc, "DDF1 Header at %p", dh);
	DP("signature:\t0x%X", dh, dh->signature);
	DP("crc:\t\t0x%X", dh, dh->crc);
	DP_GUID("guid:\t\t", dh, dh->guid);
	DP_BUF("rev:\t\t", dh, dh->ddf_rev, DDF1_REV_LENGTH);
	DP("seqnum:\t\t%d", dh, dh->seqnum);
	DP("timestamp:\t0x%X", dh, dh->timestamp);
	DP("open:\t\t0x%X", dh, dh->open_flag);
	DP("foreign:\t\t0x%X", dh, dh->foreign_flag);
	DP("grouping:\t\t0x%X", dh, dh->grouping_enforced);
	DP("primary header:\t%lu", dh, dh->primary_table_lba);
	DP("secondary header:\t%lu", dh, dh->secondary_table_lba);
	DP("header type:\t0x%X", dh, dh->header_type);
	DP("workspace len:\t%d", dh, dh->workspace_length);
	DP("workspace lba:\t%lu", dh, dh->workspace_lba);
	DP("max pd:\t\t%d", dh, dh->max_phys_drives);
	DP("max vd:\t\t%d", dh, dh->max_virt_drives);
	DP("max part:\t\t%d", dh, dh->max_partitions);
	DP("vd_config len:\t%d", dh, dh->vd_config_record_len);
	DP("max_primary_elts:\t%d", dh, dh->max_primary_elements);
	DP("adapter_offset:\t%d", dh, dh->adapter_data_offset);
	DP("adapter_len:\t%d", dh, dh->adapter_data_len);
	DP("pd_offset:\t%d", dh, dh->phys_drive_offset);
	DP("pd_len:\t\t%d", dh, dh->phys_drive_len);
	DP("vd_offset:\t%d", dh, dh->virt_drive_offset);
	DP("vd_len:\t\t%d", dh, dh->virt_drive_len);
	DP("config_offset:\t%d", dh, dh->config_record_offset);
	DP("config_len:\t%d", dh, dh->config_record_len);
	DP("disk_data_offset:\t%d", dh, dh->disk_data_offset);
	DP("disk_data_len:\t%d", dh, dh->disk_data_len);
	DP("badblock_offset:\t%d", dh, dh->badblock_offset);
	DP("badblock_len:\t%d", dh, dh->badblock_len);
	DP("diag_offset:\t%d", dh, dh->diag_offset);
	DP("diag_len:\t\t%d", dh, dh->diag_len);
	DP("vendor_offset:\t%d", dh, dh->vendor_offset);
	DP("vendor_len:\t%d", dh, dh->vendor_len);
}

static void
dump_adapter(struct lib_context *lc, struct ddf1_adapter *da)
{
	if (!da)
		return;

	log_print(lc, "Adapter Data at %p", da);
	DP("signature:\t0x%X", da, da->signature);
	DP("crc:\t\t0x%X", da, da->crc);
	DP_GUID("guid:\t\t", da, da->guid);
	DP("pci vendor:\t0x%X", da, da->pci_vendor);
	DP("pci device:\t0x%X", da, da->pci_device);
	DP("pci subvendor:\t0x%X", da, da->pci_subvendor);
	DP("pci subdevice:\t0x%X", da, da->pci_subdevice);
}

static void
dump_disk_data(struct lib_context *lc, struct ddf1_disk_data *fg)
{
	log_print(lc, "Disk Data at %p", fg);
	DP("signature:\t0x%X", fg, fg->signature);
	DP("crc:\t\t0x%X", fg, fg->crc);
	DP_GUID("guid:\t\t", fg, fg->guid);
	DP("reference:\t\t0x%X", fg, fg->reference);
	DP("forced_ref_flag:\t%d", fg, fg->forced_ref_flag);
	DP("forced_guid_flag:\t%d", fg, fg->forced_guid_flag);
}

static void
dump_phys_drive_header(struct lib_context *lc, struct ddf1_phys_drives *pd)
{
	log_print(lc, "Physical Drive Header at %p", pd);
	DP("signature:\t0x%X", pd, pd->signature);
	DP("crc:\t\t0x%X", pd, pd->crc);
	DP("num drives:\t%d", pd, pd->num_drives);
	DP("max drives:\t%d", pd, pd->max_drives);
}

static void
dump_phys_drive(struct lib_context *lc, struct ddf1_phys_drive *pd)
{
	log_print(lc, "Physical Drive at %p", pd);
	DP_GUID("guid:\t\t", pd, pd->guid);
	DP("reference #:\t0x%X", pd, pd->reference);
	DP("type:\t\t0x%X", pd, pd->type);
	DP("state:\t\t0x%X", pd, pd->state);
	DP("size:\t\t%llu", pd, pd->size);
	DP_BUF("path info:\t", pd, pd->path_info, 18);
}

static void
dump_virt_drive_header(struct lib_context *lc, struct ddf1_virt_drives *vd)
{
	log_print(lc, "Virtual Drive Header at %p", vd);
	DP("signature:\t0x%X", vd, vd->signature);
	DP("crc:\t\t0x%X", vd, vd->crc);
	DP("num drives:\t%d", vd, vd->num_drives);
	DP("max drives:\t%d", vd, vd->max_drives);
}

static void
dump_virt_drive(struct lib_context *lc, struct ddf1_virt_drive *vd)
{
	log_print(lc, "Virtual Drive at %p", vd);
	DP_GUID("guid:\t\t", vd, vd->guid);
	DP("vd #:\t\t0x%X", vd, vd->vd_num);
	DP("type:\t\t0x%X", vd, vd->type);
	DP("state:\t\t0x%X", vd, vd->state);
	DP("init state:\t0x%X", vd, vd->init_state);
	DP_BUF("name:\t\t", vd, vd->name, 16);
}

static int
dump_config_record(struct lib_context *lc, struct dev_info *di,
		   struct ddf1 *ddf, int idx)
{
	int i;
	uint16_t x;
	uint32_t *cfg_drive_ids;
	uint64_t *cfg_drive_offsets;
	struct ddf1_config_record *cfg = CR(ddf, idx);

	if (cfg->signature != DDF1_VD_CONFIG_REC)
		return 1;

	log_print(lc, "Virtual Drive Config Record at %p", cfg);
	DP("signature:\t0x%X", cfg, cfg->signature);
	DP("crc:\t\t0x%X", cfg, cfg->crc);
	DP_GUID("guid:\t\t", cfg, cfg->guid);
	DP("timestamp:\t0x%X", cfg, cfg->timestamp);
	DP("seqnum:\t\t%d", cfg, cfg->seqnum);
	DP("primary count:\t%d", cfg, cfg->primary_element_count);
	DP("stripe size:\t%dKiB", cfg, cfg->stripe_size);
	DP("raid level:\t%d", cfg, cfg->raid_level);
	DP("raid qualifier:\t%d", cfg, cfg->raid_qualifier);
	DP("secondary count:\t%d", cfg, cfg->secondary_element_count);
	DP("secondary number:\t%d", cfg, cfg->secondary_element_number);
	DP("secondary level:\t%d", cfg, cfg->secondary_element_raid_level);
	DP("spare 0:\t\t0x%X", cfg, cfg->spares[0]);
	DP("spare 1:\t\t0x%X", cfg, cfg->spares[1]);
	DP("spare 2:\t\t0x%X", cfg, cfg->spares[2]);
	DP("spare 3:\t\t0x%X", cfg, cfg->spares[3]);
	DP("spare 4:\t\t0x%X", cfg, cfg->spares[4]);
	DP("spare 5:\t\t0x%X", cfg, cfg->spares[5]);
	DP("spare 6:\t\t0x%X", cfg, cfg->spares[6]);
	DP("spare 7:\t\t0x%X", cfg, cfg->spares[7]);
	DP("cache policy:\t0x%X", cfg, cfg->cache_policy);
	DP("bg task rate:\t%d", cfg, cfg->bg_task_rate);
	DP("sector count:\t%llu", cfg, cfg->sectors);
	DP("size:\t\t%llu", cfg, cfg->size);
	cfg_drive_ids = CR_IDS(ddf, cfg);
	cfg_drive_offsets = CR_OFF(ddf, cfg);

	x = cfg->primary_element_count;
	log_print(lc, "Drive map:");
	for (i = 0; i < x; i++) {
		log_print(lc, "%d: %X @ %lu", i, cfg_drive_ids[i],
			  cfg_drive_offsets[i]);
	}
	return 1;
}

static int
dump_spares(struct lib_context *lc, struct dev_info *di,
	    struct ddf1 *ddf1, int idx)
{
	int i;
	struct ddf1_spare_header *sh = SR(ddf1, idx);

	log_print(lc, "Spare Config Record at %p", sh);
	DP("signature:\t0x%X", sh, sh->signature);
	DP("crc:\t\t0x%X", sh, sh->crc);
	DP("timestamp:\t0x%X", sh, sh->timestamp);
	DP("type:\t\t0x%X", sh, sh->type);
	DP("num drives:\t%d", sh, sh->num_spares);
	DP("max drives:\t%d", sh, sh->max_spares);

	for (i = 0; i < sh->num_spares; i++) {
		log_print(lc, "Spare %d:", i);
		DP_GUID("guid:\t\t", sh, sh->spares[i].guid);
		DP("secondary:\t%d", sh, sh->spares[i].secondary_element);
	}
	return 1;
}

static void
dump_config_records(struct lib_context *lc, struct dev_info *di,
		    struct ddf1 *ddf1)
{
	static struct ddf1_record_handler handlers = {
		.vd = dump_config_record,
		.spare = dump_spares,
	};

	ddf1_process_records(lc, di, &handlers, ddf1, 1);
}

/* Dump the entire table */
void
ddf1_dump_all(struct lib_context *lc, struct dev_info *di,
	      struct ddf1 *ddf1, const char *handler)
{
	int i;

	dump_top(lc, di, ddf1, handler);
	dump_header(lc, &ddf1->anchor);
	dump_header(lc, ddf1->primary);
	dump_header(lc, ddf1->secondary);
	dump_adapter(lc, ddf1->adapter);
	dump_disk_data(lc, ddf1->disk_data);
	dump_phys_drive_header(lc, ddf1->pd_header);
	for (i = 0; i < ddf1->pd_header->num_drives; i++)
		dump_phys_drive(lc, ddf1->pds + i);

	dump_virt_drive_header(lc, ddf1->vd_header);
	for (i = 0; i < ddf1->vd_header->num_drives; i++)
		dump_virt_drive(lc, ddf1->vds + i);

	dump_config_records(lc, di, ddf1);
}

#endif /* DMRAID_NATIVE_LOG */
