/*
 * printinfo.c  -  Print more info about an option rom
 *
 * Copyright (C) 1998-2003 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.c,v 1.4 2003/01/25 23:29:43 gkminix Exp $
 */

#include <stdio.h>
#include <dos.h>
#include <ctype.h>
#include "romcheck.h"
#include "pnp.h"



/*
 * Convert PnP product identifier into readable text
 */
static char *convid(pnp)
struct pnphdr *pnp;
{
  static char buf[8];
  char *id = pnp->devid;
  int 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(ofs, seg)
unsigned short ofs;
unsigned short seg;
{
  static char buf[50];
  char *cp;

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

  /* Copy string into local buffer */
  movedata(seg, ofs, __get_ds(), buf, 49);
  buf[49] = '\0';

  /* Convert all special characters to blanks */
  for (cp = buf; *cp; cp++)
	if (*cp && *cp < 0x20)
		*cp = ' ';
  return(buf);
}



/*
 * Print information for one single PnP expansion header
 */
static void printsingle(pnp, seg)
struct pnphdr *pnp;
unsigned short seg;
{
  int pnptype;
  static 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:  %d\n", (int)(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:           %02X %02X %02X  ",
			pnp->devtype[2], pnp->devtype[1], pnp->devtype[0]);

  pnptype = pnp->devtype[2];
  if (pnptype < 1 || pnptype > 11)
	printf("(unknown)\n");
  else
	printf("(%s)\n", basetypes[pnptype - 1]);

  printf("        Device indicators:\n");
  if (pnp->devind == 0) {
	printf("            none\n");
  } else {
	if (pnp->devind & DEVIND_IPL)
		printf("            Device is an Initial Program Load device\n");
	if (pnp->devind & DEVIND_INPUT)
		printf("            Device is an input device\n");
	if (pnp->devind & DEVIND_DISP)
		printf("            Device is a display device\n");
	if (pnp->devind & DEVIND_DDIM)
		printf("            ROM supports Device Driver Initialization Model\n");
	if (pnp->devind & DEVIND_SHADOW)
		printf("            ROM may be shadowed in RAM\n");
	if (pnp->devind & DEVIND_CACHE)
		printf("            ROM is read cacheable\n");
	if (pnp->devind & DEVIND_BOOT)
		printf("            ROM is only required if device selected for boot\n");
  }
}



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

  /* Don't do anything if the index number or rom signature are invalid */
  if (index < 0 || 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 */
  movedata(sip->sigsegs[index], PNP_HDROFS, __get_ds(), &hdrofs, sizeof(hdrofs));

  /* Scan through all expansion headers */
  while (hdrofs > 0 && hdrofs < segend) {
	/* Copy expansion header into local memory */
	movedata(sip->sigsegs[index], hdrofs, __get_ds(), &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 %04X\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 += ((unsigned char *)&pnp)[i];
		hdrchksum &= 0x00FF;
	}
	if (hdrchksum != 0) {
		printf("    PnP compatible ROM header found with invalid checksum at offset %04X\n",
								hdrofs);
		return;
	}

	/* Print PnP header information */
	printf("    Information from PnP expansion header %d:\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 %d PnP compatible ROM headers\n", hdrnum);
}



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

  /* Ask the user about which rom to print information for */
  printf("\n\n");
  index = readint("Enter rom area number", 1, sip->signum, 1) - 1;

  /* Print information for rom */
  printf("\n\nInformation for rom area number %d:\n", index + 1);
  printf("    Starting segment: %4X", sip->sigsegs[index]);
  if (sip->flashseg == sip->sigsegs[index])
	printf("   (at beginning of flash EPROM)");
  printf("\n    Rom size:         %d 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(sip)
struct scaninfo *sip;
{
  unsigned short index = sip->nbindex;

  if (index >= 0 && index < sip->signum) {
	printf("\n\nFound netboot bootrom:\n");
	printf("    Rom area number:  %d\n", index + 1);
	printf("    Starting segment: %4X", sip->sigsegs[index]);
	if (sip->flashseg == sip->sigsegs[index])
		printf("   (at beginning of flash EPROM)");
	printf("\n    Rom size:         %d kB\n", sip->sigsize[index] / 64);
	printf("    Version:          %d.%d\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");
  }
}



syntax highlighted by Code2HTML, v. 0.9.1