All of lore.kernel.org
 help / color / mirror / Atom feed
From: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
To: linux-kernel@vger.kernel.org
Cc: linux-pcmcia@lists.infradead.org, David Woodhouse <dwmw2@infradead.org>
Subject: [PATCH 5/5] Documentation: add an utility to parse CIS files to readable form
Date: Thu, 23 Sep 2010 19:19:57 +0400	[thread overview]
Message-ID: <1285255197-9262-5-git-send-email-dbaryshkov@gmail.com> (raw)
In-Reply-To: <1285255197-9262-1-git-send-email-dbaryshkov@gmail.com>

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@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@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


  parent reply	other threads:[~2010-09-23 15:20 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-09-23 15:19 [PATCH 1/5] Makefile.fwinst: include Makefile.lib Dmitry Eremin-Solenikov
2010-09-23 15:19 ` [PATCH 2/5] Add mkcis - a program that can create .cis from text description Dmitry Eremin-Solenikov
2010-09-23 15:29   ` David Woodhouse
2010-09-23 15:36     ` Dmitry Eremin-Solenikov
2010-09-23 15:54       ` David Woodhouse
2010-09-23 16:58       ` Wolfram Sang
2010-09-23 16:55     ` Gene Heskett
2010-09-23 15:19 ` [PATCH 3/5] firmware: enable usage of mkcis Dmitry Eremin-Solenikov
2010-09-23 15:19 ` [PATCH 4/5] firmware: replace ihex files with text descriptions for CIS files Dmitry Eremin-Solenikov
2010-09-23 15:19 ` Dmitry Eremin-Solenikov [this message]
2010-09-23 15:45 ` [PATCH 1/5] Makefile.fwinst: include Makefile.lib Sam Ravnborg

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1285255197-9262-5-git-send-email-dbaryshkov@gmail.com \
    --to=dbaryshkov@gmail.com \
    --cc=dwmw2@infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pcmcia@lists.infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.