/*======================================================================

    PCMCIA card CIS dump

    Written by David Hinds, dhinds@allegro.stanford.edu
    
======================================================================*/

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/stat.h>

#include "cs_types.h"
#include "cs.h"
#include "cistpl.h"
#include "ds.h"

static const char *version =
    "dump_tuples.c v$Revision: 1.17 $: $Date: 1995/04/06 20:02:08 $ (David Hinds)\n";

/*====================================================================*/

int lookup_dev(char *name)
{
    FILE *f;
    int n;
    char s[32], t[32];
    
    f = fopen("/proc/devices", "r");
    if (f == NULL)
	return -1;
    while (fgets(s, 32, f) != NULL) {
	if (sscanf(s, "%d %s", &n, t) == 2)
	    if (strcmp(name, t) == 0)
		break;
    }
    fclose(f);
    if (strcmp(name, t) == 0)
	return n;
    else
	return -1;
} /* lookup_dev */

/*====================================================================*/

int open_dev(dev_t dev)
{
    char *fn;
    int fd;
    
    if ((fn = tmpnam(NULL)) == NULL)
	return -1;
    if (mknod(fn, (S_IFCHR|S_IREAD|S_IWRITE), dev) != 0)
	return -1;
    if ((fd = open(fn, O_RDONLY)) < 0)
	return -1;
    if (unlink(fn) != 0) {
	close(fd);
	return -1;
    }
    return fd;
} /* open_dev */

/*====================================================================*/

void print_tuple(tuple_parse_t *tup)
{
    int i;
    printf("  Offset: 0x%2.2lx, tuple: %#2.2x, link: %#2.2x\n",
	   tup->tuple.CISOffset, tup->tuple.TupleCode,
	   tup->tuple.TupleLink);
    for (i = 0; i < tup->tuple.TupleDataLen; i++) {
	if ((i % 16) == 0) printf("    ");
	printf("%2.2x ", (u_char)tup->data[i]);
	if ((i % 16) == 15) printf("\n");
    }
    if ((i % 16) != 0) printf("\n");
} /* print_tuple */

/*====================================================================*/

void print_funcid(cistpl_funcid_t *fn)
{
    printf("  Function: ");
    switch (fn->func) {
    case CISTPL_FUNCID_MULTI:
	printf("multi-function"); break;
    case CISTPL_FUNCID_MEMORY:
	printf("memory card"); break;
    case CISTPL_FUNCID_SERIAL:
	printf("serial port"); break;
    case CISTPL_FUNCID_PARALLEL:
	printf("parallel port"); break;
    case CISTPL_FUNCID_FIXED:
	printf("fixed disk"); break;
    case CISTPL_FUNCID_VIDEO:
	printf("video adapter"); break;
    case CISTPL_FUNCID_NETWORK:
	printf("network adapter"); break;
    case CISTPL_FUNCID_AIMS:
	printf("auto-inc mass storage"); break;
    default:
	printf("unknown"); break;
    }
    if (fn->sysinit & CISTPL_SYSINIT_POST)
	printf(" [POST]");
    if (fn->sysinit & CISTPL_SYSINIT_ROM)
	printf(" [ROM]");
    printf("\n");
} /* print_funcid */

/*====================================================================*/

void print_size(u_long size)
{
    if (size < 1024)
	printf("%lu bytes", size);
    else if (size < 1024*1024)
	printf("%lu kb", size/1024);
    else
	printf("%lu mb", size/(1024*1024));
}

void print_time(u_long tm, u_long scale)
{
    float t = (float)(tm) * scale * 0.000000001;
    if (t > 1.0)
	printf("%.1f s", t);
    else if (t > 0.001)
	printf("%.0f ms", t*1000.0);
    else if (t > 0.000001)
	printf("%.0f us", t*1000000.0);
    else
	printf("%.0f ns", t*1000000000.0);
}

void print_volt(u_long vi)
{
    float v = vi * 0.00001;
    if (v > 1.0)
	printf("%.1f V", v);
    else if (v > 0.001)
	printf("%.0f mV", v*1000.0);
    else
	printf("%.0f uV", v*1000000.0);
}
    
void print_current(u_long ii)
{
    float i = ii * 0.0000001;
    if (i > 1.0)
	printf("%.1f A", i);
    else if (i > 0.001)
	printf("%.0f mA", i*1000.0);
    else
	printf("%.0f uA", i*1000000.0);
}
    
/*====================================================================*/

static const char *dtype[] = {
    "NULL", "ROM", "OTPROM", "EPROM", "EEPROM", "FLASH", "SRAM",
    "DRAM", "rsvd", "rsvd", "rsvd", "rsvd", "rsvd", "Function-specific",
    "Extended", "rsvd"
};

