readcis.c revision 112233
1/*
2 * Copyright (c) 1995 Andrew McRae.  All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 * 3. The name of the author may not be used to endorse or promote products
13 *    derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#ifndef lint
28static const char rcsid[] =
29  "$FreeBSD: head/usr.sbin/pccard/pccardd/readcis.c 112233 2003-03-14 13:17:35Z imp $";
30#endif /* not lint */
31
32/*
33 * Code cleanup, bug-fix and extension
34 * by Tatsumi Hosokawa <hosokawa@mt.cs.keio.ac.jp>
35 */
36
37#include <err.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
41#include <unistd.h>
42#include <sys/ioctl.h>
43
44#include <pccard/cardinfo.h>
45#include <pccard/cis.h>
46
47#include "readcis.h"
48
49#ifdef RATOCLAN
50static int	rex5588 = 0;
51#endif
52int isdumpcisfile = 0;
53
54static int read_attr(int, char *, int);
55static int ck_linktarget(int, off_t, int);
56static void cis_info(struct cis *, unsigned char *, int);
57static void device_desc(unsigned char *, int, struct dev_mem *);
58static void config_map(struct cis *, unsigned char *, int);
59static void cis_config(struct cis *, unsigned char *, int);
60static void cis_manuf_id(struct cis *, unsigned char *, int);
61static void cis_func_id(struct cis *, unsigned char *, int);
62static void cis_network_ext(struct cis *, unsigned char *, int);
63static struct tuple_list *read_one_tuplelist(int, int, off_t);
64static struct tuple_list *read_tuples(int);
65static struct tuple *find_tuple_in_list(struct tuple_list *, unsigned char);
66static struct tuple_info *get_tuple_info(unsigned char);
67
68static struct tuple_info tuple_info[] = {
69	{"Null tuple", 0x00, 0},
70	{"Common memory descriptor", 0x01, 255},
71	{"Long link to next chain for CardBus", 0x02, 255},
72	{"Indirect access", 0x03, 255},
73	{"Configuration map for CardBus", 0x04, 255},
74	{"Configuration entry for CardBus", 0x05, 255},
75	{"Long link to next chain for MFC", 0x06, 255},
76	{"Base address register for CardBus", 0x07, 6},
77	{"Checksum", 0x10, 5},
78	{"Long link to attribute memory", 0x11, 4},
79	{"Long link to common memory", 0x12, 4},
80	{"Link target", 0x13, 3},
81	{"No link", 0x14, 0},
82	{"Version 1 info", 0x15, 255},
83	{"Alternate language string", 0x16, 255},
84	{"Attribute memory descriptor", 0x17, 255},
85	{"JEDEC descr for common memory", 0x18, 255},
86	{"JEDEC descr for attribute memory", 0x19, 255},
87	{"Configuration map", 0x1A, 255},
88	{"Configuration entry", 0x1B, 255},
89	{"Other conditions for common memory", 0x1C, 255},
90	{"Other conditions for attribute memory", 0x1D, 255},
91	{"Geometry info for common memory", 0x1E, 255},
92	{"Geometry info for attribute memory", 0x1F, 255},
93	{"Manufacturer ID", 0x20, 4},
94	{"Functional ID", 0x21, 2},
95	{"Functional EXT", 0x22, 255},
96	{"Software interleave", 0x23, 2},
97	{"Version 2 Info", 0x40, 255},
98	{"Data format", 0x41, 255},
99	{"Geometry", 0x42, 4},
100	{"Byte order", 0x43, 2},
101	{"Card init date", 0x44, 4},
102	{"Battery replacement", 0x45, 4},
103	{"Organization", 0x46, 255},
104	{"Terminator", 0xFF, 0},
105	{0, 0, 0}
106};
107
108/*
109 *	After reading the tuples, decode the relevant ones.
110 */
111struct cis *
112readcis(int fd)
113{
114	struct tuple_list *tl;
115	struct tuple *tp;
116	struct cis *cp;
117
118	cp = xmalloc(sizeof(*cp));
119	cp->tlist = read_tuples(fd);
120	if (cp->tlist == 0)
121		return (NULL);
122
123	for (tl = cp->tlist; tl; tl = tl->next)
124		for (tp = tl->tuples; tp; tp = tp->next) {
125#if 0
126			printf("tuple code = 0x%02x, data is\n", tp->code);
127			dump(tp->data, tp->length);
128#endif
129			switch (tp->code) {
130			case CIS_MEM_COMMON:	/* 0x01 */
131				device_desc(tp->data, tp->length, &cp->common_mem);
132				break;
133			case CIS_INFO_V1:	/* 0x15 */
134				cis_info(cp, tp->data, tp->length);
135				break;
136			case CIS_MEM_ATTR:	/* 0x17 */
137				device_desc(tp->data, tp->length, &cp->attr_mem);
138				break;
139			case CIS_CONF_MAP:	/* 0x1A */
140				config_map(cp, tp->data, tp->length);
141				break;
142			case CIS_CONFIG:	/* 0x1B */
143				cis_config(cp, tp->data, tp->length);
144				break;
145			case CIS_MANUF_ID:	/* 0x20 */
146				cis_manuf_id(cp, tp->data, tp->length);
147				break;
148			case CIS_FUNC_ID:	/* 0x21 */
149				cis_func_id(cp, tp->data, tp->length);
150				break;
151			case CIS_FUNC_EXT:	/* 0x22 */
152				if (cp->func_id1 == 6)	/* LAN adaptor */
153					cis_network_ext(cp, tp->data, tp->length);
154				break;
155			}
156		}
157	return (cp);
158}
159
160/*
161 *	free_cis - delete cis entry.
162 */
163void
164freecis(struct cis *cp)
165{
166	struct cis_ioblk *io;
167	struct cis_memblk *mem;
168	struct cis_config *conf;
169	struct tuple *tp;
170	struct tuple_list *tl;
171
172	while ((tl = cp->tlist) != 0) {
173		cp->tlist = tl->next;
174		while ((tp = tl->tuples) != 0) {
175			tl->tuples = tp->next;
176			if (tp->data)
177				free(tp->data);
178		}
179	}
180
181	while ((conf = cp->conf) != 0) {
182		cp->conf = conf->next;
183		while ((io = conf->io) != 0) {
184			conf->io = io->next;
185			free(io);
186		}
187		while ((mem = conf->mem) != 0) {
188			conf->mem = mem->next;
189			free(mem);
190		}
191		free(conf);
192	}
193	free(cp);
194}
195
196/*
197 *	Fills in CIS version data.
198 */
199static void
200cis_info(struct cis *cp, unsigned char *p, int len)
201{
202	cp->maj_v = *p++;
203	cp->min_v = *p++;
204	len -= 2;
205	if (cp->manuf) {
206		free(cp->manuf);
207		cp->manuf = NULL;
208	}
209	if (len > 1 && *p != 0xff) {
210		cp->manuf = strdup(p);
211		len -= strlen(p) + 1;
212		p += strlen(p) + 1;
213	}
214	if (cp->vers) {
215		free(cp->vers);
216		cp->vers = NULL;
217	}
218	if (len > 1 && *p != 0xff) {
219		cp->vers = strdup(p);
220		len -= strlen(p) + 1;
221		p += strlen(p) + 1;
222	} else {
223		cp->vers = strdup("[none]");
224	}
225	if (cp->add_info1) {
226		free(cp->add_info1);
227		cp->add_info1 = NULL;
228	}
229	if (len > 1 && *p != 0xff) {
230		cp->add_info1 = strdup(p);
231		len -= strlen(p) + 1;
232		p += strlen(p) + 1;
233	} else {
234		cp->add_info1 = strdup("[none]");
235	}
236	if (cp->add_info2) {
237		free(cp->add_info2);
238		cp->add_info2 = NULL;
239	}
240	if (len > 1 && *p != 0xff)
241		cp->add_info2 = strdup(p);
242	else
243		cp->add_info2 = strdup("[none]");
244}
245
246static void
247cis_manuf_id(struct cis *cp, unsigned char *p, int len)
248{
249	if (len > 4) {
250		cp->manufacturer = tpl16(p);
251		cp->product = tpl16(p+2);
252		if (len == 5)
253			cp->prodext = *(p+4); /* For xe driver */
254	} else {
255		cp->manufacturer=0;
256		cp->product=0;
257		cp->prodext=0;
258	}
259}
260/*
261 *	Fills in CIS function ID.
262 */
263static void
264cis_func_id(struct cis *cp, unsigned char *p, int len)
265{
266	cp->func_id1 = *p++;
267	cp->func_id2 = *p++;
268}
269
270static void
271cis_network_ext(struct cis *cp, unsigned char *p, int len)
272{
273	int i;
274
275	switch (p[0]) {
276	case 4:		/* Node ID */
277		if (len <= 2 || len < p[1] + 2)
278			return;
279
280		if (cp->lan_nid)
281			free(cp->lan_nid);
282		cp->lan_nid = xmalloc(p[1]);
283
284		for (i = 0; i <= p[1]; i++)
285			cp->lan_nid[i] = p[i + 1];
286		break;
287	}
288}
289
290/*
291 *	"FUJITSU LAN Card (FMV-J182)" has broken CIS
292 */
293static int
294fmvj182_check(unsigned char *p)
295{
296	char    manuf[BUFSIZ], vers[BUFSIZ];
297
298	p++;			/* major version */
299	p++;			/* minor version */
300	strncpy(manuf, p, sizeof(manuf) - 1);
301	while (*p++);
302	strncpy(vers, p, sizeof(vers) - 1);
303	if (!strcmp(manuf, "FUJITSU") && !strcmp(vers, "LAN Card(FMV-J182)"))
304		return 1;
305	else
306		return 0;
307}
308
309#ifdef RATOCLAN
310/*
311 *	"RATOC LAN Card (REX-5588)" has broken CIS
312 */
313static int
314rex5588_check(unsigned char *p)
315{
316	char    manuf[BUFSIZ], vers[BUFSIZ];
317
318	p++;			/* major version */
319	p++;			/* minor version */
320	strncpy(manuf, p, sizeof(manuf) - 1);
321	while (*p++);
322	strncpy(vers, p, sizeof(manuf) - 1);
323	if (!strcmp(manuf, "PCMCIA LAN MBH10304  ES"))
324		return 1;
325	else
326		return 0;
327}
328#endif
329
330#ifdef HSSYNTH
331/*
332 *	Broken CIS for "HITACHI MICROCOMPUTER SYSTEM LTD." "MSSHVPC02"
333 */
334static int
335hss_check(unsigned char *p)
336{
337	char    manuf[BUFSIZ], vers[BUFSIZ];
338
339	p++;			/* major version */
340	p++;			/* minor version */
341	strncpy(manuf, p, sizeof(manuf) - 1);
342	while (*p++);
343	strncpy(vers, p, sizeof(vers) - 1);
344	if (!strcmp(manuf, "HITACHI MICROCOMPUTER SYSTEMS LTD.")
345	 && !strcmp(vers, "MSSHVPC02"))
346		return 1;
347	else
348		return 0;
349}
350#endif	/* HSSYNTH */
351
352/*
353 *	device_desc - decode device descriptor.
354 */
355static void
356device_desc(unsigned char *p, int len, struct dev_mem *dp)
357{
358	while (len > 0 && *p != 0xFF) {
359		dp->valid = 1;
360		dp->type = (*p & 0xF0) >> 4;
361		dp->wps = !!(*p & 0x8);
362		dp->speed = *p & 7;
363		p++;
364		if (*p != 0xFF) {
365			dp->addr = (*p >> 3) & 0xF;
366			dp->units = *p & 7;
367		}
368		p++;
369		len -= 2;
370	}
371}
372
373/*
374 *	configuration map of card control register.
375 */
376static void
377config_map(struct cis *cp, unsigned char *p, int len)
378{
379	unsigned char *p1;
380	int rlen = (*p & 3) + 1;
381
382	p1 = p + 1;
383	cp->last_config = *p1++ & 0x3F;
384	cp->reg_addr = parse_num(rlen | 0x10, p1, &p1, 0);
385	cp->ccrs = *p1;
386}
387
388/*
389 *	Parse variable length value.
390 */
391u_int
392parse_num(int sz, u_char *p, u_char **q, int ofs)
393{
394	u_int num = 0;
395
396	switch (sz) {
397	case 0:
398	case 0x10:
399		break;
400	case 1:
401	case 0x11:
402		num = (*p++) + ofs;
403		break;
404	case 2:
405	case 0x12:
406		num = tpl16(p) + ofs;
407		p += 2;
408		break;
409	case 0x13:
410		num = tpl24(p) + ofs;
411		p += 3;
412		break;
413	case 3:
414	case 0x14:
415		num = tpl32(p) + ofs;
416		p += 4;
417		break;
418	}
419	if (q)
420		*q = p;
421	return num;
422}
423
424/*
425 *	CIS config entry - Decode and build configuration entry.
426 */
427static void
428cis_config(struct cis *cp, unsigned char *p, int len)
429{
430	int     x;
431	int     i, j;
432	struct cis_config *conf, *last;
433	unsigned char feat;
434
435	conf = xmalloc(sizeof(*conf));
436	if ((last = cp->conf) != 0) {
437		while (last->next)
438			last = last->next;
439		last->next = conf;
440	} else
441		cp->conf = conf;
442 	conf->id = *p & 0x3F;	/* Config index */
443#ifdef RATOCLAN
444	if (rex5588 && conf->id >= 0x08 && conf->id <= 0x1d)
445		conf->id |= 0x20;
446#endif
447 	if (*p & 0x40)		/* Default flag */
448		cp->def_config = conf;
449	if (*p++ & 0x80)
450 		p++;		/* Interface byte skip */
451 	feat = *p++;		/* Features byte */
452	for (i = 0; i < CIS_FEAT_POWER(feat); i++) {
453		unsigned char parms = *p++;
454
455		conf->pwr = 1;
456		for (j = 0; j < 8; j++)
457			if (parms & (1 << j))
458				while (*p++ & 0x80);
459	}
460	if (feat & CIS_FEAT_TIMING) {
461		conf->timing = 1;
462		i = *p++;
463		if (CIS_WAIT_SCALE(i) != 3)
464			p++;
465		if (CIS_READY_SCALE(i) != 7)
466			p++;
467		if (CIS_RESERVED_SCALE(i) != 7)
468			p++;
469	}
470	if (feat & CIS_FEAT_I_O) {
471		conf->iospace = 1;
472		if (CIS_IO_RANGE & *p)
473			conf->io_blks = CIS_IO_BLKS(p[1]) + 1;
474		conf->io_addr = CIS_IO_ADDR(*p);
475		conf->io_bus = (*p >> 5) & 3; /* CIS_IO_8BIT | CIS_IO_16BIT */
476		if (*p++ & CIS_IO_RANGE) {
477			struct cis_ioblk *io;
478			struct cis_ioblk *last_io = NULL;
479
480			i = CIS_IO_ADSZ(*p);
481			j = CIS_IO_BLKSZ(*p++);
482			for (x = 0; x < conf->io_blks; x++) {
483				io = xmalloc(sizeof(*io));
484				if (last_io)
485					last_io->next = io;
486				else
487					conf->io = io;
488				last_io = io;
489				io->addr = parse_num(i, p, &p, 0);
490				io->size = parse_num(j, p, &p, 1);
491			}
492		}
493	}
494	if (feat & CIS_FEAT_IRQ) {
495		conf->irq = 1;
496		conf->irqlevel = *p & 0xF;
497		conf->irq_flags = *p & 0xF0;
498		if (*p++ & CIS_IRQ_MASK) {
499			conf->irq_mask = tpl16(p);
500			p += 2;
501		}
502	}
503	switch (CIS_FEAT_MEMORY(feat)) {
504	case CIS_FEAT_MEM_NONE:
505		break;
506	case CIS_FEAT_MEM_LEN:
507		conf->memspace = 1;
508		conf->mem = xmalloc(sizeof(*conf->mem));
509		conf->mem->length = tpl16(p) << 8;
510		break;
511	case CIS_FEAT_MEM_ADDR:
512		conf->memspace = 1;
513		conf->mem = xmalloc(sizeof(*conf->mem));
514		conf->mem->length = tpl16(p) << 8;
515		conf->mem->address = tpl16(p + 2) << 8;
516		break;
517	case CIS_FEAT_MEM_WIN: {
518		struct cis_memblk *mem;
519		struct cis_memblk *last_mem = NULL;
520
521		conf->memspace = 1;
522		x = *p++;
523		conf->memwins = CIS_MEM_WINS(x);
524		for (i = 0; i < conf->memwins; i++) {
525			mem = xmalloc(sizeof(*mem));
526			if (last_mem)
527				last_mem->next = mem;
528			else
529				conf->mem = mem;
530			last_mem = mem;
531			mem->length = parse_num(CIS_MEM_LENSZ(x) | 0x10, p, &p, 0) << 8;
532			mem->address = parse_num(CIS_MEM_ADDRSZ(x) | 0x10, p, &p, 0) << 8;
533			if (x & CIS_MEM_HOST) {
534				mem->host_address = parse_num(CIS_MEM_ADDRSZ(x) | 0x10,
535							      p, &p, 0) << 8;
536			}
537		}
538		break;
539	    }
540	}
541	if (feat & CIS_FEAT_MISC) {
542		conf->misc_valid = 1;
543		conf->misc = *p++;
544	}
545}
546
547/*
548 *	Read the tuples from the card.
549 *	The processing of tuples is as follows:
550 *		- Read tuples at attribute memory, offset 0.
551 *		- If a CIS_END is the first tuple, look for
552 *		  a tuple list at common memory offset 0; this list
553 *		  must start with a LINKTARGET.
554 *		- If a long link tuple was encountered, execute the long
555 *		  link.
556 *		- If a no-link tuple was seen, terminate processing.
557 *		- If no no-link tuple exists, and no long link tuple
558 *		  exists while processing the primary tuple list,
559 *		  then look for a LINKTARGET tuple in common memory.
560 *		- If a long link tuple is found in any list, then process
561 *		  it. Only one link is allowed per list.
562 */
563static struct tuple_list *tlist;
564
565static struct tuple_list *
566read_tuples(int fd)
567{
568	struct tuple_list *tl = 0, *last_tl;
569	struct tuple *tp;
570	int     flag;
571	off_t   offs;
572
573	tlist = 0;
574	last_tl = tlist = read_one_tuplelist(fd, MDF_ATTR, (off_t) 0);
575
576	/* Now start processing the links (if any). */
577	do {
578		flag = MDF_ATTR;
579		tp = find_tuple_in_list(last_tl, CIS_LONGLINK_A);
580		if (tp == 0) {
581			flag = 0;
582			tp = find_tuple_in_list(last_tl, CIS_LONGLINK_C);
583		}
584		if (tp && tp->length == 4) {
585			offs = tpl32(tp->data);
586#ifdef	DEBUG
587			printf("Checking long link at %qd (%s memory)\n",
588			    offs, flag ? "Attribute" : "Common");
589#endif
590			/* If a link was found, read the tuple list from it. */
591			if (ck_linktarget(fd, offs, flag)) {
592				tl = read_one_tuplelist(fd, flag, offs);
593				last_tl->next = tl;
594				last_tl = tl;
595			}
596		} else
597			tl = 0;
598	} while (tl);
599
600	/*
601	 * If the primary list had no NOLINK tuple, and no LINKTARGET,
602	 * then try to read a tuple list at common memory (offset 0).
603	 */
604	if (find_tuple_in_list(tlist, CIS_NOLINK) == 0 && tlist->next == 0 &&
605	    ck_linktarget(fd, (off_t) 0, 0)) {
606#ifdef	DEBUG
607		printf("Reading long link at %qd (%s memory)\n",
608		    offs, flag ? "Attribute" : "Common");
609#endif
610		tlist->next = read_one_tuplelist(fd, 0, (off_t) 0);
611	}
612	return (tlist);
613}
614
615/*
616 *	Read one tuple list from the card.
617 */
618static struct tuple_list *
619read_one_tuplelist(int fd, int flags, off_t offs)
620{
621	struct tuple *tp, *last_tp = 0;
622	struct tuple_list *tl;
623	struct tuple_info *tinfo;
624	int     total = 0;
625	unsigned char code, length;
626	int     fmvj182 = 0;
627#ifdef HSSYNTH
628	int     hss = 0;
629#endif	/* HSSYNTH */
630
631	/* Check to see if this memory has already been scanned. */
632	for (tl = tlist; tl; tl = tl->next)
633		if (tl->offs == offs && tl->flags == (flags & MDF_ATTR))
634			return (0);
635	tl = xmalloc(sizeof(*tl));
636	tl->offs = offs;
637	tl->flags = flags & MDF_ATTR;
638	ioctl(fd, PIOCRWFLAG, &flags);
639	lseek(fd, offs, SEEK_SET);
640	do {
641		if (read_attr(fd, &code, 1) != 1) {
642			warn("CIS code read");
643			break;
644		}
645		total++;
646		if (code == CIS_NULL)
647			continue;
648		tp = xmalloc(sizeof(*tp));
649		tp->code = code;
650		if (code == CIS_END)
651			length = 0;
652		else {
653			if (read_attr(fd, &length, 1) != 1) {
654				warn("CIS len read");
655				break;
656			}
657			total++;
658			if (fmvj182 && (code == 0x1b) && (length == 25))
659				length = 31;
660		}
661		tp->length = length;
662#ifdef	DEBUG
663		printf("Tuple code = 0x%x, len = %d\n", code, length);
664#endif
665		if (length == 0xFF) {
666			length = tp->length = 0;
667			code = CIS_END;
668		}
669		if (length != 0) {
670			total += length;
671			tp->data = xmalloc(length);
672			if (read_attr(fd, tp->data, length) != length) {
673				warn("CIS read");
674				break;
675			}
676		}
677
678		/*
679		 * Check the tuple, and ignore it if it isn't in the table
680		 * or the length is illegal.
681		 */
682		tinfo = get_tuple_info(code);
683		if (code == CIS_INFO_V1) {
684			/* Hack for broken CIS of FMV-J182 Ethernet card */
685			fmvj182 = fmvj182_check(tp->data);
686#ifdef RATOCLAN
687			/* Hack for RATOC LAN card */
688			rex5588 = rex5588_check(tp->data);
689#endif /* RATOCLAN */
690#ifdef	HSSYNTH
691			/* Hack for Hitachi Speech Synthesis card */
692			hss = hss_check(tp->data);
693#endif	/* HSSYNTH */
694		}
695		if (tinfo != NULL && (tinfo->length != 255 && tinfo->length > length)) {
696			printf("code %s ignored\n", tuple_name(code));
697			tp->code = CIS_NULL;
698		}
699		if (tl->tuples == NULL)
700			tl->tuples = tp;
701		else
702			last_tp->next = tp;
703		last_tp = tp;
704	} while (code != CIS_END && total < 1024);
705	return (tl);
706}
707
708/*
709 *	return true if the offset points to a LINKTARGET tuple.
710 */
711static int
712ck_linktarget(int fd, off_t offs, int flag)
713{
714	char    blk[5];
715
716	ioctl(fd, PIOCRWFLAG, &flag);
717	lseek(fd, offs, SEEK_SET);
718	if (read_attr(fd, blk, 5) != 5)
719		return (0);
720	if (blk[0] == 0x13 &&
721	    blk[1] == 0x3 &&
722	    blk[2] == 'C' &&
723	    blk[3] == 'I' &&
724	    blk[4] == 'S')
725		return (1);
726	return (0);
727}
728
729/*
730 *	find_tuple_in_list - find a tuple within a
731 *	single tuple list.
732 */
733static struct tuple *
734find_tuple_in_list(struct tuple_list *tl, unsigned char code)
735{
736	struct tuple *tp;
737
738	for (tp = tl->tuples; tp; tp = tp->next)
739		if (tp->code == code)
740			break;
741	return (tp);
742}
743
744static int
745read_attr(int fd, char *bp, int len)
746{
747	char    blk[1024], *p = blk;
748	int     i, l;
749
750	if (isdumpcisfile)
751		return (read(fd, bp, len));
752	if (len > sizeof(blk) / 2)
753		len = sizeof(blk) / 2;
754	l = i = read(fd, blk, len * 2);
755	if (i <= 0) {
756		printf("Read return %d bytes (expected %d)\n", i, len * 2);
757		return (i);
758	}
759	while (i > 0) {
760		*bp++ = *p++;
761		p++;
762		i -= 2;
763	}
764	return (l / 2);
765}
766
767/*
768 *	return table entry for code.
769 */
770static struct tuple_info *
771get_tuple_info(unsigned char code)
772{
773	struct tuple_info *tp;
774
775	for (tp = tuple_info; tp->name; tp++)
776		if (tp->code == code)
777			return (tp);
778	return (0);
779}
780
781char *
782tuple_name(unsigned char code)
783{
784	struct tuple_info *tp;
785
786	tp = get_tuple_info(code);
787	if (tp)
788		return (tp->name);
789	return ("Unknown");
790}
791