/*
 * Copyright (c) 1996-1999 by Sun Microsystems, Inc.
 * All rights reserved.
 */

#pragma ident	"@(#)cis_handlers.c	1.45	99/09/23 SMI"

/*
 * This file contains the tuple handlers that are called by the CIS
 *	parser.
 *
 * XXX - how about a better explaination??
 */

#include <sys/types.h>
#include <sys/systm.h>
#include <sys/user.h>
#include <sys/buf.h>
#include <sys/file.h>
#include <sys/uio.h>
#include <sys/conf.h>
#include <sys/stat.h>
#include <sys/autoconf.h>
#include <sys/vtoc.h>
#include <sys/dkio.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/debug.h>
#include <sys/ddi_impldefs.h>
#include <sys/kstat.h>
#include <sys/kmem.h>
#include <sys/modctl.h>
#include <sys/kobj.h>
#include <sys/callb.h>

#include <sys/pctypes.h>
#include <pcmcia/sys/cs_types.h>
#include <pcmcia/sys/cis.h>
#include <pcmcia/sys/cis_handlers.h>
#include <pcmcia/sys/cs.h>
#include <pcmcia/sys/cs_priv.h>
#include <pcmcia/sys/cis_protos.h>

/*
 * Function prototypes
 */
static void cistpl_pd_parse(cistpl_t *, cistpl_cftable_entry_pwr_t *);
static void cis_return_name(cistpl_callout_t *, cistpl_get_tuple_name_t *);

/*
 * cis_tuple_handler - call the handler for the tuple described by the
 *				tuple pointer
 *
 *	cistpl_callout_t *co - pointer to callout structure
 *				array to use to find this tuple
 *	cistpl_t *tp - pointer to a tuple structure
 *	int flags - action for the handler to perform
 * XXX - we need a description of the flags passed to the tuple handler
 *	void *arg - argument to pass on to tuple handler
 *
 * If the tuple is not recognized but is is a vendor-specific tuple, we
 *	set the CISTPLF_VENDOR_SPECIFIC flag in the tuple.
 *
 * We return CISTPLF_UNKNOWN if this is an unrecognized	tuple as well as
 *	set the CISTPLF_UNKNOWN flag in the tuple list structure.  Note
 *	that encountering an unknown tuple is not necessarily an error,
 *	so we don't set the HANDTPL_ERROR flag on the return code.  It
 *	is up to the caller to determine what an unrecognized tuple means.
 *
 * If this is a recognized tuple, the apropriate tuple handler is called and
 *	the return value from the handler is returned directly to the caller.
 *
 * The void *arg is optional, and it's meaning is dependent on the
 *	particular tuple handler called and the flags parameter.
 *
 * For the special case of HANDTPL_RETURN_NAME, we don't bother calling the
 *	tuple handler and just return the tuple name to the caller.
 */
uint32_t
cis_tuple_handler(cistpl_callout_t *co, cistpl_t *tp, uint32_t flags,
					void *arg, cisdata_t subtype)
{
	/*
	 * Check to see if this is a vendor-specific tuple.
	 */
	if (CISTPL_IS_VENDOR_SPECIFIC(tp->type))
	    tp->flags |= CISTPLF_VENDOR_SPECIFIC;

	/*
	 * Scan the callout list until we find the tuple passed to us, or we
	 *	encounter a CISTPL_END in the callout list, which signals that
	 *	there are no more tuples in the callout list.
	 */
	while (co->type != (cisdata_t)CISTPL_END) {
	    if (co->type == tp->type &&
		((tp->type != CISTPL_FUNCE) ||
		    (tp->type == CISTPL_FUNCE && co->subtype == subtype))) {
			tp->flags &= ~CISTPLF_UNKNOWN;
			if (flags & HANDTPL_RETURN_NAME) {
			    cis_return_name(co, (cistpl_get_tuple_name_t *)arg);
			    return (CISTPLF_NOERROR);
			} else {
			    return ((*co->handler) (co, tp, flags, arg));
			} /* HANDTPL_RETURN_NAME */
	    } /* if */
	    co++;
	} /* while */

	/*
	 * If we didn't recognize the tuple and the caller wants the tuple
	 *	name back, then return the "unknown tuple" string. At this
	 *	point, "co" will be pointing to the last entry in the
	 *	callout list. It's not an error to not recognize the tuple
	 *	when the operation is HANDTPL_RETURN_NAME.
	 */
	if (flags & HANDTPL_RETURN_NAME) {
	    cis_return_name(co, (cistpl_get_tuple_name_t *)arg);
	    return (CISTPLF_NOERROR);
	}

	tp->flags |= CISTPLF_UNKNOWN;
	return (CISTPLF_UNKNOWN);
}

/*
 * cis_no_tuple_handler - this generic tuple handler is used if no special
 *				tuple processing is required for the passed
 *				tuple
 *
 *	cistpl_callout_t *co - pointer to this tuple's entry in the
 *				tuple callout structure
 *	cistpl_t *tp - pointer to this tuple's entry in the local linked list
 *	int flags - action to perform
 *
 * This handler will set the CISTPLF_COPYOK flag if the tuple link is greater
 *	than zero, indicating that it's OK to copy the tuple data body. It
 *	will also set whatever flags are specified in the callout structure.
 *
 * We always set the CISTPLF_VALID when we're called with HANDTPL_COPY_DONE.
 *
 * We return CISTPLF_UNKNOWN if we're being called to parse the tuple.
 *
 * We return CISTPLF_NOERROR in every other case to indicate that this is a
 *	recognized tuple.
 */
/*ARGSUSED*/
uint32_t
cis_no_tuple_handler(cistpl_callout_t *co, cistpl_t *tp,
					uint32_t flags, void *arg)
{
	if (flags & HANDTPL_SET_FLAGS) {
		tp->flags |= co->flags;	/* XXX - is = the right thing here? */
		if (tp->len > 0)
			tp->flags |= CISTPLF_COPYOK;
	}

	if (flags & HANDTPL_COPY_DONE)
		tp->flags |= CISTPLF_VALID;

	if (flags & HANDTPL_PARSE_LTUPLE)
	    return (CISTPLF_UNKNOWN);

	return (CISTPLF_NOERROR);
}

/*
 * cis_unknown_tuple_handler - this generic tuple handler is used if we don't
 *				understand this tuple
 *
 *	cistpl_callout_t *co - pointer to this tuple's entry in the
 *				tuple callout structure
 *	cistpl_t *tp - pointer to this tuple's entry in the local linked list
 *	int flags - action to perform
 *
 * This handler will not set the CISTPLF_COPYOK flag since we don't know the
 *	contents of a vendor-specific tuple.
 *
 * We always set the CISTPLF_VALID when we're called with HANDTPL_COPY_DONE
 *	to specify that we understand this tuple's code, but not it's data
 *	body.
 *
 * We return CISTPLF_UNKNOWN if we're being called to parse the tuple or to
 *	perform any other operation.
 */
/*ARGSUSED*/
uint32_t
cis_unknown_tuple_handler(cistpl_callout_t *co, cistpl_t *tp,
					uint32_t flags, void *arg)
{
	if (flags & HANDTPL_SET_FLAGS) {
		tp->flags |= co->flags;	/* XXX - is = the right thing here? */
		return (CISTPLF_NOERROR);
	}

	if (flags & HANDTPL_COPY_DONE) {
		tp->flags |= CISTPLF_VALID;
		return (CISTPLF_NOERROR);
	}

	return (CISTPLF_UNKNOWN);
}

/*
 * cistpl_vers_1_handler - handler for the CISTPL_VERS_1 tuple
 *
 *	void *arg - points to a cistpl_vers_1_t * where the
 *			information is stuffed into
 */
uint32_t
cistpl_vers_1_handler(cistpl_callout_t *co, cistpl_t *tp,
					uint32_t flags, void *arg)
{
	/*
	 * nothing special about our flags, so just call the
	 *	generic handler for this
	 */
	if (flags & HANDTPL_SET_FLAGS)
		return (cis_no_tuple_handler(co, tp, flags, arg));

	/*
	 * We don't currently validate this tuple. This call will
	 *	always set tp->flags |= CISTPLF_VALID.
	 */
	if (flags & HANDTPL_COPY_DONE)
		return (cis_no_tuple_handler(co, tp, flags, arg));

	if (flags & HANDTPL_PARSE_LTUPLE) {
		cistpl_vers_1_t *cs = (cistpl_vers_1_t *)arg;


		RESET_TP(tp);

		cs->major = GET_BYTE(tp);
		cs->minor = GET_BYTE(tp);
		for (cs->ns = 0; GET_LEN(tp) > 0 &&
				/* CSTYLED */
				cs->ns < CISTPL_VERS_1_MAX_PROD_STRINGS; ) {
			(void) strcpy(cs->pi[cs->ns++], cis_getstr(tp));
		} /* for */
	} /* HANDTPL_PARSE_LTUPLE */

	return (CISTPLF_NOERROR);
}

/*
 * cistpl_config_handler - handler for the CISTPL_CONFIG tuple
 *
 *	void *arg - points to a XXX where the information is stuffed into
 *
 * For the first ten config registers we set the present flags in the
 *	cistpl_config_t if the register exists.  The flags that we use
 *	for this are the same as the flags reguired for the Card Services
 *	RequestConfiguration function and they can be used by clients
 *	directly without requiring any remapping of values.
 *
 * XXX we don't handle TPCC_SBTPL subtuples yet
 */

