/* vim: set filetype=c:
 *
 * printinfo.c86  -  Print more info about an option rom
 *
 * Copyright (C) 1998-2007 Gero Kuhlmann   <gero@gkminix.han.de>
 *
 *  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
 *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Id: printinfo.c86,v 1.6 2007/01/06 18:31:21 gkminix Exp $
 */

#include <common.h86>
#include <i386/conio.h86>
#include <i386/ctype.h86>
#include <i386/string.h86>
#include <system/bootrom.h86>
#include <system/pnp.h86>
#include "romcheck.h86"



/*
 * Convert PnP product identifier into readable text
 */
static char *convid(const struct pnphdr *pnp)
{
  static char buf[8];
  const __u8 *id = pnp->devid;
  short i;

  /* Check if we have an identifier at all */
  if ((id[0] + id[1] + id[2] + id[3]) == 0)
	return("(none)");

  /* Convert compressed product identifier into ascii text */
  buf[0] = ((id[0] & 0x7C) >> 2) + 0x40;
  buf[1] = ((id[0] & 0x03) << 3) + ((id[1] & 0xE0) >> 5) + 0x40;
  buf[2] = ((id[1] & 0x1F)) + 0x40;
  buf[3] = ((id[2] & 0xF0) >> 4);
  buf[4] = ((id[2] & 0x0F));
  buf[5] = ((id[3] & 0xF0) >> 4);
  buf[6] = ((id[3] & 0x0F));
  buf[7] = 0x00;

  /* Convert BCD number into ascii text */
  for (i = 3; i < 7; i++) {
	buf[i] += '0';
	if (buf[i] > '9')
		buf[i] += 'A' - '0';
  }
  return(buf);
}



/*
 * Get zero terminated string from ROM into static buffer
 */
static char *convstr(unsigned short ofs, unsigned short seg)
{
  static unsigned char buf[50];
  unsigned char *cp;

  /* Check if we have a string at all */
  if (ofs == 0)
	return("(none)");

  /* Copy string into local buffer */
  movemem(far2linear(MK_FP(seg, ofs)),
          far2linear(MK_FP(getds(), (int)(buf))),
          sizeof(buf) - 1);
  buf[sizeof(buf) - 1] = '\0';

  /* Convert all special characters to blanks */
  for (cp = buf; *cp; cp++)
	if (*cp && !isprint(*cp))
		*cp = ' ';
  return((char *)buf);
}



/*
 * Print information for one single PnP expansion header
 */
static void printsingle(const struct pnphdr *pnp, unsigned short seg)
{
  unsigned short pnptype;
  static const char *basetypes[] = {
		"Mass Storage Device",
		"Network Interface Controller",
		"Display Controller",
		"Multimedia Controller",
		"Memory",
		"Bridge Controller",
		"Communications Device",
		"System Peripherals",
		"Input Devices",
		"Docking Station",
		"CPU Type",
		"Serial Controller" };

  printf("        Revision number of header:  %hu\n",
			(unsigned short)(pnp->revision));
  printf("        Device identifier:          %s\n",
			convid(pnp));
  printf("        Manufacturer string:        %s\n",
			convstr(pnp->manufacturer, seg));
  printf("        Product name string:        %s\n",
			convstr(pnp->productname, seg));
  printf("        Device type code:           %02hX %02hX %02hX",
			(unsigned short)(pnp->devtype[0]),
			(unsigned short)(pnp->devtype[1]),
			(unsigned short)(pnp->devtype[2]));

  pnptype = (unsigned short)(pnp->devtype[0]);
  if (pnptype > 0 && pnptype < 13)
	printf("  (%s)\n", basetypes[pnptype - 1]);
  else
	printf("\n");

  printf("        Device indicators:\n");
  if (pnp->devind == 0) {
	printf("            none\n");
  } else {
	if (pnp->devind & PNP_DEVIND_IPL)
		printf("            Device is an Initial Program Load device\n");
	if (pnp->devind & PNP_DEVIND_INPUT)
		printf("            Device is an input device\n");
	if (pnp->devind & PNP_DEVIND_DISP)
		printf("            Device is a display device\n");
	if (pnp->devind & PNP_DEVIND_DDIM)
		printf("            ROM supports Device Driver Initialization Model\n");
	if (pnp->devind & PNP_DEVIND_SHADOW)
		printf("            ROM may be shadowed in RAM\n");
	if (pnp->devind & PNP_DEVIND_CACHE)
		printf("            ROM is read cacheable\n");
	if (pnp->devind & PNP_DEVIND_BOOT)
		printf("            ROM is only required if device selected for boot\n");
  }
}



/*
 * Print PnP expansion header information
 */
