cis_handlers.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% 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)(uintptr_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);
}