void print_device(cistpl_device_t *dev)
{
    int i;
    for (i = 0; i < dev->ndev; i++) {
	printf("    %s: ", dtype[dev->dev[i].type]);
	printf("%ld ns, ", dev->dev[i].speed);
	print_size(dev->dev[i].size);
	printf("\n");
    }
    if (dev->ndev == 0)
	printf("    No device info.\n");
} /* print_device */

/*====================================================================*/

void print_power(cistpl_power_t *power)
{
    int first = 1;
    if (power->present & (1<<CISTPL_POWER_VNOM)) {
	first = 0;
	printf(" Vnom ");
	print_volt(power->param[CISTPL_POWER_VNOM]);
    }
    if (power->present & (1<<CISTPL_POWER_VMIN)) {
	if (!first) putchar(','); else first = 0;
	printf(" Vmin ");
	print_volt(power->param[CISTPL_POWER_VMIN]);
    }
    if (power->present & (1<<CISTPL_POWER_VMAX)) {
	if (!first) putchar(','); else first = 0;
 	printf(" Vmax ");
	print_volt(power->param[CISTPL_POWER_VMAX]);
    }
    printf("\n       "); first = 1;
    if (power->present & (1<<CISTPL_POWER_ISTATIC)) {
	first = 0;
	printf(" Istatic ");
	print_current(power->param[CISTPL_POWER_ISTATIC]);
    }
    if (power->present & (1<<CISTPL_POWER_IAVG)) {
	if (!first) putchar(','); else first = 0;
	printf(" Iavg ");
	print_current(power->param[CISTPL_POWER_IAVG]);
    }
    if (power->present & (1<<CISTPL_POWER_IPEAK)) {
	if (!first) putchar(','); else first = 0;
	printf(" Ipeak ");
	print_current(power->param[CISTPL_POWER_IPEAK]);
    }
    if (power->present & (1<<CISTPL_POWER_IDOWN)) {
	if (!first) putchar(','); else first = 0;
	printf(" Idown ");
	print_current(power->param[CISTPL_POWER_IDOWN]);
    }
    if (power->flags & CISTPL_POWER_HIGHZ_OK) {
	if (!first) putchar(','); else first = 0;
	printf(" highz OK");
    }
    if (power->flags & CISTPL_POWER_HIGHZ_REQ) {
	if (!first) putchar(','); else first = 0;
	printf(" highz");
    }
    printf("\n");
} /* print_power */

/*====================================================================*/

void print_cftable_entry(cistpl_cftable_entry_t *entry)
{
    int i;
    
    printf("  Config entry %#2.2x%s:\n", entry->index,
	   (entry->flags & CISTPL_CFTABLE_DEFAULT) ? " (default)" : "");

    if (entry->flags & ~CISTPL_CFTABLE_DEFAULT) {
	printf(" ");
	if (entry->flags & CISTPL_CFTABLE_BVDS)
	    printf(" [BVD]");
	if (entry->flags & CISTPL_CFTABLE_WP)
	    printf(" [WP]");
	if (entry->flags & CISTPL_CFTABLE_RDYBSY)
	    printf(" [RDYBSY]");
	if (entry->flags & CISTPL_CFTABLE_MWAIT)
	    printf(" [MWAIT]");
	if (entry->flags & CISTPL_CFTABLE_AUDIO)
	    printf(" [AUDIO]");
	if (entry->flags & CISTPL_CFTABLE_READONLY)
	    printf(" [READONLY]");
	if (entry->flags & CISTPL_CFTABLE_PWRDOWN)
	    printf(" [PWRDOWN]");
	printf("\n");
    }
    
    if (entry->vcc.present) {
	printf("  Vcc: ");
	print_power(&entry->vcc);
    }
    if (entry->vpp1.present) {
	printf("  Vpp1:");
	print_power(&entry->vpp1);
    }
    if (entry->vpp2.present) {
	printf("  Vpp2:");
	print_power(&entry->vpp2);
    }

    if ((entry->timing.wait != 0) || (entry->timing.ready != 0) ||
	(entry->timing.reserved != 0)) {
	printf("  Timing:");
	if (entry->timing.wait != 0) {
	    printf(" wait ");
	    print_time(entry->timing.wait, entry->timing.waitscale);
	}
	if (entry->timing.ready != 0) {
	    printf(" ready ");
	    print_time(entry->timing.ready, entry->timing.rdyscale);
	}
	if (entry->timing.reserved != 0) {
	    printf(" reserved ");
	    print_time(entry->timing.reserved, entry->timing.rsvscale);
	}
	printf("\n");
    }
    
    if (entry->io.nwin) {
	cistpl_io_t *io = &entry->io;
	printf("  I/O windows: [lines = %d]",
	       io->flags & CISTPL_IO_LINES_MASK);
	if (io->flags & CISTPL_IO_8BIT) printf(" [8 bit]");
	if (io->flags & CISTPL_IO_16BIT) printf(" [16 bit]");
	if (io->flags & CISTPL_IO_RANGE) printf(" [range]");
	printf("\n");
	for (i = 0; i < io->nwin; i++)
	    printf("    base = %#4.4lx, length = %#4.4lx\n",
		   io->win[i].base, io->win[i].len);
    }

    if (entry->irq.IRQInfo1) {
	printf("  Interrupt ");
	if (entry->irq.IRQInfo1 & IRQ_INFO2_VALID)
	    printf("mask = %#4.4lx", entry->irq.IRQInfo2);
	else
	    printf("%ld", entry->irq.IRQInfo1 & IRQ_MASK);
	if (entry->irq.IRQInfo1 & IRQ_LEVEL_ID) printf(" [level]");
	if (entry->irq.IRQInfo1 & IRQ_PULSE_ID) printf(" [pulse]");
	if (entry->irq.IRQInfo1 & IRQ_SHARE_ID) printf(" [shared]");
	printf("\n");
    }

    if (entry->mem.nwin) {
	cistpl_mem_t *mem = &entry->mem;
	printf("  Memory windows:\n");
	for (i = 0; i < mem->nwin; i++)
	    printf("    card = 0x%4.4lx, host = 0x%4.4lx, length = "
		   "0x%4.4lx\n", mem->win[i].card_addr,
		   (u_long)mem->win[i].host_addr, mem->win[i].len);
    }

    if (entry->subtuples)
	printf("  %d bytes in subtuples\n", entry->subtuples);
    
} /* print_cftable_entry */