static void printpnp(const struct scaninfo *sip, unsigned short index)
{
  static struct pnphdr pnp;
  __u16 hdrofs, segend;
  unsigned short hdrlen, i;
  unsigned short hdrchksum = 0;
  unsigned short hdrnum = 0;

  /* Don't do anything if the index number or rom signature are invalid */
  if (index >= sip->signum || !sip->sigvalid[index])
	return;

  /* Determine last possible offset for PnP expansion header */
  if ((sip->sigsize[index] & 0xF000) != 0)
	segend = 0xFFFF - sizeof(pnp);
  else
	segend = (sip->sigsize[index] * 16) - sizeof(pnp);

  /* Get address of first expansion header */
  movemem(far2linear(MK_FP(sip->sigsegs[index], ROMPNPPTROFS)),
          far2linear(MK_FP(getds(), (int)(&hdrofs))),
          sizeof(hdrofs));

  /* Scan through all expansion headers */
  while (hdrofs > 0 && hdrofs < segend) {
	/* Copy expansion header into local memory */
  	movemem(far2linear(MK_FP(sip->sigsegs[index], hdrofs)),
	          far2linear(MK_FP(getds(), (int)(&pnp))),
	          sizeof(pnp));

	/* Check if we have a valid PnP header */
	if (memcmp(pnp.sig, "$PnP", 4))
		break;

	/* Check for correct header size */
	if (pnp.size != (sizeof(pnp) / 16)) {
		printf("    PnP compatible ROM header found with invalid size at offset %04hX\n",
								hdrofs);
		return;
	}
	hdrlen = pnp.size * 16;
	if (hdrlen > sizeof(pnp))
		hdrlen = sizeof(pnp);

	/* Compute checksum of PnP expansion header */
	for (i = 0; i < hdrlen; i++) {
		hdrchksum += ((__u8 *)&pnp)[i];
		hdrchksum &= 0x00FF;
	}
	if (hdrchksum != 0) {
		printf("    PnP compatible ROM header found with invalid checksum at offset %04hX\n",
								hdrofs);
		return;
	}

	/* Print PnP header information */
	printf("    Information from PnP expansion header %hu:\n", hdrnum);
	printsingle(&pnp, sip->sigsegs[index]);
	hdrnum++;

	/* Get offset to next header */
	hdrofs = pnp.next;
  }

  /* Finally print how many headers we found */
  if (hdrnum == 0)
	printf("    No PnP compatible ROM header found\n");
  else if (hdrnum == 1)
	printf("    Found one PnP compatible ROM header\n");
  else
	printf("    Found %hu PnP compatible ROM headers\n", hdrnum);
}



/*
 * Print a rom area or the whole memory as a hex dump.
 */
void printinfo(const struct scaninfo *sip)
{
  unsigned short index;

  /* Ask the user about which rom to print information for */
  if (sip->signum == 0) {
	printf("\n\nNo rom area found for which to print information\n");
	index = 0;
  } else if (sip->signum > 1) {
	printf("\n\n");
	index = (unsigned short)readint("Enter rom area number", 1, sip->signum, 1);
  } else
	index = 1;

  /* Print information for rom */
  if (index > 0) {
	printf("\n\nInformation for rom area number %hd:\n", index--);
	printf("    Starting segment: %04hX", sip->sigsegs[index]);
	printf("\n    Rom size:         %hu kB\n", sip->sigsize[index] / 64);
	printf("    Copyright:        %s\n",
			convstr(sip->sigcpyrt[index], sip->sigsegs[index]));
	if (!sip->sigvalid[index])
		printf("    The checksum of the rom is invalid!\n");
	else
		printpnp(sip, index);
	if (index == sip->nbindex)
		printf("    This rom is a netboot bootrom\n");
  }
}



/*
 * Print information about netboot bootrom
 */
void printnb(const struct scaninfo *sip)
{
  unsigned short index = sip->nbindex;

  if (index < sip->signum) {
	printf("\n\nFound netboot bootrom:\n");
	printf("    Rom area number:  %hu\n", index + 1);
	printf("    Starting segment: %04hX", sip->sigsegs[index]);
	printf("\n    Rom size:         %hu kB\n", sip->sigsize[index] / 64);
	printf("    Version:          %hd.%hd\n", sip->nbmajor, sip->nbminor);
	printf("    Copyright:        %s\n", convstr(sip->sigcpyrt[index], sip->sigsegs[index]));
	if (!sip->sigvalid[index])
		printf("    The checksum of the netboot bootrom is invalid!\n");
	else
		printpnp(sip, index);
  } else {
	printf("\nNo netboot bootrom found\n");
  }
}

