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

    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 "cisreg.h"
#include "ds.h"

static const char *version =
    "dump_cisreg.c v1.4: 1995/01/10 00:55:37 (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 */

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

u_char get_reg(int fd, off_t off)
{
    ds_ioctl_arg_t arg;
    int ret;
    
    arg.conf_reg.Action = CS_READ;
    arg.conf_reg.Offset = off;
    ret = ioctl(fd, DS_ACCESS_CONFIGURATION_REGISTER, &arg);
    if (ret != 0) {
	perror("read config register");
	return 0;
    }
    return arg.conf_reg.Value;
}

void dump_option(int fd)
{
    u_char v = get_reg(fd, 0);
    
    printf("  Configuration option register = %#2.2x\n", v);
    printf("   ");
    if (v & COR_LEVEL_REQ) printf(" [LEVEL_REQ]");
    if (v & COR_SOFT_RESET) printf(" [SOFT_RESET]");
    printf(" [index = %#2.2x]\n", v & COR_CONFIG_MASK);
}

void dump_status(int fd)
{
    u_char v = get_reg(fd, 2);
    
    printf("  Card configuration and status register = %#2.2x\n", v);
    printf("   ");
    if (v & CCSR_INTR_PENDING) printf(" [INTR_PENDING]");
    if (v & CCSR_POWER_DOWN) printf(" [POWER_DOWN]");
    if (v & CCSR_AUDIO_ENA) printf(" [AUDIO]");
    if (v & CCSR_IOIS8) printf(" [IOIS8]");
    if (v & CCSR_SIGCHG_ENA) printf(" [SIGCHG]");
    if (v & CCSR_CHANGED) printf(" [CHANGED]");
    printf("\n");
}

void dump_pin(int fd)
{
    u_char v = get_reg(fd, 4);
    
    printf("  Pin replacement register = %#2.2x\n", v);
    printf("   ");
    if (v & PRR_WP_STATUS) printf(" [WP]");
    if (v & PRR_READY_STATUS) printf(" [READY]");
    if (v & PRR_BVD2_STATUS) printf(" [BVD2]");
    if (v & PRR_BVD1_STATUS) printf(" [BVD1]");
    if (v & PRR_WP_EVENT) printf(" [WP_EVENT]");
    if (v & PRR_READY_EVENT) printf(" [READY_EVENT]");
    if (v & PRR_BVD2_EVENT) printf(" [BVD2_EVENT]");
    if (v & PRR_BVD1_EVENT) printf(" [BVD1_EVENT]");
    printf("\n");
}

void dump_copy(int fd)
{
    u_char v = get_reg(fd, 6);
    
    printf("  Socket and copy register = %#2.2x\n", v);
    printf("    [socket = %d] [copy = %d]\n",
	   v & SCR_SOCKET_NUM,
	   (v & SCR_COPY_NUM) >> 4);
}

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

#define MAX_SOCKS 8

void main(int argc, char *argv[])
{
    int i, major, fd, ret;
    u_long mask;
    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 = 0;
	arg.tuple.DesiredTuple = CISTPL_CONFIG;
	arg.tuple.TupleOffset = 0;
	ret = ioctl(fd, DS_GET_FIRST_TUPLE, &arg);
	if (ret != 0) {
	    perror("no config tuple");
	    continue;
	}
	ret = ioctl(fd, DS_GET_TUPLE_DATA, &arg);
	if (ret != 0) {
	    perror("reading tuple data");
	    continue;
	}
	ret = ioctl(fd, DS_PARSE_TUPLE, &arg);
	if (ret != 0) {
	    perror("parse error");
	    continue;
	}
	printf("  Config register base = %#4.4lx, mask = %#4.4lx\n",
	       arg.tuple_parse.parse.config.base,
	       arg.tuple_parse.parse.config.rmask[0]);
	mask = arg.tuple_parse.parse.config.rmask[0];
	if (mask & PRESENT_OPTION)
	    dump_option(fd);
	if (mask & PRESENT_STATUS)
	    dump_status(fd);
	if (mask & PRESENT_PIN_REPLACE)
	    dump_pin(fd);
	if (mask & PRESENT_COPY)
	    dump_copy(fd);
	if (mask == 0)
	    printf("  no config registers\n\n");
	else
	    printf("\n");
    }
}