/*====================================================================*/

void print_jedec(cistpl_jedec_t *jedec)
{
    int i;
    for (i = 0; i < jedec->nid; i++) {
	printf("    mfr 0x%02x, info 0x%02x\n",
	       jedec->id[i].mfr, jedec->id[i].info);
    }
} /* print_jedec */

/*====================================================================*/

void print_device_geo(cistpl_device_geo_t *geo)
{
    int i;
    for (i = 0; i < geo->ngeo; i++) {
	printf("    bus %d, erase %#lx, read %#lx, write %#lx, "
	       "partition %#lx, interleave %#lx\n",
	       geo->geo[i].buswidth, geo->geo[i].erase_block,
	       geo->geo[i].read_block, geo->geo[i].write_block,
	       geo->geo[i].partition, geo->geo[i].interleave);
    }
} /* print_device_geo */

/*====================================================================*/

void print_org(cistpl_org_t *org)
{
    printf("  Data organization: ");
    switch (org->data_org) {
    case CISTPL_ORG_FS:
	printf("filesystem"); break;
    case CISTPL_ORG_APPSPEC:
	printf("app-specific"); break;
    case CISTPL_ORG_XIP:
	printf("executable code"); break;
    default:
	if (org->data_org < 0x80)
	    printf("reserved");
	else
	    printf("vendor-specific");
    }
    printf(", \"%s\"\n", org->desc);
} /* print_org */

/*====================================================================*/

void print_vers_2(cistpl_vers_2_t *v2)
{
    printf("  Version 0x%2.2x, compliance 0x%2.2x, dindex 0x%4.4x\n",
	   v2->vers, v2->comply, v2->dindex);
    printf("  vspec8 = 0x%2.2x, vspec9 = 0x%2.2x, nhdr = %d\n",
	   v2->vspec8, v2->vspec9, v2->nhdr);
    printf("  Vendor \"%s\"\n", v2->str+v2->vendor);
    printf("  Info: %s\n", v2->str+v2->info);
} /* print_vers_2 */

/*====================================================================*/

