[PATCH 5/5] Documentation: add an utility to parse CIS files to readable form

Dmitry Eremin-Solenikov dbaryshkov at gmail.com
Thu Sep 23 11:19:57 EDT 2010


Import from pcmcia-cs project a dump_cis utility. It reads
machine-readable CIS file and output text representation (which can be
processed by firmware/mkcis utility).

Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov at gmail.com>
---
 Documentation/pcmcia/Makefile   |    3 +-
 Documentation/pcmcia/dump_cis.c | 1992 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 1994 insertions(+), 1 deletions(-)
 create mode 100644 Documentation/pcmcia/dump_cis.c

diff --git a/Documentation/pcmcia/Makefile b/Documentation/pcmcia/Makefile
index accde87..d3a4b40 100644
--- a/Documentation/pcmcia/Makefile
+++ b/Documentation/pcmcia/Makefile
@@ -2,9 +2,10 @@
 obj- := dummy.o
 
 # List of programs to build
-hostprogs-y := crc32hash
+hostprogs-y := crc32hash dump_cis
 
 # Tell kbuild to always build the programs
 always := $(hostprogs-y)
 
 HOSTCFLAGS_crc32hash.o += -I$(objtree)/usr/include
+HOSTCFLAGS_dump_cis.o += -Iinclude/pcmcia
diff --git a/Documentation/pcmcia/dump_cis.c b/Documentation/pcmcia/dump_cis.c
new file mode 100644
index 0000000..4de8641
--- /dev/null
+++ b/Documentation/pcmcia/dump_cis.c
@@ -0,0 +1,1992 @@
+/***********************************************************************
+ *     PC Card CIS dump utility
+ *
+ *     dump_cis.c from pcmcia-cs
+ *
+ *     The contents of this file are subject to the Mozilla Public
+ *     License Version 1.1 (the "License"); you may not use this file
+ *     except in compliance with the License. You may obtain a copy of
+ *     the License at http://www.mozilla.org/MPL/
+ *
+ *     Software distributed under the License is distributed on an "AS
+ *     IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ *     implied. See the License for the specific language governing
+ *     rights and limitations under the License.
+ *
+ *     The initial developer of the original code is David A. Hinds
+ *     <dahinds at users.sourceforge.net>.  Portions created by David A. Hinds
+ *     are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+ *
+ *     Alternatively, the contents of this file may be used under the
+ *     terms of the GNU Public License version 2 (the "GPL"), in which
+ *     case the provisions of the GPL are applicable instead of the
+ *     above.  If you wish to allow the use of your version of this file
+ *     only under the terms of the GPL and not to allow others to use
+ *     your version of this file under the MPL, indicate your decision
+ *     by deleting the provisions above and replace them with the notice
+ *     and other provisions required by the GPL.  If you do not delete
+ *     the provisions above, a recipient may use your version of this
+ *     file under either the MPL or the GPL.
+ ***********************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <endian.h>
+
+#ifndef le16toh
+#  include <byteswap.h>
+#  if __BYTE_ORDER == __LITTLE_ENDIAN
+#	define le16toh(x) (x)
+#	define le32toh(x) (x)
+#  else
+#	define le16toh(x) bswap_16(x)
+#	define le32toh(x) bswap_32(x)
+#  endif
+#endif
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <cistpl.h>
+
+/* Bits in IRQInfo1 field */
+#define IRQ_MASK				0x0f
+#define IRQ_NMI_ID				0x01
+#define IRQ_IOCK_ID				0x02
+#define IRQ_BERR_ID				0x04
+#define IRQ_VEND_ID				0x08
+#define IRQ_INFO2_VALID				0x10
+#define IRQ_LEVEL_ID				0x20
+#define IRQ_PULSE_ID				0x40
+#define IRQ_SHARE_ID				0x80
+
+typedef struct tuple_parse_t {
+	tuple_t tuple;
+	cisdata_t data[255];
+	cisparse_t parse;
+} tuple_parse_t;
+
+static int get_tuple_buf(int fd, tuple_t * tuple, int first);
+static int parse_tuple(tuple_t * tuple, cisparse_t * parse);
+
+static int verbose;
+static char indent[10] = "  ";
+
+/*====================================================================*/
+
+static void print_tuple(tuple_parse_t * tup)
+{
+	int i;
+	printf("%soffset 0x%2.2x, tuple 0x%2.2x, link 0x%2.2x\n",
+	       indent, tup->tuple.CISOffset, tup->tuple.TupleCode,
+	       tup->tuple.TupleLink);
+	for (i = 0; i < tup->tuple.TupleDataLen; i++) {
+		if ((i % 16) == 0)
+			printf("%s  ", indent);
+		printf("%2.2x ", (u_char) tup->data[i]);
+		if ((i % 16) == 15)
+			putchar('\n');
+	}
+	if ((i % 16) != 0)
+		putchar('\n');
+}
+
+/*====================================================================*/
+
+static void print_funcid(cistpl_funcid_t * fn)
+{
+	printf("%sfuncid ", indent);
+	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("aims_card");
+		break;
+	case CISTPL_FUNCID_SCSI:
+		printf("scsi_adapter");
+		break;
+	default:
+		printf("unknown");
+		break;
+	}
+	if (fn->sysinit & CISTPL_SYSINIT_POST)
+		printf(" [post]");
+	if (fn->sysinit & CISTPL_SYSINIT_ROM)
+		printf(" [rom]");
+	putchar('\n');
+}
+
+/*====================================================================*/
+
+static void print_size(u_int size)
+{
+	if (size < 1024)
+		printf("%ub", size);
+	else if (size < 1024 * 1024)
+		printf("%ukb", size / 1024);
+	else
+		printf("%umb", size / (1024 * 1024));
+}
+
+static void print_unit(u_int v, char *unit, char tag)
+{
+	int n;
+	for (n = 0; (v % 1000) == 0; n++)
+		v /= 1000;
+	printf("%u", v);
+	if (n < strlen(unit))
+		putchar(unit[n]);
+	putchar(tag);
+}
+
+static void print_time(u_int tm, u_long scale)
+{
+	print_unit(tm * scale, "num", 's');
+}
+
+static void print_volt(u_int vi)
+{
+	print_unit(vi * 10, "um", 'V');
+}
+
+static void print_current(u_int ii)
+{
+	print_unit(ii / 10, "um", 'A');
+}
+
+static void print_speed(u_int b)
+{
+	if (b < 1000)
+		printf("%u bits/sec", b);
+	else if (b < 1000000)
+		printf("%u kb/sec", b / 1000);
+	else
+		printf("%u mb/sec", b / 1000000);
+}
+
+/*====================================================================*/
+
+static const char *dtype[] = {
+	"NULL", "ROM", "OTPROM", "EPROM", "EEPROM", "FLASH", "SRAM",
+	"DRAM", "rsvd", "rsvd", "rsvd", "rsvd", "rsvd", "fn_specific",
+	"extended", "rsvd"
+};
+
+static void print_device(cistpl_device_t * dev)
+{
+	int i;
+	for (i = 0; i < dev->ndev; i++) {
+		printf("%s  %s ", indent, dtype[dev->dev[i].type]);
+		printf("%uns, ", dev->dev[i].speed);
+		print_size(dev->dev[i].size);
+		putchar('\n');
+	}
+	if (dev->ndev == 0)
+		printf("%s  no_info\n", indent);
+}
+
+/*====================================================================*/
+
+static void print_power(char *tag, cistpl_power_t * power)
+{
+	int i, n;
+	for (i = n = 0; i < 8; i++)
+		if (power->present & (1 << i))
+			n++;
+	i = 0;
+	printf("%s  %s", indent, tag);
+	if (power->present & (1 << CISTPL_POWER_VNOM)) {
+		printf(" Vnom ");
+		i++;
+		print_volt(power->param[CISTPL_POWER_VNOM]);
+	}
+	if (power->present & (1 << CISTPL_POWER_VMIN)) {
+		printf(" Vmin ");
+		i++;
+		print_volt(power->param[CISTPL_POWER_VMIN]);
+	}
+	if (power->present & (1 << CISTPL_POWER_VMAX)) {
+		printf(" Vmax ");
+		i++;
+		print_volt(power->param[CISTPL_POWER_VMAX]);
+	}
+	if (power->present & (1 << CISTPL_POWER_ISTATIC)) {
+		printf(" Istatic ");
+		i++;
+		print_current(power->param[CISTPL_POWER_ISTATIC]);
+	}
+	if (power->present & (1 << CISTPL_POWER_IAVG)) {
+		if (++i == 5)
+			printf("\n%s   ", indent);
+		printf(" Iavg ");
+		print_current(power->param[CISTPL_POWER_IAVG]);
+	}
+	if (power->present & (1 << CISTPL_POWER_IPEAK)) {
+		if (++i == 5)
+			printf("\n%s ", indent);
+		printf(" Ipeak ");
+		print_current(power->param[CISTPL_POWER_IPEAK]);
+	}
+	if (power->present & (1 << CISTPL_POWER_IDOWN)) {
+		if (++i == 5)
+			printf("\n%s ", indent);
+		printf(" Idown ");
+		print_current(power->param[CISTPL_POWER_IDOWN]);
+	}
+	if (power->flags & CISTPL_POWER_HIGHZ_OK) {
+		if (++i == 5)
+			printf("\n%s ", indent);
+		printf(" [highz OK]");
+	}
+	if (power->flags & CISTPL_POWER_HIGHZ_REQ) {
+		printf(" [highz]");
+	}
+	putchar('\n');
+}
+
+/*====================================================================*/
+
+static void print_cftable_entry(cistpl_cftable_entry_t * entry)
+{
+	int i;
+
+	printf("%scftable_entry 0x%2.2x%s\n", indent, entry->index,
+	       (entry->flags & CISTPL_CFTABLE_DEFAULT) ? " [default]" : "");
+
+	if (entry->flags & ~CISTPL_CFTABLE_DEFAULT) {
+		printf("%s ", indent);
+		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]");
+		putchar('\n');
+	}
+
+	if (entry->vcc.present)
+		print_power("Vcc", &entry->vcc);
+	if (entry->vpp1.present)
+		print_power("Vpp1", &entry->vpp1);
+	if (entry->vpp2.present)
+		print_power("Vpp2", &entry->vpp2);
+
+	if ((entry->timing.wait != 0) || (entry->timing.ready != 0) ||
+	    (entry->timing.reserved != 0)) {
+		printf("%s  timing", indent);
+		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);
+		}
+		putchar('\n');
+	}
+
+	if (entry->io.nwin) {
+		cistpl_io_t *io = &entry->io;
+		printf("%s  io", indent);
+		for (i = 0; i < io->nwin; i++) {
+			if (i)
+				putchar(',');
+			printf(" 0x%4.4x-0x%4.4x", io->win[i].base,
+			       io->win[i].base + io->win[i].len - 1);
+		}
+		printf(" [lines=%d]", io->flags & CISTPL_IO_LINES_MASK);
+		if (io->flags & CISTPL_IO_8BIT)
+			printf(" [8bit]");
+		if (io->flags & CISTPL_IO_16BIT)
+			printf(" [16bit]");
+		if (io->flags & CISTPL_IO_RANGE)
+			printf(" [range]");
+		putchar('\n');
+	}
+
+	if (entry->irq.IRQInfo1) {
+		printf("%s  irq ", indent);
+		if (entry->irq.IRQInfo1 & IRQ_INFO2_VALID)
+			printf("mask 0x%04x", entry->irq.IRQInfo2);
+		else
+			printf("%u", 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]");
+		putchar('\n');
+	}
+
+	if (entry->mem.nwin) {
+		cistpl_mem_t *mem = &entry->mem;
+		printf("%s  memory", indent);
+		for (i = 0; i < mem->nwin; i++) {
+			if (i)
+				putchar(',');
+			printf(" 0x%4.4x-0x%4.4x @ 0x%4.4x",
+			       mem->win[i].card_addr,
+			       mem->win[i].card_addr + mem->win[i].len - 1,
+			       mem->win[i].host_addr);
+		}
+		putchar('\n');
+	}
+
+	if (verbose && entry->subtuples)
+		printf("%s  %d bytes in subtuples\n", indent, entry->subtuples);
+
+}
+
+/*====================================================================*/
+
+static void print_cftable_entry_cb(cistpl_cftable_entry_cb_t * entry)
+{
+	int i;
+
+	printf("%scftable_entry_cb 0x%2.2x%s\n", indent, entry->index,
+	       (entry->flags & CISTPL_CFTABLE_DEFAULT) ? " [default]" : "");
+
+	if (entry->flags & ~CISTPL_CFTABLE_DEFAULT) {
+		printf("%s ", indent);
+		if (entry->flags & CISTPL_CFTABLE_MASTER)
+			printf(" [master]");
+		if (entry->flags & CISTPL_CFTABLE_INVALIDATE)
+			printf(" [invalidate]");
+		if (entry->flags & CISTPL_CFTABLE_VGA_PALETTE)
+			printf(" [vga palette]");
+		if (entry->flags & CISTPL_CFTABLE_PARITY)
+			printf(" [parity]");
+		if (entry->flags & CISTPL_CFTABLE_WAIT)
+			printf(" [wait]");
+		if (entry->flags & CISTPL_CFTABLE_SERR)
+			printf(" [serr]");
+		if (entry->flags & CISTPL_CFTABLE_FAST_BACK)
+			printf(" [fast back]");
+		if (entry->flags & CISTPL_CFTABLE_BINARY_AUDIO)
+			printf(" [binary audio]");
+		if (entry->flags & CISTPL_CFTABLE_PWM_AUDIO)
+			printf(" [pwm audio]");
+		putchar('\n');
+	}
+
+	if (entry->vcc.present)
+		print_power("Vcc", &entry->vcc);
+	if (entry->vpp1.present)
+		print_power("Vpp1", &entry->vpp1);
+	if (entry->vpp2.present)
+		print_power("Vpp2", &entry->vpp2);
+
+	if (entry->io) {
+		printf("%s  io_base", indent);
+		for (i = 0; i < 8; i++)
+			if (entry->io & (1 << i))
+				printf(" %d", i);
+		putchar('\n');
+	}
+
+	if (entry->irq.IRQInfo1) {
+		printf("%s  irq ", indent);
+		if (entry->irq.IRQInfo1 & IRQ_INFO2_VALID)
+			printf("mask 0x%4.4x", entry->irq.IRQInfo2);
+		else
+			printf("%u", 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]");
+		putchar('\n');
+	}
+
+	if (entry->mem) {
+		printf("%s  mem_base", indent);
+		for (i = 0; i < 8; i++)
+			if (entry->mem & (1 << i))
+				printf(" %d", i);
+		putchar('\n');
+	}
+
+	if (verbose && entry->subtuples)
+		printf("%s  %d bytes in subtuples\n", indent, entry->subtuples);
+
+}
+
+/*====================================================================*/
+
+static void print_jedec(cistpl_jedec_t * j)
+{
+	int i;
+	for (i = 0; i < j->nid; i++) {
+		if (i != 0)
+			putchar(',');
+		printf(" 0x%02x 0x%02x", j->id[i].mfr, j->id[i].info);
+	}
+	putchar('\n');
+}
+
+/*====================================================================*/
+
+static void print_device_geo(cistpl_device_geo_t * geo)
+{
+	int i;
+	for (i = 0; i < geo->ngeo; i++) {
+		printf("%s  width %d erase 0x%x read 0x%x write 0x%x "
+		       "partition 0x%x interleave 0x%x\n", indent,
+		       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);
+	}
+}
+
+/*====================================================================*/
+
+static void print_org(cistpl_org_t * org)
+{
+	printf("%sdata_org ", indent);
+	switch (org->data_org) {
+	case CISTPL_ORG_FS:
+		printf("[filesystem]");
+		break;
+	case CISTPL_ORG_APPSPEC:
+		printf("[app_specific]");
+		break;
+	case CISTPL_ORG_XIP:
+		printf("[code]");
+		break;
+	default:
+		if (org->data_org < 0x80)
+			printf("[reserved]");
+		else
+			printf("[vendor_specific]");
+	}
+	printf(", \"%s\"\n", org->desc);
+}
+
+/*====================================================================*/
+
+static char *data_mod[] = {
+	"Bell103", "V.21", "V.23", "V.22", "Bell212A", "V.22bis",
+	"V.26", "V.26bis", "V.27bis", "V.29", "V.32", "V.32bis",
+	"V.34", "rfu", "rfu", "rfu"
+};
+
+static char *fax_mod[] = {
+	"V.21-C2", "V.27ter", "V.29", "V.17", "V.33", "rfu", "rfu", "rfu"
+};
+
+static char *fax_features[] = {
+	"T.3", "T.4", "T.6", "error", "voice", "poll", "file", "passwd"
+};
+
+static char *cmd_protocol[] = {
+	"AT1", "AT2", "AT3", "MNP_AT", "V.25bis", "V.25A", "DMCL"
+};
+
+static char *uart[] = {
+	"8250", "16450", "16550", "8251", "8530", "85230"
+};
+static char *parity[] = { "space", "mark", "odd", "even" };
+static char *stop[] = { "1", "1.5", "2" };
+
+static char *flow[] = {
+	"XON/XOFF xmit", "XON/XOFF rcv", "hw xmit", "hw rcv", "transparent"
+};
+
+static void print_serial(cistpl_funce_t * funce)
+{
+	cistpl_serial_t *s;
+	cistpl_data_serv_t *ds;
+	cistpl_fax_serv_t *fs;
+	cistpl_modem_cap_t *cp;
+	int i, j;
+
+	switch (funce->type & 0x0f) {
+	case CISTPL_FUNCE_SERIAL_IF:
+	case CISTPL_FUNCE_SERIAL_IF_DATA:
+	case CISTPL_FUNCE_SERIAL_IF_FAX:
+	case CISTPL_FUNCE_SERIAL_IF_VOICE:
+		s = (cistpl_serial_t *) (funce->data);
+		printf("%sserial_interface", indent);
+		if ((funce->type & 0x0f) == CISTPL_FUNCE_SERIAL_IF_DATA)
+			printf("_data");
+		else if ((funce->type & 0x0f) == CISTPL_FUNCE_SERIAL_IF_FAX)
+			printf("_fax");
+		else if ((funce->type & 0x0f) == CISTPL_FUNCE_SERIAL_IF_VOICE)
+			printf("_voice");
+		printf("\n%s  uart %s", indent,
+		       (s->uart_type < 6) ? uart[s->uart_type] : "reserved");
+		if (s->uart_cap_0) {
+			printf(" [");
+			for (i = 0; i < 4; i++)
+				if (s->uart_cap_0 & (1 << i))
+					printf("%s%s", parity[i],
+					       (s->uart_cap_0 >=
+						(2 << i)) ? "/" : "]");
+		}
+		if (s->uart_cap_1) {
+			int m = s->uart_cap_1 & 0x0f;
+			int n = s->uart_cap_1 >> 4;
+			printf(" [");
+			for (i = 0; i < 4; i++)
+				if (m & (1 << i))
+					printf("%d%s", i + 5,
+					       (m >= (2 << i)) ? "/" : "");
+			printf("] [");
+			for (i = 0; i < 3; i++)
+				if (n & (1 << i))
+					printf("%s%s", stop[i],
+					       (n >= (2 << i)) ? "/" : "]");
+		}
+		printf("\n");
+		break;
+	case CISTPL_FUNCE_SERIAL_CAP:
+	case CISTPL_FUNCE_SERIAL_CAP_DATA:
+	case CISTPL_FUNCE_SERIAL_CAP_FAX:
+	case CISTPL_FUNCE_SERIAL_CAP_VOICE:
+		cp = (cistpl_modem_cap_t *) (funce->data);
+		printf("%sserial_modem_cap", indent);
+		if ((funce->type & 0x0f) == CISTPL_FUNCE_SERIAL_CAP_DATA)
+			printf("_data");
+		else if ((funce->type & 0x0f) == CISTPL_FUNCE_SERIAL_CAP_FAX)
+			printf("_fax");
+		else if ((funce->type & 0x0f) == CISTPL_FUNCE_SERIAL_CAP_VOICE)
+			printf("_voice");
+		if (cp->flow) {
+			printf("\n%s  flow", indent);
+			for (i = 0; i < 5; i++)
+				if (cp->flow & (1 << i))
+					printf(" [%s]", flow[i]);
+		}
+		printf("\n%s  cmd_buf %d rcv_buf %d xmit_buf %d\n",
+		       indent, 4 * (cp->cmd_buf + 1),
+		       cp->rcv_buf_0 + (cp->rcv_buf_1 << 8) +
+		       (cp->rcv_buf_2 << 16),
+		       cp->xmit_buf_0 + (cp->xmit_buf_1 << 8) +
+		       (cp->xmit_buf_2 << 16));
+		break;
+	case CISTPL_FUNCE_SERIAL_SERV_DATA:
+		ds = (cistpl_data_serv_t *) (funce->data);
+		printf("%sserial_data_services\n", indent);
+		printf("%s  data_rate %d\n", indent,
+		       75 * ((ds->max_data_0 << 8) + ds->max_data_1));
+		printf("%s  modulation", indent);
+		for (i = j = 0; i < 16; i++)
+			if (((ds->modulation_1 << 8) +
+			     ds->modulation_0) & (1 << i)) {
+				if (++j % 6 == 0)
+					printf("\n%s   ", indent);
+				printf(" [%s]", data_mod[i]);
+			}
+		printf("\n");
+		if (ds->error_control) {
+			printf("%s  error_control", indent);
+			if (ds->error_control & CISTPL_SERIAL_ERR_MNP2_4)
+				printf(" [MNP2-4]");
+			if (ds->error_control & CISTPL_SERIAL_ERR_V42_LAPM)
+				printf(" [V.42/LAPM]");
+			printf("\n");
+		}
+		if (ds->compression) {
+			printf("%s  compression", indent);
+			if (ds->compression & CISTPL_SERIAL_CMPR_V42BIS)
+				printf(" [V.42bis]");
+			if (ds->compression & CISTPL_SERIAL_CMPR_MNP5)
+				printf(" [MNP5]");
+			printf("\n");
+		}
+		if (ds->cmd_protocol) {
+			printf("%s  cmd_protocol", indent);
+			for (i = 0; i < 7; i++)
+				if (ds->cmd_protocol & (1 << i))
+					printf(" [%s]", cmd_protocol[i]);
+			printf("\n");
+		}
+		break;
+
+	case CISTPL_FUNCE_SERIAL_SERV_FAX:
+		fs = (cistpl_fax_serv_t *) (funce->data);
+		printf("%sserial_fax_services [class=%d]\n",
+		       indent, funce->type >> 4);
+		printf("%s  data_rate %d\n", indent,
+		       75 * ((fs->max_data_0 << 8) + fs->max_data_1));
+		printf("%s  modulation", indent);
+		for (i = 0; i < 8; i++)
+			if (fs->modulation & (1 << i))
+				printf(" [%s]", fax_mod[i]);
+		printf("\n");
+		if (fs->features_0) {
+			printf("%s  features", indent);
+			for (i = 0; i < 8; i++)
+				if (fs->features_0 & (1 << i))
+					printf(" [%s]", fax_features[i]);
+			printf("\n");
+		}
+		break;
+	}
+}
+
+/*====================================================================*/
+
+static void print_fixed(cistpl_funce_t * funce)
+{
+	cistpl_ide_interface_t *i;
+	cistpl_ide_feature_t *f;
+
+	switch (funce->type) {
+	case CISTPL_FUNCE_IDE_IFACE:
+		i = (cistpl_ide_interface_t *) (funce->data);
+		printf("%sdisk_interface ", indent);
+		if (i->interface == CISTPL_IDE_INTERFACE)
+			printf("[ide]\n");
+		else
+			printf("[undefined]\n");
+		break;
+	case CISTPL_FUNCE_IDE_MASTER:
+	case CISTPL_FUNCE_IDE_SLAVE:
+		f = (cistpl_ide_feature_t *) (funce->data);
+		printf("%sdisk_features", indent);
+		if (f->feature1 & CISTPL_IDE_SILICON)
+			printf(" [silicon]");
+		else
+			printf(" [rotating]");
+		if (f->feature1 & CISTPL_IDE_UNIQUE)
+			printf(" [unique]");
+		if (f->feature1 & CISTPL_IDE_DUAL)
+			printf(" [dual]");
+		else
+			printf(" [single]");
+		if (f->feature1 && f->feature2)
+			printf("\n%s ", indent);
+		if (f->feature2 & CISTPL_IDE_HAS_SLEEP)
+			printf(" [sleep]");
+		if (f->feature2 & CISTPL_IDE_HAS_STANDBY)
+			printf(" [standby]");
+		if (f->feature2 & CISTPL_IDE_HAS_IDLE)
+			printf(" [idle]");
+		if (f->feature2 & CISTPL_IDE_LOW_POWER)
+			printf(" [low power]");
+		if (f->feature2 & CISTPL_IDE_REG_INHIBIT)
+			printf(" [reg inhibit]");
+		if (f->feature2 & CISTPL_IDE_HAS_INDEX)
+			printf(" [index]");
+		if (f->feature2 & CISTPL_IDE_IOIS16)
+			printf(" [iois16]");
+		putchar('\n');
+		break;
+	}
+}
+
+/*====================================================================*/
+
+static const char *tech[] = {
+	"undefined", "ARCnet", "ethernet", "token_ring", "localtalk",
+	"FDDI/CDDI", "ATM", "wireless"
+};
+
+static const char *media[] = {
+	"undefined", "unshielded_twisted_pair", "shielded_twisted_pair",
+	"thin_coax", "thick_coax", "fiber", "900_MHz", "2.4_GHz",
+	"5.4_GHz", "diffuse_infrared", "point_to_point_infrared"
+};
+
+static void print_network(cistpl_funce_t * funce)
+{
+	cistpl_lan_tech_t *t;
+	cistpl_lan_speed_t *s;
+	cistpl_lan_media_t *m;
+	cistpl_lan_node_id_t *n;
+	cistpl_lan_connector_t *c;
+	int i;
+
+	switch (funce->type) {
+	case CISTPL_FUNCE_LAN_TECH:
+		t = (cistpl_lan_tech_t *) (funce->data);
+		printf("%slan_technology %s\n", indent, tech[t->tech]);
+		break;
+	case CISTPL_FUNCE_LAN_SPEED:
+		s = (cistpl_lan_speed_t *) (funce->data);
+		printf("%slan_speed ", indent);
+		print_speed(s->speed);
+		putchar('\n');
+		break;
+	case CISTPL_FUNCE_LAN_MEDIA:
+		m = (cistpl_lan_media_t *) (funce->data);
+		printf("%slan_media %s\n", indent, media[m->media]);
+		break;
+	case CISTPL_FUNCE_LAN_NODE_ID:
+		n = (cistpl_lan_node_id_t *) (funce->data);
+		printf("%slan_node_id", indent);
+		for (i = 0; i < n->nb; i++)
+			printf(" %02x", n->id[i]);
+		putchar('\n');
+		break;
+	case CISTPL_FUNCE_LAN_CONNECTOR:
+		c = (cistpl_lan_connector_t *) (funce->data);
+		printf("%slan_connector ", indent);
+		if (c->code == 0)
+			printf("Open connector standard\n");
+		else
+			printf("Closed connector standard\n");
+		break;
+	}
+}
+
+/*====================================================================*/
+
+static void print_vers_1(cistpl_vers_1_t * v1)
+{
+	int i, n;
+	char s[32];
+	sprintf(s, "%svers_1 %d.%d", indent, v1->major, v1->minor);
+	printf("%s", s);
+	n = strlen(s);
+	for (i = 0; i < v1->ns; i++) {
+		if (n + strlen(v1->str + v1->ofs[i]) + 4 > 72) {
+			n = strlen(indent) + 2;
+			printf(",\n%s  ", indent);
+		} else {
+			printf(", ");
+			n += 2;
+		}
+		printf("\"%s\"", v1->str + v1->ofs[i]);
+		n += strlen(v1->str + v1->ofs[i]) + 2;
+	}
+	putchar('\n');
+}
+
+/*====================================================================*/
+
+static void print_vers_2(cistpl_vers_2_t * v2)
+{
+	printf("%sversion 0x%2.2x, compliance 0x%2.2x, dindex 0x%4.4x\n",
+	       indent, v2->vers, v2->comply, v2->dindex);
+	printf("%s  vspec8 0x%2.2x, vspec9 0x%2.2x, nhdr %d\n",
+	       indent, v2->vspec8, v2->vspec9, v2->nhdr);
+	printf("%s  vendor \"%s\"\n", indent, v2->str + v2->vendor);
+	printf("%s  info \"%s\"\n", indent, v2->str + v2->info);
+}
+
+/*====================================================================*/
+
+#ifdef CISTPL_FORMAT_DISK
+static void print_format(cistpl_format_t * fmt)
+{
+	if (fmt->type == CISTPL_FORMAT_DISK)
+		printf("%s  [disk]", indent);
+	else if (fmt->type == CISTPL_FORMAT_MEM)
+		printf("%s  [memory]", indent);
+	else
+		printf("%s  [type 0x%02x]\n", indent, fmt->type);
+	if (fmt->edc == CISTPL_EDC_NONE)
+		printf(" [no edc]");
+	else if (fmt->edc == CISTPL_EDC_CKSUM)
+		printf(" [cksum]");
+	else if (fmt->edc == CISTPL_EDC_CRC)
+		printf(" [crc]");
+	else if (fmt->edc == CISTPL_EDC_PCC)
+		printf(" [pcc]");
+	else
+		printf(" [edc 0x%02x]", fmt->edc);
+	printf(" offset 0x%04x length ", fmt->offset);
+	print_size(fmt->length);
+	putchar('\n');
+}
+#endif
+
+/*====================================================================*/
+
+static void print_config(int code, cistpl_config_t * cfg)
+{
+	printf("%sconfig%s base 0x%4.4x", indent,
+	       (code == CISTPL_CONFIG_CB) ? "_cb" : "", cfg->base);
+	if (code == CISTPL_CONFIG)
+		printf(" mask 0x%4.4x", cfg->rmask[0]);
+	printf(" last_index 0x%2.2x\n", cfg->last_idx);
+	if (verbose && cfg->subtuples)
+		printf("%s  %d bytes in subtuples\n", indent, cfg->subtuples);
+}
+
+/*====================================================================*/
+
+static int nfn = 0, cur = 0;
+
+static void print_parse(tuple_parse_t * tup)
+{
+	static int func = 0;
+	int i;
+
+	switch (tup->tuple.TupleCode) {
+	case CISTPL_DEVICE:
+	case CISTPL_DEVICE_A:
+		if (tup->tuple.TupleCode == CISTPL_DEVICE)
+			printf("%sdev_info\n", indent);
+		else
+			printf("%sattr_dev_info\n", indent);
+		print_device(&tup->parse.device);
+		break;
+	case CISTPL_CHECKSUM:
+		printf("%schecksum 0x%04x-0x%04x = 0x%02x\n",
+		       indent, tup->parse.checksum.addr,
+		       tup->parse.checksum.addr + tup->parse.checksum.len - 1,
+		       tup->parse.checksum.sum);
+		break;
+	case CISTPL_LONGLINK_A:
+		if (verbose)
+			printf("%slong_link_attr 0x%04x\n", indent,
+			       tup->parse.longlink.addr);
+		break;
+	case CISTPL_LONGLINK_C:
+		if (verbose)
+			printf("%slong_link 0x%04x\n", indent,
+			       tup->parse.longlink.addr);
+		break;
+	case CISTPL_LONGLINK_MFC:
+		if (verbose) {
+			printf("%smfc_long_link\n", indent);
+			for (i = 0; i < tup->parse.longlink_mfc.nfn; i++)
+				printf("%s function %d: %s 0x%04x\n", indent, i,
+				       tup->parse.longlink_mfc.fn[i].space ?
+				       "common" : "attr",
+				       tup->parse.longlink_mfc.fn[i].addr);
+		}
+
+		printf("%smfc {\n", indent);
+		nfn = tup->parse.longlink_mfc.nfn;
+		cur = 0;
+		strcat(indent, "  ");
+		while (cur < nfn) {
+			tuple_parse_t tuple_parse = *tup;
+			tuple_parse.tuple.CISOffset =
+			    tup->parse.longlink_mfc.fn[cur].addr;
+			tuple_parse.tuple.TupleLink = 0;
+
+			while (1) {
+				if (get_tuple_buf(-1, &tuple_parse.tuple, 0))
+					break;
+				if (verbose)
+					print_tuple(&tuple_parse);
+				if (parse_tuple
+				    (&tuple_parse.tuple,
+				     &tuple_parse.parse) == 0)
+					print_parse(&tuple_parse);
+				else
+					printf("%sparse error\n", indent);
+				if (verbose)
+					putchar('\n');
+				if (tuple_parse.tuple.TupleCode == CISTPL_END)
+					break;
+			}
+		}
+		break;
+	case CISTPL_NO_LINK:
+		if (verbose)
+			printf("%sno_long_link\n", indent);
+		break;
+#ifdef CISTPL_INDIRECT
+	case CISTPL_INDIRECT:
+		if (verbose)
+			printf("%sindirect_access\n", indent);
+		break;
+#endif
+	case CISTPL_LINKTARGET:
+		if (verbose)
+			printf("%slink_target\n", indent);
+
+		if (cur++)
+			printf("%s}, {\n", indent + 2);
+		break;
+	case CISTPL_VERS_1:
+		print_vers_1(&tup->parse.version_1);
+		break;
+	case CISTPL_ALTSTR:
+		break;
+	case CISTPL_JEDEC_A:
+	case CISTPL_JEDEC_C:
+		if (tup->tuple.TupleCode == CISTPL_JEDEC_C)
+			printf("%scommon_jedec", indent);
+		else
+			printf("%sattr_jedec", indent);
+		print_jedec(&tup->parse.jedec);
+		break;
+	case CISTPL_DEVICE_GEO:
+	case CISTPL_DEVICE_GEO_A:
+		if (tup->tuple.TupleCode == CISTPL_DEVICE_GEO)
+			printf("%scommon_geometry\n", indent);
+		else
+			printf("%sattr_geometry\n", indent);
+		print_device_geo(&tup->parse.device_geo);
+		break;
+	case CISTPL_MANFID:
+		printf("%smanfid 0x%4.4x, 0x%4.4x\n", indent,
+		       tup->parse.manfid.manf, tup->parse.manfid.card);
+		break;
+	case CISTPL_FUNCID:
+		print_funcid(&tup->parse.funcid);
+		func = tup->parse.funcid.func;
+		break;
+	case CISTPL_FUNCE:
+		switch (func) {
+		case CISTPL_FUNCID_SERIAL:
+			print_serial(&tup->parse.funce);
+			break;
+		case CISTPL_FUNCID_FIXED:
+			print_fixed(&tup->parse.funce);
+			break;
+		case CISTPL_FUNCID_NETWORK:
+			print_network(&tup->parse.funce);
+			break;
+		}
+		break;
+	case CISTPL_BAR:
+		printf("%sBAR %d size ", indent,
+		       tup->parse.bar.attr & CISTPL_BAR_SPACE);
+		print_size(tup->parse.bar.size);
+		if (tup->parse.bar.attr & CISTPL_BAR_SPACE_IO)
+			printf(" [io]");
+		else
+			printf(" [mem]");
+		if (tup->parse.bar.attr & CISTPL_BAR_PREFETCH)
+			printf(" [prefetch]");
+		if (tup->parse.bar.attr & CISTPL_BAR_CACHEABLE)
+			printf(" [cacheable]");
+		if (tup->parse.bar.attr & CISTPL_BAR_1MEG_MAP)
+			printf(" [<1mb]");
+		putchar('\n');
+		break;
+	case CISTPL_CONFIG:
+	case CISTPL_CONFIG_CB:
+		print_config(tup->tuple.TupleCode, &tup->parse.config);
+		break;
+	case CISTPL_CFTABLE_ENTRY:
+		print_cftable_entry(&tup->parse.cftable_entry);
+		break;
+	case CISTPL_CFTABLE_ENTRY_CB:
+		print_cftable_entry_cb(&tup->parse.cftable_entry_cb);
+		break;
+	case CISTPL_VERS_2:
+		print_vers_2(&tup->parse.vers_2);
+		break;
+	case CISTPL_ORG:
+		print_org(&tup->parse.org);
+		break;
+#ifdef CISTPL_FORMAT_DISK
+	case CISTPL_FORMAT:
+	case CISTPL_FORMAT_A:
+		if (tup->tuple.TupleCode == CISTPL_FORMAT)
+			printf("%scommon_format\n", indent);
+		else
+			printf("%sattr_format\n", indent);
+		print_format(&tup->parse.format);
+#endif
+	}
+}
+
+/*====================================================================*/
+
+static int get_tuple_buf(int fd, tuple_t * tuple, int first)
+{
+	u_int ofs;
+	static int nb = 0;
+	static u_char buf[1024];
+
+	if (first) {
+		nb = read(fd, buf, sizeof(buf));
+		tuple->TupleLink = tuple->CISOffset = 0;
+	}
+	ofs = tuple->CISOffset + tuple->TupleLink;
+	if (ofs >= nb)
+		return -1;
+	tuple->TupleCode = buf[ofs++];
+	tuple->TupleDataLen = tuple->TupleLink = buf[ofs++];
+	tuple->CISOffset = ofs;
+	memcpy(tuple->TupleData, buf + ofs, tuple->TupleLink);
+	return 0;
+}
+
+/*======================================================================
+
+	Parsing routines for individual tuples
+
+======================================================================*/
+
+static const u_char mantissa[] = {
+	10, 12, 13, 15, 20, 25, 30, 35,
+	40, 45, 50, 55, 60, 70, 80, 90
+};
+
+static const u_int exponent[] = {
+	1, 10, 100, 1000, 10000, 100000, 1000000, 10000000
+};
+
+/* Convert an extended speed byte to a time in nanoseconds */
+#define SPEED_CVT(v) \
+	(mantissa[(((v)>>3)&15)-1] * exponent[(v)&7] / 10)
+/* Convert a power byte to a current in 0.1 microamps */
+#define POWER_CVT(v) \
+	(mantissa[((v)>>3)&15] * exponent[(v)&7] / 10)
+#define POWER_SCALE(v)				(exponent[(v)&7])
+
+static int parse_device(tuple_t * tuple, cistpl_device_t * device)
+{
+	int i;
+	u_char scale;
+	u_char *p, *q;
+
+	p = (u_char *) tuple->TupleData;
+	q = p + tuple->TupleDataLen;
+
+	device->ndev = 0;
+	for (i = 0; i < CISTPL_MAX_DEVICES; i++) {
+
+		if (*p == 0xff)
+			break;
+		device->dev[i].type = (*p >> 4);
+		device->dev[i].wp = (*p & 0x08) ? 1 : 0;
+		switch (*p & 0x07) {
+		case 0:
+			device->dev[i].speed = 0;
+			break;
+		case 1:
+			device->dev[i].speed = 250;
+			break;
+		case 2:
+			device->dev[i].speed = 200;
+			break;
+		case 3:
+			device->dev[i].speed = 150;
+			break;
+		case 4:
+			device->dev[i].speed = 100;
+			break;
+		case 7:
+			if (++p == q)
+				return -1;
+			if (p == q)
+				return -1;
+			device->dev[i].speed = SPEED_CVT(*p);
+			while (*p & 0x80)
+				if (++p == q)
+					return -1;
+			break;
+		default:
+			return -1;
+		}
+
+		if (++p == q)
+			return -1;
+		if (*p == 0xff)
+			break;
+		scale = *p & 7;
+		if (scale == 7)
+			return -1;
+		device->dev[i].size = ((*p >> 3) + 1) * (512 << (scale * 2));
+		device->ndev++;
+		if (++p == q)
+			break;
+	}
+
+	return 0;
+}
+
+/*====================================================================*/
+
+static int parse_checksum(tuple_t * tuple, cistpl_checksum_t * csum)
+{
+	u_char *p;
+	if (tuple->TupleDataLen < 5)
+		return -1;
+	p = (u_char *) tuple->TupleData;
+	csum->addr = tuple->CISOffset + (short)le16toh(*(u_short *) p) - 2;
+	csum->len = le16toh(*(u_short *) (p + 2));
+	csum->sum = *(p + 4);
+	return 0;
+}
+
+/*====================================================================*/
+
+static int parse_longlink(tuple_t * tuple, cistpl_longlink_t * link)
+{
+	if (tuple->TupleDataLen < 4)
+		return -1;
+	link->addr = le32toh(*(u_int *) tuple->TupleData);
+	return 0;
+}
+
+/*====================================================================*/
+
+static int parse_longlink_mfc(tuple_t * tuple, cistpl_longlink_mfc_t * link)
+{
+	u_char *p;
+	int i;
+
+	p = (u_char *) tuple->TupleData;
+
+	link->nfn = *p;
+	p++;
+	if (tuple->TupleDataLen <= link->nfn * 5)
+		return -1;
+	for (i = 0; i < link->nfn; i++) {
+		link->fn[i].space = *p;
+		p++;
+		link->fn[i].addr = le32toh(*(u_int *) p);
+		p += 4;
+	}
+	return 0;
+}
+
+/*====================================================================*/
+
+static int parse_strings(u_char * p, u_char * q, int max,
+			 char *s, u_char * ofs, u_char * found)
+{
+	int i, j, ns;
+
+	if (p == q)
+		return -1;
+	ns = 0;
+	j = 0;
+	for (i = 0; i < max; i++) {
+		if (*p == 0xff)
+			break;
+		ofs[i] = j;
+		ns++;
+		for (;;) {
+			s[j++] = (*p == 0xff) ? '\0' : *p;
+			if ((*p == '\0') || (*p == 0xff))
+				break;
+			if (++p == q)
+				return -1;
+		}
+		if ((*p == 0xff) || (++p == q))
+			break;
+	}
+	if (found) {
+		*found = ns;
+		return 0;
+	} else {
+		return (ns == max) ? 0 : -1;
+	}
+}
+
+/*====================================================================*/
+
+static int parse_vers_1(tuple_t * tuple, cistpl_vers_1_t * vers_1)
+{
+	u_char *p, *q;
+
+	p = (u_char *) tuple->TupleData;
+	q = p + tuple->TupleDataLen;
+
+	vers_1->major = *p;
+	p++;
+	vers_1->minor = *p;
+	p++;
+	if (p >= q)
+		return -1;
+
+	return parse_strings(p, q, CISTPL_VERS_1_MAX_PROD_STRINGS,
+			     vers_1->str, vers_1->ofs, &vers_1->ns);
+}
+
+/*====================================================================*/
+
+static int parse_altstr(tuple_t * tuple, cistpl_altstr_t * altstr)
+{
+	u_char *p, *q;
+
+	p = (u_char *) tuple->TupleData;
+	q = p + tuple->TupleDataLen;
+
+	return parse_strings(p, q, CISTPL_MAX_ALTSTR_STRINGS,
+			     altstr->str, altstr->ofs, &altstr->ns);
+}
+
+/*====================================================================*/
+
+static int parse_jedec(tuple_t * tuple, cistpl_jedec_t * jedec)
+{
+	u_char *p, *q;
+	int nid;
+
+	p = (u_char *) tuple->TupleData;
+	q = p + tuple->TupleDataLen;
+
+	for (nid = 0; nid < CISTPL_MAX_DEVICES; nid++) {
+		if (p > q - 2)
+			break;
+		jedec->id[nid].mfr = p[0];
+		jedec->id[nid].info = p[1];
+		p += 2;
+	}
+	jedec->nid = nid;
+	return 0;
+}
+
+/*====================================================================*/
+
+static int parse_manfid(tuple_t * tuple, cistpl_manfid_t * m)
+{
+	u_short *p;
+	if (tuple->TupleDataLen < 4)
+		return -1;
+	p = (u_short *) tuple->TupleData;
+	m->manf = le16toh(p[0]);
+	m->card = le16toh(p[1]);
+	return 0;
+}
+
+/*====================================================================*/
+
+static int parse_funcid(tuple_t * tuple, cistpl_funcid_t * f)
+{
+	u_char *p;
+	if (tuple->TupleDataLen < 2)
+		return -1;
+	p = (u_char *) tuple->TupleData;
+	f->func = p[0];
+	f->sysinit = p[1];
+	return 0;
+}
+
+/*====================================================================*/
+
+static int parse_funce(tuple_t * tuple, cistpl_funce_t * f)
+{
+	u_char *p;
+	int i;
+	if (tuple->TupleDataLen < 1)
+		return -1;
+	p = (u_char *) tuple->TupleData;
+	f->type = p[0];
+	for (i = 1; i < tuple->TupleDataLen; i++)
+		f->data[i - 1] = p[i];
+	return 0;
+}
+
+/*====================================================================*/
+
+static int parse_config(tuple_t * tuple, cistpl_config_t * config)
+{
+	int rasz, rmsz, i;
+	u_char *p;
+
+	p = (u_char *) tuple->TupleData;
+	rasz = *p & 0x03;
+	rmsz = (*p & 0x3c) >> 2;
+	if (tuple->TupleDataLen < rasz + rmsz + 4)
+		return -1;
+	config->last_idx = *(++p);
+	p++;
+	config->base = 0;
+	for (i = 0; i <= rasz; i++)
+		config->base += p[i] << (8 * i);
+	p += rasz + 1;
+	for (i = 0; i < 4; i++)
+		config->rmask[i] = 0;
+	for (i = 0; i <= rmsz; i++)
+		config->rmask[i >> 2] += p[i] << (8 * (i % 4));
+	config->subtuples = tuple->TupleDataLen - (rasz + rmsz + 4);
+	return 0;
+}
+
+/*======================================================================
+
+	The following routines are all used to parse the nightmarish
+	config table entries.
+
+======================================================================*/
+
+static u_char *parse_power(u_char * p, u_char * q, cistpl_power_t * pwr)
+{
+	int i;
+	u_int scale;
+
+	if (p == q)
+		return NULL;
+	pwr->present = *p;
+	pwr->flags = 0;
+	p++;
+	for (i = 0; i < 7; i++)
+		if (pwr->present & (1 << i)) {
+			if (p == q)
+				return NULL;
+			pwr->param[i] = POWER_CVT(*p);
+			scale = POWER_SCALE(*p);
+			while (*p & 0x80) {
+				if (++p == q)
+					return NULL;
+				if ((*p & 0x7f) < 100)
+					pwr->param[i] +=
+					    (*p & 0x7f) * scale / 100;
+				else if (*p == 0x7d)
+					pwr->flags |= CISTPL_POWER_HIGHZ_OK;
+				else if (*p == 0x7e)
+					pwr->param[i] = 0;
+				else if (*p == 0x7f)
+					pwr->flags |= CISTPL_POWER_HIGHZ_REQ;
+				else
+					return NULL;
+			}
+			p++;
+		}
+	return p;
+}
+
+/*====================================================================*/
+
+static u_char *parse_timing(u_char * p, u_char * q, cistpl_timing_t * timing)
+{
+	u_char scale;
+
+	if (p == q)
+		return NULL;
+	scale = *p;
+	if ((scale & 3) != 3) {
+		if (++p == q)
+			return NULL;
+		timing->wait = SPEED_CVT(*p);
+		timing->waitscale = exponent[scale & 3];
+	} else
+		timing->wait = 0;
+	scale >>= 2;
+	if ((scale & 7) != 7) {
+		if (++p == q)
+			return NULL;
+		timing->ready = SPEED_CVT(*p);
+		timing->rdyscale = exponent[scale & 7];
+	} else
+		timing->ready = 0;
+	scale >>= 3;
+	if (scale != 7) {
+		if (++p == q)
+			return NULL;
+		timing->reserved = SPEED_CVT(*p);
+		timing->rsvscale = exponent[scale];
+	} else
+		timing->reserved = 0;
+	p++;
+	return p;
+}
+
+/*====================================================================*/
+
+static u_char *parse_io(u_char * p, u_char * q, cistpl_io_t * io)
+{
+	int i, j, bsz, lsz;
+
+	if (p == q)
+		return NULL;
+	io->flags = *p;
+
+	if (!(*p & 0x80)) {
+		io->nwin = 1;
+		io->win[0].base = 0;
+		io->win[0].len = (1 << (io->flags & CISTPL_IO_LINES_MASK));
+		return p + 1;
+	}
+
+	if (++p == q)
+		return NULL;
+	io->nwin = (*p & 0x0f) + 1;
+	bsz = (*p & 0x30) >> 4;
+	if (bsz == 3)
+		bsz++;
+	lsz = (*p & 0xc0) >> 6;
+	if (lsz == 3)
+		lsz++;
+	p++;
+
+	for (i = 0; i < io->nwin; i++) {
+		io->win[i].base = 0;
+		io->win[i].len = 1;
+		for (j = 0; j < bsz; j++, p++) {
+			if (p == q)
+				return NULL;
+			io->win[i].base += *p << (j * 8);
+		}
+		for (j = 0; j < lsz; j++, p++) {
+			if (p == q)
+				return NULL;
+			io->win[i].len += *p << (j * 8);
+		}
+	}
+	return p;
+}
+
+/*====================================================================*/
+
+static u_char *parse_mem(u_char * p, u_char * q, cistpl_mem_t * mem)
+{
+	int i, j, asz, lsz, has_ha;
+	u_int len, ca, ha;
+
+	if (p == q)
+		return NULL;
+
+	mem->nwin = (*p & 0x07) + 1;
+	lsz = (*p & 0x18) >> 3;
+	asz = (*p & 0x60) >> 5;
+	has_ha = (*p & 0x80);
+	if (++p == q)
+		return NULL;
+
+	for (i = 0; i < mem->nwin; i++) {
+		len = ca = ha = 0;
+		for (j = 0; j < lsz; j++, p++) {
+			if (p == q)
+				return NULL;
+			len += *p << (j * 8);
+		}
+		for (j = 0; j < asz; j++, p++) {
+			if (p == q)
+				return NULL;
+			ca += *p << (j * 8);
+		}
+		if (has_ha)
+			for (j = 0; j < asz; j++, p++) {
+				if (p == q)
+					return NULL;
+				ha += *p << (j * 8);
+			}
+		mem->win[i].len = len << 8;
+		mem->win[i].card_addr = ca << 8;
+		mem->win[i].host_addr = ha << 8;
+	}
+	return p;
+}
+
+/*====================================================================*/
+
+static u_char *parse_irq(u_char * p, u_char * q, cistpl_irq_t * irq)
+{
+	if (p == q)
+		return NULL;
+	irq->IRQInfo1 = *p;
+	p++;
+	if (irq->IRQInfo1 & IRQ_INFO2_VALID) {
+		if (p + 2 > q)
+			return NULL;
+		irq->IRQInfo2 = (p[1] << 8) + p[0];
+		p += 2;
+	}
+	return p;
+}
+
+/*====================================================================*/
+
+static int parse_cftable_entry(tuple_t * tuple, cistpl_cftable_entry_t * entry)
+{
+	u_char *p, *q, features;
+
+	p = tuple->TupleData;
+	q = p + tuple->TupleDataLen;
+	entry->index = *p & 0x3f;
+	entry->flags = 0;
+	if (*p & 0x40)
+		entry->flags |= CISTPL_CFTABLE_DEFAULT;
+	if (*p & 0x80) {
+		if (++p == q)
+			return -1;
+		if (*p & 0x10)
+			entry->flags |= CISTPL_CFTABLE_BVDS;
+		if (*p & 0x20)
+			entry->flags |= CISTPL_CFTABLE_WP;
+		if (*p & 0x40)
+			entry->flags |= CISTPL_CFTABLE_RDYBSY;
+		if (*p & 0x80)
+			entry->flags |= CISTPL_CFTABLE_MWAIT;
+		entry->interface = *p & 0x0f;
+	} else
+		entry->interface = 0;
+
+	/* Process optional features */
+	if (++p == q)
+		return -1;
+	features = *p;
+	p++;
+
+	/* Power options */
+	if ((features & 3) > 0) {
+		p = parse_power(p, q, &entry->vcc);
+		if (p == NULL)
+			return -1;
+	} else
+		entry->vcc.present = 0;
+	if ((features & 3) > 1) {
+		p = parse_power(p, q, &entry->vpp1);
+		if (p == NULL)
+			return -1;
+	} else
+		entry->vpp1.present = 0;
+	if ((features & 3) > 2) {
+		p = parse_power(p, q, &entry->vpp2);
+		if (p == NULL)
+			return -1;
+	} else
+		entry->vpp2.present = 0;
+
+	/* Timing options */
+	if (features & 0x04) {
+		p = parse_timing(p, q, &entry->timing);
+		if (p == NULL)
+			return -1;
+	} else {
+		entry->timing.wait = 0;
+		entry->timing.ready = 0;
+		entry->timing.reserved = 0;
+	}
+
+	/* I/O window options */
+	if (features & 0x08) {
+		p = parse_io(p, q, &entry->io);
+		if (p == NULL)
+			return -1;
+	} else
+		entry->io.nwin = 0;
+
+	/* Interrupt options */
+	if (features & 0x10) {
+		p = parse_irq(p, q, &entry->irq);
+		if (p == NULL)
+			return -1;
+	} else
+		entry->irq.IRQInfo1 = 0;
+
+	switch (features & 0x60) {
+	case 0x00:
+		entry->mem.nwin = 0;
+		break;
+	case 0x20:
+		entry->mem.nwin = 1;
+		entry->mem.win[0].len = le16toh(*(u_short *) p) << 8;
+		entry->mem.win[0].card_addr = 0;
+		entry->mem.win[0].host_addr = 0;
+		p += 2;
+		if (p > q)
+			return -1;
+		break;
+	case 0x40:
+		entry->mem.nwin = 1;
+		entry->mem.win[0].len = le16toh(*(u_short *) p) << 8;
+		entry->mem.win[0].card_addr =
+		    le16toh(*(u_short *) (p + 2)) << 8;
+		entry->mem.win[0].host_addr = 0;
+		p += 4;
+		if (p > q)
+			return -1;
+		break;
+	case 0x60:
+		p = parse_mem(p, q, &entry->mem);
+		if (p == NULL)
+			return -1;
+		break;
+	}
+
+	/* Misc features */
+	if (features & 0x80) {
+		if (p == q)
+			return -1;
+		entry->flags |= (*p << 8);
+		while (*p & 0x80)
+			if (++p == q)
+				return -1;
+		p++;
+	}
+
+	entry->subtuples = q - p;
+
+	return 0;
+}
+
+/*====================================================================*/
+
+static int parse_bar(tuple_t * tuple, cistpl_bar_t * bar)
+{
+	u_char *p;
+	if (tuple->TupleDataLen < 6)
+		return -1;
+	p = (u_char *) tuple->TupleData;
+	bar->attr = *p;
+	p += 2;
+	bar->size = le32toh(*(u_int *) p);
+	return 0;
+}
+
+static int parse_config_cb(tuple_t * tuple, cistpl_config_t * config)
+{
+	u_char *p;
+
+	p = (u_char *) tuple->TupleData;
+	if ((*p != 3) || (tuple->TupleDataLen < 6))
+		return -1;
+	config->last_idx = *(++p);
+	p++;
+	config->base = le32toh(*(u_int *) p);
+	config->subtuples = tuple->TupleDataLen - 6;
+	return 0;
+}
+
+static int parse_cftable_entry_cb(tuple_t * tuple,
+				  cistpl_cftable_entry_cb_t * entry)
+{
+	u_char *p, *q, features;
+
+	p = tuple->TupleData;
+	q = p + tuple->TupleDataLen;
+	entry->index = *p & 0x3f;
+	entry->flags = 0;
+	if (*p & 0x40)
+		entry->flags |= CISTPL_CFTABLE_DEFAULT;
+
+	/* Process optional features */
+	if (++p == q)
+		return -1;
+	features = *p;
+	p++;
+
+	/* Power options */
+	if ((features & 3) > 0) {
+		p = parse_power(p, q, &entry->vcc);
+		if (p == NULL)
+			return -1;
+	} else
+		entry->vcc.present = 0;
+	if ((features & 3) > 1) {
+		p = parse_power(p, q, &entry->vpp1);
+		if (p == NULL)
+			return -1;
+	} else
+		entry->vpp1.present = 0;
+	if ((features & 3) > 2) {
+		p = parse_power(p, q, &entry->vpp2);
+		if (p == NULL)
+			return -1;
+	} else
+		entry->vpp2.present = 0;
+
+	/* I/O window options */
+	if (features & 0x08) {
+		if (p == q)
+			return -1;
+		entry->io = *p;
+		p++;
+	} else
+		entry->io = 0;
+
+	/* Interrupt options */
+	if (features & 0x10) {
+		p = parse_irq(p, q, &entry->irq);
+		if (p == NULL)
+			return -1;
+	} else
+		entry->irq.IRQInfo1 = 0;
+
+	if (features & 0x20) {
+		if (p == q)
+			return -1;
+		entry->mem = *p;
+		p++;
+	} else
+		entry->mem = 0;
+
+	/* Misc features */
+	if (features & 0x80) {
+		if (p == q)
+			return -1;
+		entry->flags |= (*p << 8);
+		if (*p & 0x80) {
+			if (++p == q)
+				return -1;
+			entry->flags |= (*p << 16);
+		}
+		while (*p & 0x80)
+			if (++p == q)
+				return -1;
+		p++;
+	}
+
+	entry->subtuples = q - p;
+
+	return 0;
+}
+
+/*====================================================================*/
+
+static int parse_device_geo(tuple_t * tuple, cistpl_device_geo_t * geo)
+{
+	u_char *p, *q;
+	int n;
+
+	p = (u_char *) tuple->TupleData;
+	q = p + tuple->TupleDataLen;
+
+	for (n = 0; n < CISTPL_MAX_DEVICES; n++) {
+		if (p > q - 6)
+			break;
+		geo->geo[n].buswidth = p[0];
+		geo->geo[n].erase_block = 1 << (p[1] - 1);
+		geo->geo[n].read_block = 1 << (p[2] - 1);
+		geo->geo[n].write_block = 1 << (p[3] - 1);
+		geo->geo[n].partition = 1 << (p[4] - 1);
+		geo->geo[n].interleave = 1 << (p[5] - 1);
+		p += 6;
+	}
+	geo->ngeo = n;
+	return 0;
+}
+
+/*====================================================================*/
+
+static int parse_vers_2(tuple_t * tuple, cistpl_vers_2_t * v2)
+{
+	u_char *p, *q;
+
+	if (tuple->TupleDataLen < 10)
+		return -1;
+
+	p = tuple->TupleData;
+	q = p + tuple->TupleDataLen;
+
+	v2->vers = p[0];
+	v2->comply = p[1];
+	v2->dindex = le16toh(*(u_short *) (p + 2));
+	v2->vspec8 = p[6];
+	v2->vspec9 = p[7];
+	v2->nhdr = p[8];
+	p += 9;
+	return parse_strings(p, q, 2, v2->str, &v2->vendor, NULL);
+}
+
+/*====================================================================*/
+
+static int parse_org(tuple_t * tuple, cistpl_org_t * org)
+{
+	u_char *p, *q;
+	int i;
+
+	p = tuple->TupleData;
+	q = p + tuple->TupleDataLen;
+	if (p == q)
+		return -1;
+	org->data_org = *p;
+	if (++p == q)
+		return -1;
+	for (i = 0; i < 30; i++) {
+		org->desc[i] = *p;
+		if (*p == '\0')
+			break;
+		if (++p == q)
+			return -1;
+	}
+	return 0;
+}
+
+/*====================================================================*/
+
+static int parse_format(tuple_t * tuple, cistpl_format_t * fmt)
+{
+	u_char *p;
+
+	if (tuple->TupleDataLen < 10)
+		return -1;
+
+	p = tuple->TupleData;
+
+	fmt->type = p[0];
+	fmt->edc = p[1];
+	fmt->offset = le32toh(*(u_int *) (p + 2));
+	fmt->length = le32toh(*(u_int *) (p + 6));
+
+	return 0;
+}
+
+/*====================================================================*/
+
+static int parse_tuple(tuple_t * tuple, cisparse_t * parse)
+{
+	int ret = 0;
+
+	if (tuple->TupleDataLen > tuple->TupleDataMax)
+		return -1;
+	switch (tuple->TupleCode) {
+	case CISTPL_DEVICE:
+	case CISTPL_DEVICE_A:
+		ret = parse_device(tuple, &parse->device);
+		break;
+	case CISTPL_BAR:
+		ret = parse_bar(tuple, &parse->bar);
+		break;
+	case CISTPL_CONFIG_CB:
+		ret = parse_config_cb(tuple, &parse->config);
+		break;
+	case CISTPL_CFTABLE_ENTRY_CB:
+		ret = parse_cftable_entry_cb(tuple, &parse->cftable_entry_cb);
+		break;
+	case CISTPL_CHECKSUM:
+		ret = parse_checksum(tuple, &parse->checksum);
+		break;
+	case CISTPL_LONGLINK_A:
+	case CISTPL_LONGLINK_C:
+		ret = parse_longlink(tuple, &parse->longlink);
+		break;
+	case CISTPL_LONGLINK_MFC:
+		ret = parse_longlink_mfc(tuple, &parse->longlink_mfc);
+		break;
+	case CISTPL_VERS_1:
+		ret = parse_vers_1(tuple, &parse->version_1);
+		break;
+	case CISTPL_ALTSTR:
+		ret = parse_altstr(tuple, &parse->altstr);
+		break;
+	case CISTPL_JEDEC_A:
+	case CISTPL_JEDEC_C:
+		ret = parse_jedec(tuple, &parse->jedec);
+		break;
+	case CISTPL_MANFID:
+		ret = parse_manfid(tuple, &parse->manfid);
+		break;
+	case CISTPL_FUNCID:
+		ret = parse_funcid(tuple, &parse->funcid);
+		break;
+	case CISTPL_FUNCE:
+		ret = parse_funce(tuple, &parse->funce);
+		break;
+	case CISTPL_CONFIG:
+		ret = parse_config(tuple, &parse->config);
+		break;
+	case CISTPL_CFTABLE_ENTRY:
+		ret = parse_cftable_entry(tuple, &parse->cftable_entry);
+		break;
+	case CISTPL_DEVICE_GEO:
+	case CISTPL_DEVICE_GEO_A:
+		ret = parse_device_geo(tuple, &parse->device_geo);
+		break;
+	case CISTPL_VERS_2:
+		ret = parse_vers_2(tuple, &parse->vers_2);
+		break;
+	case CISTPL_ORG:
+		ret = parse_org(tuple, &parse->org);
+		break;
+	case CISTPL_FORMAT:
+	case CISTPL_FORMAT_A:
+		ret = parse_format(tuple, &parse->format);
+		break;
+	case CISTPL_NO_LINK:
+	case CISTPL_LINKTARGET:
+		ret = 0;
+		break;
+	case CISTPL_END:
+		ret = 0;
+		break;
+	default:
+		ret = -1;
+		break;
+	}
+	return ret;
+}
+
+/*====================================================================*/
+
+#define MAX_SOCKS 8
+
+int main(int argc, char *argv[])
+{
+	int i, fd;
+	tuple_parse_t tuple_parse;
+	int optch, errflg, first;
+	char *infile = NULL;
+
+	errflg = 0;
+	while ((optch = getopt(argc, argv, "vi:")) != -1) {
+		switch (optch) {
+		case 'v':
+			verbose = 1;
+			break;
+		default:
+			errflg = 1;
+			break;
+		}
+	}
+	if (errflg || (optind > argc - 1)) {
+		fprintf(stderr, "usage: %s [-v] infile\n", argv[0]);
+		exit(EXIT_FAILURE);
+	}
+
+	for (i = 0; optind + i < argc; i++) {
+		infile = argv[optind + i];
+
+		nfn = cur = 0;
+		indent[0] = '\0';
+		fd = open(infile, O_RDONLY);
+		if (fd < 0) {
+			perror("open()");
+			return -1;
+		}
+
+		if (!verbose && (i > 0))
+			putchar('\n');
+
+		tuple_parse.tuple.TupleDataMax = sizeof(tuple_parse.data);
+		tuple_parse.tuple.Attributes =
+		    TUPLE_RETURN_LINK | TUPLE_RETURN_COMMON;
+		tuple_parse.tuple.DesiredTuple = RETURN_FIRST_TUPLE;
+		tuple_parse.tuple.TupleOffset = 0;
+		tuple_parse.tuple.TupleData = tuple_parse.data;
+
+		for (first = 1;; first = 0) {
+			if (get_tuple_buf(fd, &tuple_parse.tuple, first) != 0)
+				break;
+			if (verbose)
+				print_tuple(&tuple_parse);
+			if (parse_tuple(&tuple_parse.tuple, &tuple_parse.parse))
+				printf("%sparse error\n", indent);
+			else
+				print_parse(&tuple_parse);
+			if (verbose)
+				putchar('\n');
+			if (tuple_parse.tuple.TupleCode == CISTPL_END)
+				break;
+		}
+
+		if (!verbose && (nfn > 0))
+			printf("%s}\n", indent + 2);
+	}
+
+	return 0;
+}
-- 
1.5.6.5




More information about the linux-pcmcia mailing list