uint32_t	config_regs_present_map[] = {
	CONFIG_OPTION_REG_PRESENT,	/* COR present */
	CONFIG_STATUS_REG_PRESENT,	/* STAT reg present */
	CONFIG_PINREPL_REG_PRESENT,	/* PRR present */
	CONFIG_COPY_REG_PRESENT,	/* COPY reg present */
	CONFIG_EXSTAT_REG_PRESENT,	/* EXSTAT reg present */
	CONFIG_IOBASE0_REG_PRESENT,	/* IOBASE0 reg present */
	CONFIG_IOBASE1_REG_PRESENT,	/* IOBASE1 reg present */
	CONFIG_IOBASE2_REG_PRESENT,	/* IOBASE2 reg present */
	CONFIG_IOBASE3_REG_PRESENT,	/* IOBASE3 reg present */
	CONFIG_IOLIMIT_REG_PRESENT,	/* IOLIMIT reg present */
};

uint32_t
cistpl_config_handler(cistpl_callout_t *co, cistpl_t *tp,
					uint32_t flags, void *arg)
{
	cisdata_t tpcc_sz;
	int i, n, nrb, na, hr = 0;

	/*
	 * nothing special about our flags, so just call the
	 *	generic handler for this
	 */
	if (flags & HANDTPL_SET_FLAGS)
		return (cis_no_tuple_handler(co, tp, flags, arg));

	/*
	 * We don't currently validate this tuple. This call will
	 *	always set tp->flags |= CISTPLF_VALID.
	 */
	if (flags & HANDTPL_COPY_DONE)
		return (cis_no_tuple_handler(co, tp, flags, arg));

	if (flags & HANDTPL_PARSE_LTUPLE) {
		cistpl_config_t *cr = (cistpl_config_t *)arg;
		int crn = 0;

		RESET_TP(tp);

		tpcc_sz = GET_BYTE(tp);		/* config regs size fields */
		cr->last = GET_BYTE(tp);	/* last config index */

		na = (tpcc_sz&3)+1;		/* config regs address bytes */
		nrb = ((tpcc_sz>>2)&0x0f)+1;	/* number of bytes in config */
						/*	regs presence mask */

		/*
		 * Construct the base offset address for the config registers.
		 *	We jump through these hoops because the base address
		 *	can be between one and four bytes in length.
		 */
		cr->base = 0;
		n = na;
		while (n--)
			cr->base |= ((GET_BYTE(tp) & 0x0ff) <<
							(8 * (na - (n+1))));

		/*
		 * Go through the config register presense mask bit by bit and
		 *	figure out which config registers are present and which
		 *	aren't.
		 * For the first ten config registers, set the appropriate
		 *	bits in the cr->present member so that the caller
		 *	doesn't have to do this.
		 */
		cr->nr = 0;
		cr->present = 0;
		n = nrb;
		while (n--) {
			for (i = 0; i < 8; i++, crn++) {
				if (LOOK_BYTE(tp) & (1<<i)) {
				    if (crn < (sizeof (config_regs_present_map)/
							sizeof (uint32_t)))
					cr->present |=
						config_regs_present_map[crn];
				    cr->nr++;
				    cr->hr = hr;
				    cr->regs[hr] = MAKE_CONFIG_REG_ADDR(
								cr->base, hr);
				} /* LOOK_BYTE */
				hr++;
			} /* for */
			(void) GET_BYTE(tp);
		} /* while */
	}

	return (CISTPLF_NOERROR);
}

/*
 * cistpl_device_handler - handler for the CISTPL_DEVICE, CISTPL_DEVICE_A,
 *				CISTPL_DEVICE_OC and CISTPL_DEVICE_OA tuples
 *
 *	void *arg - points to a cistpl_device_t * where the
 *			information is stuffed into
 *
 * XXX - we only handle CISTPL_DEVICE_MAX_DEVICES device descriptions
 *		described in the tuple
 */
uint32_t
cistpl_device_handler(cistpl_callout_t *co, cistpl_t *tp,
					uint32_t flags, void *arg)
{
	cisdata_t dev_id;

	/*
	 * nothing special about our flags, so just call the
	 *	generic handler for this
	 */
	if (flags & HANDTPL_SET_FLAGS)
		return (cis_no_tuple_handler(co, tp, flags, arg));

	/*
	 * We don't currently validate this tuple. This call will
	 *	always set tp->flags |= CISTPLF_VALID.
	 */
	if (flags & HANDTPL_COPY_DONE)
		return (cis_no_tuple_handler(co, tp, flags, arg));

	if (flags & HANDTPL_PARSE_LTUPLE) {
		convert_speed_t convert_speed;
		cistpl_device_t *dt = (cistpl_device_t *)arg;
		cistpl_device_node_t *cdn;

		/*
		 * XXX - fix this to look for more than one device definition
		 * XXX - fix this to handle the OC fields for
		 *	CISTPL_DEVICE_OC and CISTPL_DEVICE_OA
		 */
		dt->num_devices = 1;
		cdn = &dt->devnode[0];

		cdn->flags = 0;

		RESET_TP(tp);

		dev_id = GET_BYTE(tp);

		/*
		 * Get the device speed code.  If it's 7, then there is an
		 *	extended speed code table in use, so parse that.
		 *	If it's anything else, get the speed information
		 *	directly from the device speed code.
		 */
		if ((dev_id & 7) == 7) {
		    cdn->nS_speed = cistpl_devspeed(tp, 0, CISTPL_DEVSPEED_EXT);
		} else {
		    cdn->nS_speed = cistpl_devspeed(NULL, dev_id,
							CISTPL_DEVSPEED_TABLE);
		}

		/*
		 * Convert the speed in nS to a device speed code.
		 * XXX -  should check return code from cis_convert_devspeed()
		 */
		convert_speed.Attributes = CONVERT_NS_TO_DEVSPEED;
		convert_speed.nS = cdn->nS_speed;
		(void) cis_convert_devspeed(&convert_speed);
		cdn->speed = convert_speed.devspeed;

		if (dev_id & 8)
			cdn->flags |= CISTPL_DEVICE_WPS;

		/*
		 * Set the device type.  Note that we take the raw value
		 *	from the tuple and pass it back to the caller.
		 *	If the device type codes in the standard change,
		 *	we will have to change our flags as well.
		 */
		cdn->type = (dev_id>>4) & 0x0f;

		/*
		 * XXX - what about the device_size byte?  Is the spec wrong?
		 */
		cdn->size = GET_BYTE(tp);
		/* check for end of list */
		if (cdn->size != 0x0ff) {
		    convert_size_t convert_size;

		    convert_size.devsize = cdn->size;
		    convert_size.Attributes = CONVERT_DEVSIZE_TO_BYTES;
		    (void) cis_convert_devsize(&convert_size);
		    cdn->size_in_bytes = convert_size.bytes;
		}
	}

	return (CISTPLF_NOERROR);
}

/*
 * cistpl_cftable_handler - handler for the CISTPL_CFTABLE_ENTRY tuple
 *
 *	void *arg - points to a XXX where the information is stuffed into
 *
 *    Return:	CISTPLF_NOERROR - if no error parsing tuple
 *		HANDTPL_ERROR - if error parsing tuple
 */
extern uint32_t cistpl_cftable_io_size_table[];
extern uint32_t cistpl_cftable_shift_table[];