void print_parse(tuple_parse_t *tup)
{
    int i;
    switch (tup->tuple.TupleCode) {
    case CISTPL_DEVICE:
    case CISTPL_DEVICE_A:
	if (tup->tuple.TupleCode == CISTPL_DEVICE)
	    printf("  Common memory devices: \n");
	else
	    printf("  Attribute memory devices: \n");
	print_device(&tup->parse.device);
	break;
    case CISTPL_CHECKSUM:
	printf("  Checksum start 0x%04x, len 0x%04x, sum 0x%02x\n",
	       tup->parse.checksum.addr, tup->parse.checksum.len,
	       tup->parse.checksum.sum);
	break;
    case CISTPL_LONGLINK_A:
	printf("  Long link attr 0x%04lx\n", tup->parse.longlink.addr);
	break;
    case CISTPL_LONGLINK_C:
	printf("  Long link common 0x%04lx\n", tup->parse.longlink.addr);
	break;
    case CISTPL_NO_LINK:
	printf("  No long link present\n");
	break;
    case CISTPL_VERS_1:
	printf("  Version %d.%d\n", tup->parse.version_1.major,
	       tup->parse.version_1.minor);
	for (i = 0; i < tup->parse.version_1.ns; i++) {
	    printf("%s", (i == 0) ? "  " : ", ");
	    printf("\"%s\"", tup->parse.version_1.str +
		   tup->parse.version_1.ofs[i]);
	}
	printf("\n");
	break;
    case CISTPL_ALTSTR:
	break;
    case CISTPL_JEDEC_A:
    case CISTPL_JEDEC_C:
	if (tup->tuple.TupleCode == CISTPL_JEDEC_C)
	    printf("  Common memory JEDEC: \n");
	else
	    printf("  Attribute memory JEDEC: \n");
	print_jedec(&tup->parse.jedec);
	break;
    case CISTPL_DEVICE_GEO:
    case CISTPL_DEVICE_GEO_A:
	if (tup->tuple.TupleCode == CISTPL_DEVICE_GEO)
	    printf("  Common memory device geometry: \n");
	else
	    printf("  Attribute memory device geometry: \n");
	print_device_geo(&tup->parse.device_geo);
	break;
    case CISTPL_MANFID:
	printf("  Manufacturer %#4.4x, card ID %#4.4x\n",
	       tup->parse.manfid.manf, tup->parse.manfid.card);
	break;
    case CISTPL_FUNCID:
	print_funcid(&tup->parse.funcid);
	break;
    case CISTPL_CONFIG:
	printf("  Config base = %#4.4lx, mask = %#4.4lx, ",
	       tup->parse.config.base, tup->parse.config.rmask[0]);
	printf("last config index = %#2.2x\n",
	       tup->parse.config.last_idx);
	if (tup->parse.config.subtuples)
	    printf("  %d bytes in subtuples\n",
		   tup->parse.config.subtuples);
	break;
    case CISTPL_CFTABLE_ENTRY:
	print_cftable_entry(&tup->parse.cftable_entry);
	break;
    case CISTPL_VERS_2:
	print_vers_2(&tup->parse.vers_2);
	break;
    case CISTPL_ORG:
	print_org(&tup->parse.org);
	break;
    }
} /* print_parse */

/*====================================================================*/

#define MAX_SOCKS 8

void main(int argc, char *argv[])
{
    int i, major, fd, ret;
    ds_ioctl_arg_t arg;

    if ((argc > 1) && (strcmp(argv[1], "-v") == 0))
	printf("%s", version);

    major = lookup_dev("pcmcia");
    if (major < 0) {
	fprintf(stderr, "no pcmcia driver in /proc/devices\n");
	exit(EXIT_FAILURE);
    }
    for (i = 0; i < MAX_SOCKS; i++) {
	fd = open_dev((major<<8)+i);
	if (fd < 0) break;
	printf("Socket %d:\n", i);
	arg.tuple.TupleDataMax = sizeof(arg.tuple_parse.data);
	arg.tuple.Attributes = TUPLE_RETURN_LINK;
	arg.tuple.DesiredTuple = RETURN_FIRST_TUPLE;
	arg.tuple.TupleOffset = 0;
	ret = ioctl(fd, DS_GET_FIRST_TUPLE, &arg);
	if (ret != 0) {
	    perror("no first tuple");
	    continue;
	}
	for (;;) {
	    if (ioctl(fd, DS_GET_TUPLE_DATA, &arg) == 0) {
		print_tuple(&arg.tuple_parse);
		ret = ioctl(fd, DS_PARSE_TUPLE, &arg);
		if (ret != 0) {
		    if (errno != ENOSYS)
			printf("  parse error: %s\n", strerror(errno));
		}
		else
		    print_parse(&arg.tuple_parse);
	    }
	    else {
		printf("  get tuple data: %s\n", strerror(errno));
		break;
	    }
	    printf("\n");
	    ret = ioctl(fd, DS_GET_NEXT_TUPLE, &arg);
	    if (ret != 0) {
		if (errno != ENODATA)
		    printf("next tuple: %s\n", strerror(errno));
		break;
	    }
	}
    }
}