uint32_t
cistpl_cftable_handler(cistpl_callout_t *co, cistpl_t *tp,
					uint32_t flags, void *arg)
{
	cisdata_t tpce_indx, tpce_fs, tpce_td, sf, tpce_io, nr;
	cisdata_t ior_desc, tpce_ir, tpce_msd;
	int i, j;

	/*
	 * nothing special about our flags, so just call the
	 *	generic handler for this
	 */
	if (flags & HANDTPL_SET_FLAGS)
		return (cis_no_tuple_handler(co, tp, flags, arg));

	/*
	 * We don't currently validate this tuple. This call will
	 *	always set tp->flags |= CISTPLF_VALID.
	 */
	if (flags & HANDTPL_COPY_DONE)
		return (cis_no_tuple_handler(co, tp, flags, arg));

	if (flags & HANDTPL_PARSE_LTUPLE) {
		cistpl_cftable_entry_t *ce = (cistpl_cftable_entry_t *)arg;

		RESET_TP(tp);

		/*
		 * Check to see if we have an interface description byte.  If
		 *	we do, grab it and give it directly to the caller, and
		 *	set a flag so the caller knows that it's there.
		 * We also setup the appropriate values in the ce->pin member
		 *	so that clients can feed this value directly to the
		 *	Card Services RequestConfiguration call.
		 */
		if ((tpce_indx = GET_BYTE(tp)) & CISTPL_CFTABLE_TPCE_IFM) {
			ce->ifc = GET_BYTE(tp);

			ce->pin = 0;

			if (ce->ifc & CISTPL_CFTABLE_TPCE_IF_BVD)
			    ce->pin |= (PRR_BVD1_STATUS | PRR_BVD2_STATUS |
					PRR_BVD1_EVENT | PRR_BVD2_EVENT);
			if (ce->ifc & CISTPL_CFTABLE_TPCE_IF_WP)
			    ce->pin |= (PRR_WP_STATUS | PRR_WP_EVENT);
			if (ce->ifc & CISTPL_CFTABLE_TPCE_IF_RDY)
			    ce->pin |= (PRR_READY_STATUS | PRR_READY_EVENT);

			ce->flags |= CISTPL_CFTABLE_TPCE_IF;
		}

		/*
		 * Return the configuration index to the caller, and set the
		 *	default configuration flag if this is a default
		 *	configuration.
		 */
		ce->index = tpce_indx & CISTPL_CFTABLE_TPCE_CFGENTRYM;
		if (tpce_indx & CISTPL_CFTABLE_TPCE_DEFAULTM)
			ce->flags |= CISTPL_CFTABLE_TPCE_DEFAULT;

		/*
		 * Feature selection flags.
		 */
		tpce_fs = GET_BYTE(tp);

		/*
		 * See what types of power information are available,
		 *	and if there is any, set the global power
		 *	information flag as well as a flag for each
		 *	power description available.
		 */
		if (tpce_fs & CISTPL_CFTABLE_TPCE_FS_PWRM) {
		    cistpl_cftable_entry_pd_t *pd = &ce->pd;

		    ce->flags |= CISTPL_CFTABLE_TPCE_FS_PWR;

		    switch (tpce_fs & CISTPL_CFTABLE_TPCE_FS_PWRM) {
			case CISTPL_CFTABLE_TPCE_FS_PWR_VPP2M:
				pd->flags |= CISTPL_CFTABLE_TPCE_FS_PWR_VPP2;
				/* FALLTHROUGH */
			case CISTPL_CFTABLE_TPCE_FS_PWR_VPP1M:
				pd->flags |= CISTPL_CFTABLE_TPCE_FS_PWR_VPP1;
				/* FALLTHROUGH */
			case CISTPL_CFTABLE_TPCE_FS_PWR_VCCM:
				pd->flags |= CISTPL_CFTABLE_TPCE_FS_PWR_VCC;
		    } /* switch */
		} /* if (CISTPL_CFTABLE_TPCE_FS_PWRM) */

		/*
		 * Set up the global memory information flag.
		 */
		if (tpce_fs & CISTPL_CFTABLE_TPCE_FS_MEMM)
			ce->flags |= CISTPL_CFTABLE_TPCE_FS_MEM;

		/*
		 * Parse the various power description structures.
		 */
		if (ce->flags & CISTPL_CFTABLE_TPCE_FS_PWR) {
			cistpl_cftable_entry_pd_t *pd = &ce->pd;
			cistpl_cftable_entry_pwr_t *pwr;
			/*
			 * Collect any Vcc information.
			 */
			if (pd->flags & CISTPL_CFTABLE_TPCE_FS_PWR_VCC) {
				pwr = &pd->pd_vcc;
				cistpl_pd_parse(tp, pwr);
			}
			/*
			 * Collect any Vpp1 information.
			 */
			if (pd->flags & CISTPL_CFTABLE_TPCE_FS_PWR_VPP1) {
				pwr = &pd->pd_vpp1;
				cistpl_pd_parse(tp, pwr);
			}
			/*
			 * Collect any Vpp2 information.
			 */
			if (pd->flags & CISTPL_CFTABLE_TPCE_FS_PWR_VPP2) {
				pwr = &pd->pd_vpp2;
				cistpl_pd_parse(tp, pwr);
			}
		} /* if (CISTPL_CFTABLE_TPCE_FS_PWR) */

		/*
		 * Check to see if there's any timing information, and if
		 *	so, parse the tuple data and store it in the
		 *	caller's structure.  Set a flag in the global
		 *	flag field indicating that there is timing information.
		 */
		if (tpce_fs & CISTPL_CFTABLE_TPCE_FS_TDM) {
			convert_speed_t convert_speed;
			cistpl_cftable_entry_speed_t *sp = &ce->speed;
			ce->flags |= CISTPL_CFTABLE_TPCE_FS_TD;
			tpce_td = GET_BYTE(tp);
			/*
			 * Parse TPCE_TD to get the various timing
			 *	scale factors. Each scale factor has
			 *	a value that indicates that the particular
			 *	timing parameter doesn't exist.
			 */
			if ((sf = (tpce_td &
					CISTPL_CFTABLE_TPCE_FS_TD_WAITM)) !=
			    CISTPL_CFTABLE_TPCE_FS_TD_WAITM) {
				sp->nS_wait = cistpl_devspeed(tp,
						GET_TPCE_FS_TD_WAITS(sf),
						CISTPL_DEVSPEED_EXT);
				convert_speed.Attributes =
							CONVERT_NS_TO_DEVSPEED;
				convert_speed.nS = sp->nS_wait;
				(void) cis_convert_devspeed(&convert_speed);
				sp->wait = convert_speed.devspeed;
				sp->flags |= CISTPL_CFTABLE_TPCE_FS_TD_WAIT;
			}

			if ((sf = (tpce_td & CISTPL_CFTABLE_TPCE_FS_TD_RDYM)) !=
			    CISTPL_CFTABLE_TPCE_FS_TD_RDYM) {
				sp->nS_rdybsy = cistpl_devspeed(tp,
						GET_TPCE_FS_TD_RDYS(sf),
						CISTPL_DEVSPEED_EXT);
				convert_speed.Attributes =
							CONVERT_NS_TO_DEVSPEED;
				convert_speed.nS = sp->nS_rdybsy;
				(void) cis_convert_devspeed(&convert_speed);
				sp->rdybsy = convert_speed.devspeed;
				sp->flags |= CISTPL_CFTABLE_TPCE_FS_TD_RDY;
			}

			if ((sf = (tpce_td &
					CISTPL_CFTABLE_TPCE_FS_TD_RSVDM)) !=
			    CISTPL_CFTABLE_TPCE_FS_TD_RSVDM) {
				sp->nS_rsvd = cistpl_devspeed(tp,
						GET_TPCE_FS_TD_RSVDS(sf),
						CISTPL_DEVSPEED_EXT);
				convert_speed.Attributes =
							CONVERT_NS_TO_DEVSPEED;
				convert_speed.nS = sp->nS_rsvd;
				(void) cis_convert_devspeed(&convert_speed);
				sp->rsvd = convert_speed.devspeed;
				sp->flags |= CISTPL_CFTABLE_TPCE_FS_TD_RSVD;
			}
		} /* if (CISTPL_CFTABLE_TPCE_FS_TDM) */


		/*
		 * Parse any I/O address information.  If there is I/O
		 *	inforamtion, set a flag in the global flag field
		 *	to let the caller know.
		 */
		if (tpce_fs & CISTPL_CFTABLE_TPCE_FS_IOM) {
			cistpl_cftable_entry_io_t *io = &ce->io;

			ce->flags |= CISTPL_CFTABLE_TPCE_FS_IO;
			tpce_io = GET_BYTE(tp);
			/*
			 * Pass any I/O flags that are in the tuple directly
			 *	to the caller.
			 */
			io->flags = tpce_io;
			io->addr_lines = tpce_io &
						CISTPL_CFTABLE_TPCE_FS_IO_ALM;
			/*
			 * If there are any ranges, extract the number of
			 *	ranges and the range descriptions.
			 */
			if (tpce_io & CISTPL_CFTABLE_TPCE_FS_IO_RANGEM) {
				cistpl_cftable_entry_io_range_t *ior;
				ior_desc = GET_BYTE(tp);
				/*
				 * Number of I/O ranges is the value specified
				 *	in the tuple plus one, so there's
				 *	always at least one I/O range if the
				 *	CISTPL_CFTABLE_TPCE_FS_IO_RANGEM bit
				 *	in the I/O flags register is set.
				 */
				nr = (ior_desc & 0x0f) + 1;
				io->ranges = nr;
				/*
				 * Cycle through each I/O range.
				 */
				for (i = 0; i < (int)nr; i++) {
					ior = &io->range[i];
					ior->addr = 0;
					ior->length = 0;
					/*
					 * Gather the address information.
					 *	It's OK if there's no address
					 *	information in which case this
					 *	loop will never execute.
					 */
					for (j = 0; j <
						cistpl_cftable_io_size_table[
							(ior_desc>>4)&3];
									j++)
						ior->addr |= (GET_BYTE(tp) <<
						cistpl_cftable_shift_table[j]);
					/*
					 * Gather the length information.
					 *	It's OK if there's no length
					 *	information in which case this
					 *	loop will never execute.
					 */
					for (j = 0; j <
						cistpl_cftable_io_size_table[
							(ior_desc>>6)&3];
									j++)
						ior->length |= (GET_BYTE(tp) <<
						cistpl_cftable_shift_table[j]);
				} /* for (nr) */
			} /* if (CISTPL_CFTABLE_TPCE_FS_IO_RANGEM) */
		} /* if (CISTPL_CFTABLE_TPCE_FS_IOM) */

		/*
		 * Parse any IRQ information.  If there is IRQ inforamtion,
		 *	set a flag in the global flag field to let the
		 *	caller know.
		 */
		if (tpce_fs & CISTPL_CFTABLE_TPCE_FS_IRQM) {
			cistpl_cftable_entry_irq_t *irq = &ce->irq;

			ce->flags |= CISTPL_CFTABLE_TPCE_FS_IRQ;
			tpce_ir = GET_BYTE(tp);
			/*
			 * Pass any IRQ flags that are in the tuple directly
			 *	to the caller.
			 */
			irq->flags = tpce_ir;
			/*
			 * Check for and parse the extended IRQ bitmask
			 *	if it exists.
			 */
			if (tpce_ir & CISTPL_CFTABLE_TPCE_FS_IRQ_MASKM) {
				irq->irqs = GET_BYTE(tp) & 0x0ff;
				irq->irqs |= (GET_BYTE(tp) << 8)&0x0ff00;
			} else {
				irq->irqs = (1<< (tpce_ir&0x0f));
			}
		} /* if (CISTPL_CFTABLE_TPCE_FS_IRQM) */

		/*
		 * Parse any memory information.
		 *
		 * XXX - should be a cleaner way to parse this information.
		 */
		if (ce->flags & CISTPL_CFTABLE_TPCE_FS_MEM) {
			cistpl_cftable_entry_mem_t *mem = &ce->mem;
			cistpl_cftable_entry_mem_window_t *win;
			/*
			 * Switch on the type of memory description
			 *	information that is available.
			 */
			switch (tpce_fs & CISTPL_CFTABLE_TPCE_FS_MEMM) {
				/*
				 * variable length memory space description
				 */
			case CISTPL_CFTABLE_TPCE_FS_MEM3M:
				mem->flags |= CISTPL_CFTABLE_TPCE_FS_MEM3;
				/* memory space descriptor */
				tpce_msd = GET_BYTE(tp);
				mem->windows = ((tpce_msd &
					(CISTPL_CFTABLE_ENTRY_MAX_MEM_WINDOWS -
								1)) + 1);
				/*
				 * If there's host address information, let
				 *	the caller know.
				 */
				if (tpce_msd & CISTPL_CFTABLE_TPCE_FS_MEM_HOSTM)
					mem->flags |=
						CISTPL_CFTABLE_TPCE_FS_MEM_HOST;
				/*
				 * Cycle through each window space description
				 *	and collect all the interesting bits.
				 */
				for (i = 0; i < mem->windows; i++) {
					win = &mem->window[i];
					win->length = 0;
					win->card_addr = 0;
					win->host_addr = 0;
					/*
					 * Gather the length information.
					 *	It's OK if there's no length
					 *	information in which case this
					 *	loop will never execute.
					 */
					for (j = 0; j <
						(int)((tpce_msd>>3)&3); j++)
						win->length |= (GET_BYTE(tp) <<
						cistpl_cftable_shift_table[j]);
					/*
					 * Gather the card address information.
					 *	It's OK if there's no card
					 *	address information in which
					 *	case this loop will never
					 *	execute.
					 */
					for (j = 0; j <
						(int)((tpce_msd>>5)&3); j++)
						win->card_addr |=
							(GET_BYTE(tp) <<
						cistpl_cftable_shift_table[j]);
					/*
					 * If there's a host address
					 *	description, grab that
					 *	as well.
					 */
					if (mem->flags &
					    CISTPL_CFTABLE_TPCE_FS_MEM_HOST) {
						/*
						 * Gather the host address
						 *	information.  It's OK
						 *	if there's no host
						 *	address information in
						 *	which case this loop
						 *	will never execute.
						 * Note that we use the card
						 *	address size to
						 *	determine how many
						 *	bytes of host address
						 *	are present.
						 */
						for (j = 0; j <
							(int)((tpce_msd>>5)&3);
									j++)
							win->host_addr |=
							(GET_BYTE(tp) <<
						cistpl_cftable_shift_table[j]);
					} else {
						/*
						 * No host address information,
						 *	so the host address is
						 *	equal to the card
						 *	address.
						 */
						win->host_addr = win->card_addr;
					}
				} /* for (i<mem->windows) */
				break;
				/*
				 * single length and card base address specified
				 */
			case CISTPL_CFTABLE_TPCE_FS_MEM2M:
				mem->flags |= CISTPL_CFTABLE_TPCE_FS_MEM2;
				win = &mem->window[0];
				mem->windows = 1;
				/*
				 * Construct the size of the window.
				 */
				win->length = GET_BYTE(tp);
				win->length |= (GET_BYTE(tp)<<8);
				win->length *=
					CISTPL_CFTABLE_TPCE_FS_MEM_PGSIZE;

				/*
				 * Construct the card base address.
				 */
				win->card_addr = GET_BYTE(tp);
				win->card_addr |= (GET_BYTE(tp)<<8);
				win->card_addr *=
					CISTPL_CFTABLE_TPCE_FS_MEM_PGSIZE;

				/*
				 * In this mode, both the host base address
				 *	and the card base address are equal.
				 */
				win->host_addr = win->card_addr;
				break;
				/*
				 * single length specified
				 */
			case CISTPL_CFTABLE_TPCE_FS_MEM1M:
				mem->flags |= CISTPL_CFTABLE_TPCE_FS_MEM1;
				win = &mem->window[0];
				mem->windows = 1;
				win->card_addr = 0;
				win->host_addr = 0;
				/*
				 * Construct the size of the window.
				 */
				win->length = GET_BYTE(tp);
				win->length |= (GET_BYTE(tp)<<8);
				win->length *=
					CISTPL_CFTABLE_TPCE_FS_MEM_PGSIZE;
				break;
			} /* switch (CISTPL_CFTABLE_TPCE_FS_MEMM) */
		} /* if (CISTPL_CFTABLE_TPCE_FS_MEM) */

		/*
		 * Check for and parse any miscellaneous information.
		 *
		 * We only understand how to parse the first
		 *	CISTPL_CFTABLE_TPCE_FS_MISC_MAX extension
		 *	bytes specified in the PC Card 95 standard;
		 *	we throw away any other extension bytes that
		 *	are past these bytes.
		 * XXX Note that the assumption here is that the
		 *	size of cistpl_cftable_entry_misc_t->flags
		 *	is at least CISTPL_CFTABLE_TPCE_FS_MISC_MAX
		 *	bytes in length.
		 */
		if (tpce_fs & CISTPL_CFTABLE_TPCE_FS_MISCM) {
		    cistpl_cftable_entry_misc_t *misc = &ce->misc;
		    int mb = CISTPL_CFTABLE_TPCE_FS_MISC_MAX;

		    ce->flags |= CISTPL_CFTABLE_TPCE_FS_MISC;
		    misc->flags = 0;

		    do {
			if (mb) {
			    misc->flags = (misc->flags << 8) | LOOK_BYTE(tp);
			    mb--;
			}
		    } while ((GET_BYTE(tp) & CISTPL_EXT_BIT) &&
				(!(tp->flags & CISTPLF_MEM_ERR)));

			/*
			 * Check to see if we tried to read past the
			 *	end of the tuple data; if we have,
			 *	there's no point in trying to parse
			 *	any more of the tuple.
			 */
		    if (tp->flags & CISTPLF_MEM_ERR)
			return (HANDTPL_ERROR);
		} /* if (CISTPL_CFTABLE_TPCE_FS_MISCM) */

		/*
		 * Check for and parse any additional subtuple
		 *	information. We know that there is
		 *	additional information if we haven't
		 *	reached the end of the tuple data area
		 *	and if the additional information is
		 *	in standard tuple format.
		 * If we don't recognize the additional info,
		 *	then just silently ignore it, don't
		 *	flag it as an error.
		 */
#ifdef	PARSE_STCE_TUPLES
		if (GET_LEN(tp) > 0) {

		ce->flags |= CISTPL_CFTABLE_TPCE_FS_STCE_EV
		ce->flags |= CISTPL_CFTABLE_TPCE_FS_STCE_PD
#endif

	} /* if (HANDTPL_PARSE_LTUPLE) */

	return (CISTPLF_NOERROR);
}

/*
 * cistpl_pd_parse - read and parse a power description structure
 *
 *	cisdata_t **ddp - pointer to pointer tuple data area
 *	cistpl_cftable_entry_pwr_t *pd - pointer to local power description
 *					structure
 */
static void
cistpl_pd_parse(cistpl_t *tp, cistpl_cftable_entry_pwr_t *pd)
{
	cisdata_t pdesc;

	pdesc = GET_BYTE(tp);	/* power description selector */

	/* nominal supply voltage */
	if (pdesc & CISTPL_CFTABLE_PD_NOMV) {
		pd->nomV = cistpl_expd_parse(tp, &pd->nomV_flags) / 100;
		pd->nomV_flags |= (pdesc | CISTPL_CFTABLE_PD_EXISTS);
	}

	/* minimum supply voltage */
	if (pdesc & CISTPL_CFTABLE_PD_MINV) {
		pd->minV = cistpl_expd_parse(tp, &pd->minV_flags) / 100;
		pd->minV_flags |= (pdesc | CISTPL_CFTABLE_PD_EXISTS);
	}

	/* maximum supply voltage */
	if (pdesc & CISTPL_CFTABLE_PD_MAXV) {
		pd->maxV = cistpl_expd_parse(tp, &pd->maxV_flags) / 100;
		pd->maxV_flags |= (pdesc | CISTPL_CFTABLE_PD_EXISTS);
	}

	/* continuous supply current */
	if (pdesc & CISTPL_CFTABLE_PD_STATICI) {
		pd->staticI_flags |= CISTPL_CFTABLE_PD_MUL10;
		pd->staticI = cistpl_expd_parse(tp, &pd->staticI_flags);
		pd->staticI_flags |= (pdesc | CISTPL_CFTABLE_PD_EXISTS);
	}

	/* maximum current required averaged over 1 second */
	if (pdesc & CISTPL_CFTABLE_PD_AVGI) {
		pd->avgI_flags |= CISTPL_CFTABLE_PD_MUL10;
		pd->avgI = cistpl_expd_parse(tp, &pd->avgI_flags);
		pd->avgI_flags |= (pdesc | CISTPL_CFTABLE_PD_EXISTS);
	}

	/* maximum current required averaged over 10mS */
	if (pdesc & CISTPL_CFTABLE_PD_PEAKI) {
		pd->peakI_flags |= CISTPL_CFTABLE_PD_MUL10;
		pd->peakI = cistpl_expd_parse(tp, &pd->peakI_flags);
		pd->peakI_flags |= (pdesc | CISTPL_CFTABLE_PD_EXISTS);
	}

	/* power down supply curent required */
	if (pdesc & CISTPL_CFTABLE_PD_PDOWNI) {
		pd->pdownI_flags |= CISTPL_CFTABLE_PD_MUL10;
		pd->pdownI = cistpl_expd_parse(tp, &pd->pdownI_flags);
		pd->pdownI_flags |= (pdesc | CISTPL_CFTABLE_PD_EXISTS);
	}
}

/*
 * cistpl_expd_parse - read and parse an extended power description structure
 *
 *	cistpl_t *tp - pointer to pointer tuple data area
 *	int *flags - flags that get for this parameter:
 *			CISTPL_CFTABLE_PD_NC_SLEEP - no connection on
 *							sleep/power down
 *			CISTPL_CFTABLE_PD_ZERO - zero value required
 *			CISTPL_CFTABLE_PD_NC - no connection ever
 *
 * The power consumption is returned in the following units:
 *
 *				voltage - milliVOLTS
 *				current - microAMPS
 */
extern cistpl_pd_struct_t cistpl_pd_struct;

uint32_t
cistpl_expd_parse(cistpl_t *tp, uint32_t *flags)
{
	cisdata_t pdesc;
	uint32_t exponent, mantisa, val, digits = 0;

	/*
	 * Get the power description parameter byte and break it up
	 *	into mantissa and exponent.
	 */
	pdesc = GET_BYTE(tp);
	exponent = pdesc&7;
	mantisa = (pdesc>>3)&0x0f;

	if (pdesc & CISTPL_EXT_BIT) {
		do {
			if (LOOK_BYTE(tp) <= 0x63)
				digits = LOOK_BYTE(tp);
			if (LOOK_BYTE(tp) == CISTPL_CFTABLE_PD_NC_SLEEPM)
				*flags |= CISTPL_CFTABLE_PD_NC_SLEEP;
			if (LOOK_BYTE(tp) == CISTPL_CFTABLE_PD_ZEROM)
				*flags |= CISTPL_CFTABLE_PD_ZERO;
			if (LOOK_BYTE(tp) == CISTPL_CFTABLE_PD_NCM)
				*flags |= CISTPL_CFTABLE_PD_NC;
		} while (GET_BYTE(tp) & CISTPL_EXT_BIT);
	}

	val = CISTPL_PD_MAN(mantisa) * CISTPL_PD_EXP(exponent);

	/*
	 * If we have to multiply the power value by ten, then just
	 *	don't bother dividing.
	 */
	if (! (*flags & CISTPL_CFTABLE_PD_MUL10))
		val = val/10;	/* do this since our mantissa table is X 10 */

	/*
	 * If we need to add some digits to the right of the decimal, do
	 *	that here.
	 */
	if (exponent)
		val = val + (digits * CISTPL_PD_EXP(exponent-1));

	val /= 1000;

	return (val);
}

/*
 * cistpl_devspeed - returns device speed in nS
 *
 *	cistpl_t *tp - tuple pointer.
 *	cisdata_t spindex - device speed table index
 *	int flags - operation flags
 *		CISTPL_DEVSPEED_TABLE:
 *		    Use the spindex argument as an index into a simple
 *			device speed table. ref: PCMCIA Release 2.01
 *			Card Metaformat pg. 5-14 table 5-12.
 *		    When this flag is set, the spindex argument is ignored.
 *		CISTPL_DEVSPEED_EXT:
 *		    Use the tp argument to access the
 *			tuple data area containing an extended speed
 *			code table.  ref: PCMCIA Release 2.01 Card
 *			Metaformat pg. 5-15 table 5-13.
 *		    The tp->read argument must point to the first byte of
 *			an extended speed code table.
 *		    When this flag is set, the spindex argument is
 *			used as a power-of-10 scale factor.  We only allow
 *			a maximum scale factor of 10^16.
 *
 * The device speed is returned in nS for all combinations of flags and
 *	speed table entries.
 *
 * Note if you pass the CISTPL_DEVSPEED_TABLE with a spindex index that
 *	refers to an extended speed table, you will get back an undefined
 *	speed value.
 */
extern cistpl_devspeed_struct_t cistpl_devspeed_struct;

uint32_t
cistpl_devspeed(cistpl_t *tp, cisdata_t spindex, uint32_t flags)
{
	int scale = 1, first;
	cisdata_t exspeed;
	int exponent, mantisa;
	uint32_t speed;

	switch (flags) {
	case CISTPL_DEVSPEED_TABLE:
		speed = CISTPL_DEVSPEED_TBL(spindex);
		break;
	case CISTPL_DEVSPEED_EXT:
		do {
			exspeed = GET_BYTE(tp);
			first = 1;
			if (first) {
				/*
				 * XXX - ugh! we don't understand additional
				 *	exspeed bytes
				 */
				first = 0;
				exponent = (exspeed & 0x07);
				mantisa = (exspeed >> 3) & 0x0f;
				spindex &= 0x0f;	/* only allow 10^16 */
				while (spindex--)
					scale *= 10;
			} /* if (first) */
		} while (exspeed & CISTPL_EXT_BIT);
		speed = scale * CISTPL_DEVSPEED_MAN(mantisa) *
						CISTPL_DEVSPEED_EXP(exponent);
		speed = speed/10;	/* XXX - mantissa table is all X 10 */
		break;
	default:
		break;
	}

	return (speed);
}

/*
 * cistpl_vers_2_handler - handler for the CISTPL_VERS_2 tuple
 *
 *	void *arg - points to a XXX where the information is stuffed into
 */
uint32_t
cistpl_vers_2_handler(cistpl_callout_t *co, cistpl_t *tp,
					uint32_t flags, void *arg)
{
	/*
	 * nothing special about our flags, so just call the
	 *	generic handler for this
	 */
	if (flags & HANDTPL_SET_FLAGS)
		return (cis_no_tuple_handler(co, tp, flags, arg));

	/*
	 * We don't currently validate this tuple. This call will
	 *	always set tp->flags |= CISTPLF_VALID.
	 */
	if (flags & HANDTPL_COPY_DONE)
		return (cis_no_tuple_handler(co, tp, flags, arg));

	if (flags & HANDTPL_PARSE_LTUPLE) {
		cistpl_vers_2_t *cs = (cistpl_vers_2_t *)arg;

		RESET_TP(tp);

		cs->vers = GET_BYTE(tp);
		cs->comply = GET_BYTE(tp);
		cs->dindex = GET_SHORT(tp);

		cs->reserved = GET_SHORT(tp);

		cs->vspec8 = GET_BYTE(tp);
		cs->vspec9 = GET_BYTE(tp);
		cs->nhdr = GET_BYTE(tp);

		(void) strcpy(cs->oem, cis_getstr(tp));

		if (GET_LEN(tp) > 0)
		    (void) strcpy(cs->info, cis_getstr(tp));
		else
		    (void) strcpy(cs->info, "(no info)");
	}

	return (CISTPLF_NOERROR);
}

/*
 * cistpl_jedec_handler - handler for JEDEC C and JEDEC A tuples
 *
 *	void *arg - points to a XXX where the information is stuffed into
 */
uint32_t
cistpl_jedec_handler(cistpl_callout_t *co, cistpl_t *tp,
					uint32_t flags, void *arg)
{
	/*
	 * nothing special about our flags, so just call the
	 *	generic handler for this
	 */
	if (flags & HANDTPL_SET_FLAGS)
		return (cis_no_tuple_handler(co, tp, flags, arg));

	/*
	 * We don't currently validate this tuple. This call will
	 *	always set tp->flags |= CISTPLF_VALID.
	 */
	if (flags & HANDTPL_COPY_DONE)
		return (cis_no_tuple_handler(co, tp, flags, arg));

	if (flags & HANDTPL_PARSE_LTUPLE) {
		int nid;
		cistpl_jedec_t *cs = (cistpl_jedec_t *)arg;

		RESET_TP(tp);

		for (nid = 0; GET_LEN(tp) > 0 &&
					nid < CISTPL_JEDEC_MAX_IDENTIFIERS &&
					LOOK_BYTE(tp) != 0xFF; nid++) {
			cs->jid[nid].id = GET_BYTE(tp);
			cs->jid[nid].info = GET_BYTE(tp);
		}
		cs->nid = nid;
	}

	return (CISTPLF_NOERROR);
}

/*
 * cistpl_format_handler - handler for the CISTPL_FORMAT and
 *				CISTPL_FORMAT_A tuples
 */
uint32_t
cistpl_format_handler(cistpl_callout_t *co, cistpl_t *tp,
					uint32_t flags, void *arg)
{
	/*
	 * nothing special about our flags, so just call the
	 *	generic handler for this
	 */
	if (flags & HANDTPL_SET_FLAGS)
		return (cis_no_tuple_handler(co, tp, flags, arg));

	/*
	 * We don't currently validate this tuple. This call will
	 *	always set tp->flags |= CISTPLF_VALID.
	 */
	if (flags & HANDTPL_COPY_DONE)
		return (cis_no_tuple_handler(co, tp, flags, arg));

	if (flags & HANDTPL_PARSE_LTUPLE) {
		cistpl_format_t *cs = (cistpl_format_t *)arg;

		RESET_TP(tp);

		cs->type = GET_BYTE(tp);
		cs->edc_length = LOOK_BYTE(tp) & EDC_LENGTH_MASK;
		cs->edc_type = ((uint32_t)GET_BYTE(tp) >> EDC_TYPE_SHIFT) &
								EDC_TYPE_MASK;
		cs->offset = GET_LONG(tp);
		cs->nbytes = GET_LONG(tp);

		switch (cs->type) {
		case TPLFMTTYPE_DISK:
			cs->dev.disk.bksize = GET_SHORT(tp);
			cs->dev.disk.nblocks = GET_LONG(tp);
			cs->dev.disk.edcloc = GET_LONG(tp);
			break;

		case TPLFMTTYPE_MEM:
			cs->dev.mem.flags = GET_BYTE(tp);
			cs->dev.mem.reserved = GET_BYTE(tp);
			cs->dev.mem.address = (caddr_t)GET_LONG(tp);
			cs->dev.disk.edcloc = GET_LONG(tp);
			break;
		default:
			/* don't know about any other type */
			break;
		}
	}

	return (CISTPLF_NOERROR);
}

/*
 * cistpl_geometry_handler - handler for the CISTPL_GEOMETRY tuple
 *
 *	void *arg - points to a XXX where the information is stuffed into
 */
uint32_t
cistpl_geometry_handler(cistpl_callout_t *co, cistpl_t *tp, uint32_t flags,
								void *arg)
{
	/*
	 * nothing special about our flags, so just call the
	 *	generic handler for this
	 */
	if (flags & HANDTPL_SET_FLAGS)
		return (cis_no_tuple_handler(co, tp, flags, arg));

	/*
	 * We don't currently validate this tuple. This call will
	 *	always set tp->flags |= CISTPLF_VALID.
	 */
	if (flags & HANDTPL_COPY_DONE)
		return (cis_no_tuple_handler(co, tp, flags, arg));

	if (flags & HANDTPL_PARSE_LTUPLE) {
		cistpl_geometry_t *cs = (cistpl_geometry_t *)arg;

		RESET_TP(tp);
		cs->spt = GET_BYTE(tp);
		cs->tpc = GET_BYTE(tp);
		cs->ncyl = GET_SHORT(tp);
	}
	return (CISTPLF_NOERROR);
}

/*
 * cistpl_byteorder_handler - handler for the CISTPL_BYTEORDER tuple
 *
 *	void *arg - points to a XXX where the information is stuffed into
 */
uint32_t
cistpl_byteorder_handler(cistpl_callout_t *co, cistpl_t *tp, uint32_t flags,
								void *arg)
{
	/*
	 * nothing special about our flags, so just call the
	 *	generic handler for this
	 */
	if (flags & HANDTPL_SET_FLAGS)
		return (cis_no_tuple_handler(co, tp, flags, arg));

	/*
	 * We don't currently validate this tuple. This call will
	 *	always set tp->flags |= CISTPLF_VALID.
	 */
	if (flags & HANDTPL_COPY_DONE)
		return (cis_no_tuple_handler(co, tp, flags, arg));

	if (flags & HANDTPL_PARSE_LTUPLE) {
		cistpl_byteorder_t *cs = (cistpl_byteorder_t *)arg;

		RESET_TP(tp);
		cs->order = GET_BYTE(tp);
		cs->map = GET_BYTE(tp);
	}
	return (CISTPLF_NOERROR);
}

/*
 * cistpl_date_handler - handler for CISTPL_DATE card format tuple
 *
 *	void *arg - points to a cistpl_date_t * where the
 *			information is stuffed into
 */
uint32_t
cistpl_date_handler(cistpl_callout_t *co, cistpl_t *tp,
					uint32_t flags, void *arg)
{
	/*
	 * nothing special about our flags, so just call the
	 *	generic handler for this
	 */
	if (flags & HANDTPL_SET_FLAGS)
		return (cis_no_tuple_handler(co, tp, flags, arg));

	/*
	 * We don't currently validate this tuple. This call will
	 *	always set tp->flags |= CISTPLF_VALID.
	 */
	if (flags & HANDTPL_COPY_DONE)
		return (cis_no_tuple_handler(co, tp, flags, arg));

	if (flags & HANDTPL_PARSE_LTUPLE) {
		cistpl_date_t *cs = (cistpl_date_t *)arg;

		RESET_TP(tp);
		cs->time = GET_SHORT(tp);
		cs->day = GET_SHORT(tp);
	}
	return (CISTPLF_NOERROR);
}

/*
 * cistpl_battery_handler - handler for CISTPL_BATTERY battery replacement
 *				date tuple
 *
 *	void *arg - points to a cistpl_battery_t * where the
 *			information is stuffed into
 */
uint32_t
cistpl_battery_handler(cistpl_callout_t *co, cistpl_t *tp,
					uint32_t flags, void *arg)
{
	/*
	 * nothing special about our flags, so just call the
	 *	generic handler for this
	 */
	if (flags & HANDTPL_SET_FLAGS)
		return (cis_no_tuple_handler(co, tp, flags, arg));

	/*
	 * We don't currently validate this tuple. This call will
	 *	always set tp->flags |= CISTPLF_VALID.
	 */
	if (flags & HANDTPL_COPY_DONE)
		return (cis_no_tuple_handler(co, tp, flags, arg));

	if (flags & HANDTPL_PARSE_LTUPLE) {
		cistpl_battery_t *cs = (cistpl_battery_t *)arg;

		RESET_TP(tp);
		cs->rday = GET_SHORT(tp);
		cs->xday = GET_SHORT(tp);
	}
	return (CISTPLF_NOERROR);
}

/*
 * cistpl_org_handler - handler for CISTPL_ORG data organization tuple
 *
 *	void *arg - points to a cistpl_org_t * where the
 *			information is stuffed into
 */
uint32_t
cistpl_org_handler(cistpl_callout_t *co, cistpl_t *tp,
					uint32_t flags, void *arg)
{
	/*
	 * nothing special about our flags, so just call the
	 *	generic handler for this
	 */
	if (flags & HANDTPL_SET_FLAGS)
		return (cis_no_tuple_handler(co, tp, flags, arg));

	/*
	 * We don't currently validate this tuple. This call will
	 *	always set tp->flags |= CISTPLF_VALID.
	 */
	if (flags & HANDTPL_COPY_DONE)
		return (cis_no_tuple_handler(co, tp, flags, arg));

	if (flags & HANDTPL_PARSE_LTUPLE) {
		cistpl_org_t *cs = (cistpl_org_t *)arg;

		RESET_TP(tp);
		cs->type = GET_BYTE(tp);

		(void) strcpy(cs->desc, cis_getstr(tp));
	}

	return (CISTPLF_NOERROR);
}


/*
 * cistpl_manfid_handler - handler for CISTPL_MANFID, the manufacturer ID tuple
 *
 *	void *arg - points to a XXX where the information is stuffed into
 */
uint32_t
cistpl_manfid_handler(cistpl_callout_t *co, cistpl_t *tp,
					uint32_t flags, void *arg)
{
	/*
	 * nothing special about our flags, so just call the
	 *	generic handler for this
	 */
	if (flags & HANDTPL_SET_FLAGS)
		return (cis_no_tuple_handler(co, tp, flags, arg));

	/*
	 * We don't currently validate this tuple. This call will
	 *	always set tp->flags |= CISTPLF_VALID.
	 */
	if (flags & HANDTPL_COPY_DONE)
		return (cis_no_tuple_handler(co, tp, flags, arg));

	if (flags & HANDTPL_PARSE_LTUPLE) {
		cistpl_manfid_t *cs = (cistpl_manfid_t *)arg;

		RESET_TP(tp);
		cs->manf = GET_SHORT(tp);
		cs->card = GET_SHORT(tp);
	}
	return (CISTPLF_NOERROR);
}

/*
 * cistpl_funcid_handler - handler for CISTPL_FUNCID
 *
 *	void *arg - points to a XXX where the information is stuffed into
 */
uint32_t
cistpl_funcid_handler(cistpl_callout_t *co, cistpl_t *tp,
					uint32_t flags, void *arg)
{
	/*
	 * nothing special about our flags, so just call the
	 *	generic handler for this
	 */
	if (flags & HANDTPL_SET_FLAGS)
		return (cis_no_tuple_handler(co, tp, flags, arg));

	/*
	 * We don't currently validate this tuple. This call will
	 *	always set tp->flags |= CISTPLF_VALID.
	 */
	if (flags & HANDTPL_COPY_DONE)
		return (cis_no_tuple_handler(co, tp, flags, arg));

	if (flags & HANDTPL_PARSE_LTUPLE) {
		cistpl_funcid_t *cs = (cistpl_funcid_t *)arg;

		RESET_TP(tp);

		cs->function = GET_BYTE(tp);
		cs->sysinit = GET_BYTE(tp);
	}
	return (CISTPLF_NOERROR);
}


/*
 * cistpl_funce_serial_handler - handler for the CISTPL_FUNCE/SERIAL tuple
 *
 *	void *arg - points to a XXX where the information is stuffed into
 */
uint32_t
cistpl_funce_serial_handler(cistpl_callout_t *co, cistpl_t *tp,
						uint32_t flags, void *arg)
{
	int subfunction;

	/*
	 * nothing special about our flags, so just call the
	 *	generic handler for this
	 */
	if (flags & HANDTPL_SET_FLAGS)
		return (cis_no_tuple_handler(co, tp, flags, arg));

	/*
	 * We don't currently validate this tuple. This call will
	 *	always set tp->flags |= CISTPLF_VALID.
	 */
	if (flags & HANDTPL_COPY_DONE)
		return (cis_no_tuple_handler(co, tp, flags, arg));

	if (flags & HANDTPL_PARSE_LTUPLE) {
		cistpl_funce_t *cs = (cistpl_funce_t *)arg;

		RESET_TP(tp);

		cs->function = TPLFUNC_SERIAL;
		cs->subfunction = subfunction = GET_BYTE(tp);
		switch (subfunction & 0xF) {
		case TPLFE_SUB_SERIAL:
		case TPLFE_CAP_SERIAL_DATA:
		case TPLFE_CAP_SERIAL_FAX:
		case TPLFE_CAP_SERIAL_VOICE:
			cs->data.serial.ua = GET_BYTE(tp);
			cs->data.serial.uc = GET_SHORT(tp);
			break;
		case TPLFE_SUB_MODEM_COMMON:
		case TPLFE_CAP_MODEM_DATA:
		case TPLFE_CAP_MODEM_FAX:
		case TPLFE_CAP_MODEM_VOICE:
			cs->data.modem.fc = GET_BYTE(tp);
			cs->data.modem.cb = (GET_BYTE(tp) + 1) * 4;
			cs->data.modem.eb = GET_INT24(tp);
			cs->data.modem.tb = GET_INT24(tp);
			break;
		case TPLFE_SUB_MODEM_DATA:
			cs->data.data_modem.ud = GET_BE_SHORT(tp) * 75;
			cs->data.data_modem.ms = GET_SHORT(tp);
			cs->data.data_modem.em = GET_BYTE(tp);
			cs->data.data_modem.dc = GET_BYTE(tp);
			cs->data.data_modem.cm = GET_BYTE(tp);
			cs->data.data_modem.ex = GET_BYTE(tp);
			cs->data.data_modem.dy = GET_BYTE(tp);
			cs->data.data_modem.ef = GET_BYTE(tp);
			for (cs->data.data_modem.ncd = 0;
				GET_LEN(tp) > 0 && cs->data.data_modem.ncd < 16;
						cs->data.data_modem.ncd++)
				if (LOOK_BYTE(tp) != 255) {
					cs->data.data_modem.cd[
						cs->data.data_modem.ncd] =
								GET_BYTE(tp);
				} else {
					GET_BYTE(tp);
					break;
				}
			break;
		case TPLFE_SUB_MODEM_FAX:
			cs->data.fax.uf = GET_BE_SHORT(tp) * 75;
			cs->data.fax.fm = GET_BYTE(tp);
			cs->data.fax.fy = GET_BYTE(tp);
			cs->data.fax.fs = GET_SHORT(tp);
			for (cs->data.fax.ncf = 0;
				GET_LEN(tp) > 0 && cs->data.fax.ncf < 16;
							cs->data.fax.ncf++)
				if (LOOK_BYTE(tp) != 255) {
					cs->data.fax.cf[cs->data.fax.ncf] =
								GET_BYTE(tp);
				} else {
					GET_BYTE(tp);
					break;
				}
			break;
		case TPLFE_SUB_VOICE:
			cs->data.voice.uv = GET_BE_SHORT(tp) * 75;
			for (cs->data.voice.nsr = 0; LOOK_BYTE(tp) != 0 &&
				GET_LEN(tp) >= 2;
						cs->data.voice.nsr++) {
				cs->data.voice.sr[cs->data.voice.nsr] =
					GET_BYTE(tp) * 1000;
				cs->data.voice.sr[cs->data.voice.nsr] +=
					GET_BYTE(tp) * 100;
			}
			for (cs->data.voice.nss = 0; LOOK_BYTE(tp) != 0 &&
				GET_LEN(tp) >= 2;
						cs->data.voice.nss++) {
				cs->data.voice.ss[cs->data.voice.nss] =
					GET_BYTE(tp) * 10;
				cs->data.voice.ss[cs->data.voice.nss] +=
								GET_BYTE(tp);
			}
			for (cs->data.voice.nsc = 0; LOOK_BYTE(tp) != 0 &&
				GET_LEN(tp) >= 1;
						cs->data.voice.nsc++) {
				cs->data.voice.sc[cs->data.voice.nsc] =
								GET_BYTE(tp);
			}
			break;
		default:
			break;
		}
	}
	return (CISTPLF_NOERROR);
}

/*
 * cistpl_funce_lan_handler - handler for the CISTPL_FUNCE/LAN tuple
 *
 *	void *arg - points to a XXX where the information is stuffed into
 */
uint32_t
cistpl_funce_lan_handler(cistpl_callout_t *co, cistpl_t *tp, uint32_t flags,
								void *arg)
{
	int subfunction;

	/*
	 * nothing special about our flags, so just call the
	 *	generic handler for this
	 */
	if (flags & HANDTPL_SET_FLAGS)
		return (cis_no_tuple_handler(co, tp, flags, arg));

	/*
	 * We don't currently validate this tuple. This call will
	 *	always set tp->flags |= CISTPLF_VALID.
	 */
	if (flags & HANDTPL_COPY_DONE)
		return (cis_no_tuple_handler(co, tp, flags, arg));

	if (flags & HANDTPL_PARSE_LTUPLE) {
		int i;
		cistpl_funce_t *cs = (cistpl_funce_t *)arg;

		RESET_TP(tp);

		cs->function = TPLFUNC_LAN;
		cs->subfunction = subfunction = GET_BYTE(tp);

		switch (subfunction) {
		case TPLFE_NETWORK_INFO:
			cs->data.lan.tech = GET_BYTE(tp);
			cs->data.lan.speed = GET_BYTE(tp);
			i = GET_BYTE(tp);
			if (i < 24) {
				cs->data.lan.speed <<= i;
			} else {
				/*
				 * if speed is too large a value
				 * to hold in a uint32 flag it and
				 * store as [mantissa][exponent]
				 * in least significant 16 bits
				 */
				cs->data.lan.speed = 0x80000000 |
					(cs->data.lan.speed << 8) | i;
			}
			cs->data.lan.media = GET_BYTE(tp);
			cs->data.lan.con = GET_BYTE(tp);
			cs->data.lan.id_sz = GET_BYTE(tp);
			if (cs->data.lan.id_sz <= 16) {
				for (i = 0; i < cs->data.lan.id_sz; i++)
					cs->data.lan.id[i] = GET_BYTE(tp);
			}
			break;
		default:
				/* unknown LAN tuple type */
			return (CISTPLF_UNKNOWN);
		}
	}
	return (CISTPLF_NOERROR);
}

/*
 * cistpl_linktarget_handler - handler for CISTPL_LINKTARGET tuple
 *
 *	void *arg - points to a cistpl_linktarget_t * where the
 *			information is stuffed into
 *
 *	If HANDTPL_COPY_DONE is set, we just validate the tuple but
 *		do not return any values.
 *	If HANDTPL_PARSE_LTUPLE is set, we validate the tuple and
 *		return the parsed tuple data if the tuple is valid.
 *
 *	If the tuple link field is invalid, the CISTPLF_LINK_INVALID flag
 *		will be set in the tp->flags field and HANDTPL_ERROR
 *		will be returned.
 *
 *	If the tuple data body is invalid, the CISTPLF_PARAMS_INVALID flag
 *		will be set in the tp->flags field and HANDTPL_ERROR
 *		will be returned.
 *
 *	The tuple is considered invalid if it's link field is less than
 *		MIN_LINKTARGET_LENGTH or if the data body of the tuple
 *		does not contain the pattern CISTPL_LINKTARGET_MAGIC.
 *
 * XXX At some point we should revisit this to see if we can call
 *	cis_validate_longlink_acm instead of doing the validation
 *	in both places.
 */
uint32_t
cistpl_linktarget_handler(cistpl_callout_t *co, cistpl_t *tp, uint32_t flags,
								void *arg)
{
	/*
	 * nothing special about our flags, so just call the
	 *	generic handler for this
	 */
	if (flags & HANDTPL_SET_FLAGS)
		return (cis_no_tuple_handler(co, tp, flags, arg));

	/*
	 * Validate the tuple for both the HANDTPL_COPY_DONE case and
	 *	the HANDTPL_PARSE_LTUPLE case. Only return data in
	 *	the HANDTPL_PARSE_LTUPLE case.
	 */
	if (flags & (HANDTPL_COPY_DONE | HANDTPL_PARSE_LTUPLE)) {
		uchar_t *cp;
		cisdata_t tl;

		if ((tl = tp->len) >= (cisdata_t)MIN_LINKTARGET_LENGTH) {
			cisdata_t *ltm = (cisdata_t *)CISTPL_LINKTARGET_MAGIC;
			int i;

			RESET_TP(tp);

			/*
			 * Save the start address of this string in case
			 *	the tuple turns out to be OK since we
			 *	need to pass this address to the caller.
			 */
			cp = GET_BYTE_ADDR(tp);

			/*
			 * Check each byte of the tuple body to see if it
			 *	matches what should be in a valid tuple.
			 *	Note that we can't assume that this magic
			 *	pattern is a string and we also only need
			 *	to be sure that MIN_LINKTARGET_LENGTH bytes
			 *	match; all bytes following this magic number
			 *	in this tuple are ignored.
			 */
			for (i = 0; i < MIN_LINKTARGET_LENGTH; i++) {
				if (GET_BYTE(tp) != *ltm++) {
					tp->flags |= CISTPLF_PARAMS_INVALID;
					return (HANDTPL_ERROR);
				}
			} /* MIN_LINKTARGET_LENGTH */

			/*
			 * This tuple is valid.
			 */
			if (flags & HANDTPL_COPY_DONE)
				tp->flags |= CISTPLF_VALID;

			/*
			 * If we're also parsing this tuple, then
			 *	setup the return values.
			 */
			if (flags & HANDTPL_PARSE_LTUPLE) {
				cistpl_linktarget_t *cs =
						(cistpl_linktarget_t *)arg;

				cs->length = tl;
				(void) strncpy(cs->tpltg_tag, (char *)cp,
								cs->length);
				cs->tpltg_tag[cs->length] = NULL;

			} /* HANDTPL_PARSE_LTUPLE */

		} else {

			tp->flags |= CISTPLF_LINK_INVALID;
			return (HANDTPL_ERROR);

		} /* CISTPL_LINKTARGET */

	} /* (HANDTPL_COPY_DONE | HANDTPL_PARSE_LTUPLE) */

	return (CISTPLF_NOERROR);
}

/*
 * cistpl_longlink_ac_handler - handler for CISTPL_LONGLINK_A and
 *				CISTPL_LONGLINK_C tuples
 *
 *	void *arg - points to a cistpl_longlink_ac_t * where the
 *			information is stuffed into
 *
 *	If the passed in tuple is CISTPL_LONGLINK_A the CISTPL_LONGLINK_AC_AM
 *		flag in cistpl_longlink_ac_t->flags is set.
 *	If the passed in tuple is CISTPL_LONGLINK_C the CISTPL_LONGLINK_AC_CM
 *		flag in cistpl_longlink_ac_t->flags is set.
 *
 *	If HANDTPL_COPY_DONE is set, we just validate the tuple but
 *		do not return any values.
 *	If HANDTPL_PARSE_LTUPLE is set, we validate the tuple and
 *		return the parsed tuple data if the tuple is valid.
 *
 *	If the tuple link field is invalid, the CISTPLF_LINK_INVALID flag
 *		will be set in the tp->flags field and HANDTPL_ERROR
 *		will be returned.
 *
 *	The tuple is considered invalid if it's link field is less than
 *		MIN_LONGLINK_AC_LENGTH.
 */
uint32_t
cistpl_longlink_ac_handler(cistpl_callout_t *co, cistpl_t *tp, uint32_t flags,
								void *arg)
{
	/*
	 * nothing special about our flags, so just call the
	 *	generic handler for this
	 */
	if (flags & HANDTPL_SET_FLAGS)
		return (cis_no_tuple_handler(co, tp, flags, arg));

	/*
	 * Validate the tuple for both the HANDTPL_COPY_DONE case and
	 *	the HANDTPL_PARSE_LTUPLE case. Only return data in
	 *	the HANDTPL_PARSE_LTUPLE case.
	 */
	if (flags & (HANDTPL_COPY_DONE | HANDTPL_PARSE_LTUPLE)) {

		if (tp->len >= (cisdata_t)MIN_LONGLINK_AC_LENGTH) {

			/*
			 * This tuple is valid.
			 */
			if (flags & HANDTPL_COPY_DONE)
				tp->flags |= CISTPLF_VALID;

			if (flags & HANDTPL_PARSE_LTUPLE) {
				cistpl_longlink_ac_t *cs =
						(cistpl_longlink_ac_t *)arg;

				switch (tp->type) {
				    case CISTPL_LONGLINK_A:
					cs->flags = CISTPL_LONGLINK_AC_AM;
					break;

				    case CISTPL_LONGLINK_C:
					cs->flags = CISTPL_LONGLINK_AC_CM;
					break;
				    default:
					break;
				} /* switch */

				RESET_TP(tp);

				cs->tpll_addr = GET_LONG(tp);

			} /* HANDTPL_PARSE_LTUPLE */

		} else {
			tp->flags |= CISTPLF_LINK_INVALID;
			return (HANDTPL_ERROR);
		} /* MIN_LONGLINK_AC_LENGTH */

	} /* (HANDTPL_COPY_DONE | HANDTPL_PARSE_LTUPLE) */

	return (CISTPLF_NOERROR);
}

/*
 * cistpl_longlink_mfc_handler - handler for CISTPL_LONGLINK_MFC tuples
 *
 *	void *arg - points to a cistpl_longlink_mfc_t * where the
 *			information is stuffed into
 *
 *	If HANDTPL_COPY_DONE is set, we just validate the tuple but
 *		do not return any values.
 *	If HANDTPL_PARSE_LTUPLE is set, we validate the tuple and
 *		return the parsed tuple data if the tuple is valid.
 *
 *	If the tuple link field is invalid, the CISTPLF_LINK_INVALID flag
 *		will be set in the tp->flags field and HANDTPL_ERROR
 *		will be returned.
 *
 *	If the number of register sets is invalid, the CISTPLF_PARAMS_INVALID
 *		flag be set in the tp->flags field and HANDTPL_ERROR will be
 *		returned.
 *
 *	The tuple is considered invalid if it's link field is less than
 *		MIN_LONGLINK_MFC_LENGTH or if the number of register sets
 *		is not in the range [MIN_LONGLINK_MFC_NREGS..CIS_MAX_FUNCTIONS]
 */
uint32_t
cistpl_longlink_mfc_handler(cistpl_callout_t *co, cistpl_t *tp,
					uint32_t flags, void *arg)
{
	/*
	 * nothing special about our flags, so just call the
	 *	generic handler for this
	 */
	if (flags & HANDTPL_SET_FLAGS)
		return (cis_no_tuple_handler(co, tp, flags, arg));

	/*
	 * Validate the tuple for both the HANDTPL_COPY_DONE case and
	 *	the HANDTPL_PARSE_LTUPLE case. Only return data in
	 *	the HANDTPL_PARSE_LTUPLE case.
	 */
	if (flags & (HANDTPL_COPY_DONE | HANDTPL_PARSE_LTUPLE)) {

		if (tp->len >= (cisdata_t)MIN_LONGLINK_MFC_LENGTH) {

			/*
			 * This tuple is valid.
			 */
			if (flags & HANDTPL_COPY_DONE)
				tp->flags |= CISTPLF_VALID;

			if (flags & HANDTPL_PARSE_LTUPLE) {
				cistpl_longlink_mfc_t *cs =
						(cistpl_longlink_mfc_t *)arg;
				int fn;

				RESET_TP(tp);

				/*
				 * Get the number of register sets described
				 *	by this tuple. The number of register
				 *	sets must be greter than or equal to
				 *	MIN_LONGLINK_MFC_NREGS and less than
				 *	CIS_MAX_FUNCTIONS.
				 * Note that the number of functions is equal
				 *	to the number of register sets.
				 */
				cs->nregs = GET_BYTE(tp);
				cs->nfuncs = cs->nregs;

				if ((cs->nregs < MIN_LONGLINK_MFC_NREGS) ||
					(cs->nregs > CIS_MAX_FUNCTIONS)) {
				    tp->flags |= CISTPLF_PARAMS_INVALID;
				    return (HANDTPL_ERROR);
				}

				/*
				 * Cycle through each function and setup
				 *	the appropriate parameter values.
				 */
				for (fn = 0; fn < cs->nregs; fn++) {
				    cs->function[fn].tas = GET_BYTE(tp);
				    cs->function[fn].addr = GET_LONG(tp);
				} /* for (fn) */

			} /* HANDTPL_PARSE_LTUPLE */

		} else {
			tp->flags |= CISTPLF_LINK_INVALID;
			return (HANDTPL_ERROR);
		} /* MIN_LONGLINK_MFC_LENGTH */

	} /* (HANDTPL_COPY_DONE | HANDTPL_PARSE_LTUPLE) */

	return (CISTPLF_NOERROR);
}

/*
 * cis_validate_longlink_acm - Validates the secondary tuple chain pointed
 *				to by cisptr and specified by a previous
 *				CISTPL_LONGLINK_A, CISTPL_LONGLINK_C or
 *				CISTPL_LONGLINK_MFC tuple.
 *
 *	cisptr->offset must be the offset to the first byte in the secondary
 *		tuple chain to validate
 *	cisptr->flags must be setup to specify the correct address space
 *
 * The cisptr->offset member is not updated after this function returns.
 *
 *	BAD_CIS_ADDR is returned is the raw CIS data cound not be read.
 *	HANDTPL_ERROR is returned if the secondary tuple chain does not
 *		contain a valid CISTPL_LINKTARGET tuple.
 */
uint32_t
cis_validate_longlink_acm(cisptr_t *cisptr)
{
	uchar_t cb[MIN_LINKTARGET_LENGTH + LINKTARGET_AC_HEADER_LENGTH];
	cisptr_t t_cisptr, *cpt;
	int tl;

	/*
	 * Since the NEXT_CIS_ADDR macro increments the cisptr_t->offset
	 *	member, make a local copy of the cisptr and use the local
	 *	copy to read data from the card.
	 */
	cpt = &t_cisptr;
	bcopy((caddr_t)cisptr, (caddr_t)cpt, sizeof (cisptr_t));

	for (tl = 0; tl < MIN_LINKTARGET_LENGTH +
					LINKTARGET_AC_HEADER_LENGTH; tl++) {

		cb[tl] = GET_CIS_DATA(cpt);
		if (!NEXT_CIS_ADDR(cpt))
			return ((uint32_t)BAD_CIS_ADDR);

	} /* for */

	if ((cb[0] == CISTPL_LINKTARGET) && (cb[1] >= MIN_LINKTARGET_LENGTH)) {
		cisdata_t *ltm = (cisdata_t *)CISTPL_LINKTARGET_MAGIC;

		for (tl = 0; tl < MIN_LINKTARGET_LENGTH; tl++, ltm++) {
			if (cb[tl + LINKTARGET_AC_HEADER_LENGTH] != *ltm)
				return (HANDTPL_ERROR);
		}
		return (CISTPLF_NOERROR);

	} /* if */

	return (HANDTPL_ERROR);
}

/*
 * cis_getstr (tp)
 *	we want the address of the first character returned
 *	but need to skip past the string in the cistpl_t structure
 */
char *
cis_getstr(cistpl_t *tp)
{
	uchar_t *cp, *cpp;
	uchar_t x;

	cp = tp->read.byte;
	cpp = cp;

	while ((x = LOOK_BYTE(tp)) != 0 && x != 0xff) {
		x = GET_BYTE(tp);
	}

	(void) GET_BYTE(tp);	/* get past that last byte */

	while ((*cpp != 0) && (*cpp != 0xff))
	    cpp++;

	*cpp = NULL;

	return ((char *)cp);
}

/*
 * cis_return_name - returns name of tuple
 *
 *    calling:	co - pointer to cistpl_callout_t entry that contains
 *			tuple name to return
 *		gtn - pointer to cistpl_get_tuple_name_t to return
 *			name into
 */
static void
cis_return_name(cistpl_callout_t *co, cistpl_get_tuple_name_t *gtn)
{
	(void) strncpy(gtn->name, co->text, CIS_MAX_TUPLE_NAME_LEN);
	gtn->name[CIS_MAX_TUPLE_NAME_LEN - 1] = NULL;
}

/*
 * cis_malloc/cis_free
 *	wrappers around kmem_alloc()/kmem_free() that
 *	provide malloc/free style usage
 */

caddr_t
cis_malloc(size_t len)
{
	caddr_t addr;

	addr = kmem_zalloc(len + sizeof (size_t), KM_SLEEP);
	*(size_t *)addr = len + sizeof (size_t);
	addr += sizeof (size_t);
	return (addr);
}

void
cis_free(caddr_t addr)
{
	size_t len;
	addr -= sizeof (size_t);
	len = *(size_t *)addr;
	kmem_free(addr, len);
}
