#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>
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 *);
uint16_t
cis_get_short(cistpl_t *tp)
{
uint16_t result;
if (tp->flags & CISTPLF_AM_SPACE) {
result = GET_AM_BYTE(tp);
result |= GET_AM_BYTE(tp) << 8;
} else {
result = GET_CM_BYTE(tp);
result |= GET_CM_BYTE(tp) << 8;
}
return (result);
}
uint16_t
cis_get_be_short(cistpl_t *tp)
{
uint16_t result;
if (tp->flags & CISTPLF_AM_SPACE) {
result = GET_AM_BYTE(tp) << 8;
result |= GET_AM_BYTE(tp);
} else {
result = GET_CM_BYTE(tp) << 8;
result |= GET_CM_BYTE(tp);
}
return (result);
}
uint32_t
cis_get_int24(cistpl_t *tp)
{
uint32_t result = cis_get_short(tp);
result |= GET_BYTE(tp) << 16;
return (result);
}
uint32_t
cis_get_long(cistpl_t *tp)
{
uint32_t result = cis_get_short(tp);
result |= cis_get_short(tp) << 16;
return (result);
}
uint32_t
cis_tuple_handler(cistpl_callout_t *co, cistpl_t *tp, uint32_t flags,
void *arg, cisdata_t subtype)
{
if (CISTPL_IS_VENDOR_SPECIFIC(tp->type))
tp->flags |= CISTPLF_VENDOR_SPECIFIC;
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));
}
}
co++;
}
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);
}
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;
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);
}
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;
return (CISTPLF_NOERROR);
}
if (flags & HANDTPL_COPY_DONE) {
tp->flags |= CISTPLF_VALID;
return (CISTPLF_NOERROR);
}
return (CISTPLF_UNKNOWN);
}
uint32_t
cistpl_vers_1_handler(cistpl_callout_t *co, cistpl_t *tp,
uint32_t flags, void *arg)
{
if (flags & HANDTPL_SET_FLAGS)
return (cis_no_tuple_handler(co, tp, flags, arg));
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 &&
cs->ns < CISTPL_VERS_1_MAX_PROD_STRINGS; ) {
(void) strcpy(cs->pi[cs->ns++], cis_getstr(tp));
}
}
return (CISTPLF_NOERROR);
}
uint32_t config_regs_present_map[] = {
CONFIG_OPTION_REG_PRESENT,
CONFIG_STATUS_REG_PRESENT,
CONFIG_PINREPL_REG_PRESENT,
CONFIG_COPY_REG_PRESENT,
CONFIG_EXSTAT_REG_PRESENT,
CONFIG_IOBASE0_REG_PRESENT,
CONFIG_IOBASE1_REG_PRESENT,
CONFIG_IOBASE2_REG_PRESENT,
CONFIG_IOBASE3_REG_PRESENT,
CONFIG_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;
if (flags & HANDTPL_SET_FLAGS)
return (cis_no_tuple_handler(co, tp, flags, arg));
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);
cr->last = GET_BYTE(tp);
na = (tpcc_sz&3)+1;
nrb = ((tpcc_sz>>2)&0x0f)+1;
cr->base = 0;
n = na;
while (n--)
cr->base |= ((GET_BYTE(tp) & 0x0ff) <<
(8 * (na - (n+1))));
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);
}
hr++;
}
(void) GET_BYTE(tp);
}
}
return (CISTPLF_NOERROR);
}
uint32_t
cistpl_device_handler(cistpl_callout_t *co, cistpl_t *tp,
uint32_t flags, void *arg)
{
cisdata_t dev_id;
if (flags & HANDTPL_SET_FLAGS)
return (cis_no_tuple_handler(co, tp, flags, arg));
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;
dt->num_devices = 1;
cdn = &dt->devnode[0];
cdn->flags = 0;
RESET_TP(tp);
dev_id = GET_BYTE(tp);
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_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;
cdn->type = (dev_id>>4) & 0x0f;
cdn->size = GET_BYTE(tp);
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);
}
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;
if (flags & HANDTPL_SET_FLAGS)
return (cis_no_tuple_handler(co, tp, flags, arg));
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);
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;
}
ce->index = tpce_indx & CISTPL_CFTABLE_TPCE_CFGENTRYM;
if (tpce_indx & CISTPL_CFTABLE_TPCE_DEFAULTM)
ce->flags |= CISTPL_CFTABLE_TPCE_DEFAULT;
tpce_fs = GET_BYTE(tp);
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;
case CISTPL_CFTABLE_TPCE_FS_PWR_VPP1M:
pd->flags |= CISTPL_CFTABLE_TPCE_FS_PWR_VPP1;
case CISTPL_CFTABLE_TPCE_FS_PWR_VCCM:
pd->flags |= CISTPL_CFTABLE_TPCE_FS_PWR_VCC;
}
}
if (tpce_fs & CISTPL_CFTABLE_TPCE_FS_MEMM)
ce->flags |= CISTPL_CFTABLE_TPCE_FS_MEM;
if (ce->flags & CISTPL_CFTABLE_TPCE_FS_PWR) {
cistpl_cftable_entry_pd_t *pd = &ce->pd;
cistpl_cftable_entry_pwr_t *pwr;
if (pd->flags & CISTPL_CFTABLE_TPCE_FS_PWR_VCC) {
pwr = &pd->pd_vcc;
cistpl_pd_parse(tp, pwr);
}
if (pd->flags & CISTPL_CFTABLE_TPCE_FS_PWR_VPP1) {
pwr = &pd->pd_vpp1;
cistpl_pd_parse(tp, pwr);
}
if (pd->flags & CISTPL_CFTABLE_TPCE_FS_PWR_VPP2) {
pwr = &pd->pd_vpp2;
cistpl_pd_parse(tp, pwr);
}
}
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);
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 (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);
io->flags = tpce_io;
io->addr_lines = tpce_io &
CISTPL_CFTABLE_TPCE_FS_IO_ALM;
if (tpce_io & CISTPL_CFTABLE_TPCE_FS_IO_RANGEM) {
cistpl_cftable_entry_io_range_t *ior;
ior_desc = GET_BYTE(tp);
nr = (ior_desc & 0x0f) + 1;
io->ranges = nr;
for (i = 0; i < (int)nr; i++) {
ior = &io->range[i];
ior->addr = 0;
ior->length = 0;
for (j = 0; j <
cistpl_cftable_io_size_table[
(ior_desc>>4)&3];
j++)
ior->addr |= (GET_BYTE(tp) <<
cistpl_cftable_shift_table[j]);
for (j = 0; j <
cistpl_cftable_io_size_table[
(ior_desc>>6)&3];
j++)
ior->length |= (GET_BYTE(tp) <<
cistpl_cftable_shift_table[j]);
}
}
}
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);
irq->flags = tpce_ir;
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 (ce->flags & CISTPL_CFTABLE_TPCE_FS_MEM) {
cistpl_cftable_entry_mem_t *mem = &ce->mem;
cistpl_cftable_entry_mem_window_t *win;
switch (tpce_fs & CISTPL_CFTABLE_TPCE_FS_MEMM) {
case CISTPL_CFTABLE_TPCE_FS_MEM3M:
mem->flags |= CISTPL_CFTABLE_TPCE_FS_MEM3;
tpce_msd = GET_BYTE(tp);
mem->windows = ((tpce_msd &
(CISTPL_CFTABLE_ENTRY_MAX_MEM_WINDOWS -
1)) + 1);
if (tpce_msd & CISTPL_CFTABLE_TPCE_FS_MEM_HOSTM)
mem->flags |=
CISTPL_CFTABLE_TPCE_FS_MEM_HOST;
for (i = 0; i < mem->windows; i++) {
win = &mem->window[i];
win->length = 0;
win->card_addr = 0;
win->host_addr = 0;
for (j = 0; j <
(int)((tpce_msd>>3)&3); j++)
win->length |= (GET_BYTE(tp) <<
cistpl_cftable_shift_table[j]);
for (j = 0; j <
(int)((tpce_msd>>5)&3); j++)
win->card_addr |=
(GET_BYTE(tp) <<
cistpl_cftable_shift_table[j]);
if (mem->flags &
CISTPL_CFTABLE_TPCE_FS_MEM_HOST) {
for (j = 0; j <
(int)((tpce_msd>>5)&3);
j++)
win->host_addr |=
(GET_BYTE(tp) <<
cistpl_cftable_shift_table[j]);
} else {
win->host_addr = win->card_addr;
}
}
break;
case CISTPL_CFTABLE_TPCE_FS_MEM2M:
mem->flags |= CISTPL_CFTABLE_TPCE_FS_MEM2;
win = &mem->window[0];
mem->windows = 1;
win->length = GET_BYTE(tp);
win->length |= (GET_BYTE(tp)<<8);
win->length *=
CISTPL_CFTABLE_TPCE_FS_MEM_PGSIZE;
win->card_addr = GET_BYTE(tp);
win->card_addr |= (GET_BYTE(tp)<<8);
win->card_addr *=
CISTPL_CFTABLE_TPCE_FS_MEM_PGSIZE;
win->host_addr = win->card_addr;
break;
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;
win->length = GET_BYTE(tp);
win->length |= (GET_BYTE(tp)<<8);
win->length *=
CISTPL_CFTABLE_TPCE_FS_MEM_PGSIZE;
break;
}
}
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)));
if (tp->flags & CISTPLF_MEM_ERR)
return (HANDTPL_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
}
return (CISTPLF_NOERROR);
}
static void
cistpl_pd_parse(cistpl_t *tp, cistpl_cftable_entry_pwr_t *pd)
{
cisdata_t pdesc;
pdesc = GET_BYTE(tp);
if (pdesc & CISTPL_CFTABLE_PD_NOMV) {
pd->nomV = cistpl_expd_parse(tp, &pd->nomV_flags) / 100;
pd->nomV_flags |= (pdesc | CISTPL_CFTABLE_PD_EXISTS);
}
if (pdesc & CISTPL_CFTABLE_PD_MINV) {
pd->minV = cistpl_expd_parse(tp, &pd->minV_flags) / 100;
pd->minV_flags |= (pdesc | CISTPL_CFTABLE_PD_EXISTS);
}
if (pdesc & CISTPL_CFTABLE_PD_MAXV) {
pd->maxV = cistpl_expd_parse(tp, &pd->maxV_flags) / 100;
pd->maxV_flags |= (pdesc | CISTPL_CFTABLE_PD_EXISTS);
}
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);
}
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);
}
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);
}
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);
}
}
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;
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 (! (*flags & CISTPL_CFTABLE_PD_MUL10))
val = val/10;
if (exponent)
val = val + (digits * CISTPL_PD_EXP(exponent-1));
val /= 1000;
return (val);
}
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) {
first = 0;
exponent = (exspeed & 0x07);
mantisa = (exspeed >> 3) & 0x0f;
spindex &= 0x0f;
while (spindex--)
scale *= 10;
}
} while (exspeed & CISTPL_EXT_BIT);
speed = scale * CISTPL_DEVSPEED_MAN(mantisa) *
CISTPL_DEVSPEED_EXP(exponent);
speed = speed/10;
break;
default:
break;
}
return (speed);
}
uint32_t
cistpl_vers_2_handler(cistpl_callout_t *co, cistpl_t *tp,
uint32_t flags, void *arg)
{
if (flags & HANDTPL_SET_FLAGS)
return (cis_no_tuple_handler(co, tp, flags, arg));
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);
}
uint32_t
cistpl_jedec_handler(cistpl_callout_t *co, cistpl_t *tp,
uint32_t flags, void *arg)
{
if (flags & HANDTPL_SET_FLAGS)
return (cis_no_tuple_handler(co, tp, flags, arg));
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);
}
uint32_t
cistpl_format_handler(cistpl_callout_t *co, cistpl_t *tp,
uint32_t flags, void *arg)
{
if (flags & HANDTPL_SET_FLAGS)
return (cis_no_tuple_handler(co, tp, flags, arg));
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:
break;
}
}
return (CISTPLF_NOERROR);
}
uint32_t
cistpl_geometry_handler(cistpl_callout_t *co, cistpl_t *tp, uint32_t flags,
void *arg)
{
if (flags & HANDTPL_SET_FLAGS)
return (cis_no_tuple_handler(co, tp, flags, arg));
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);
}
uint32_t
cistpl_byteorder_handler(cistpl_callout_t *co, cistpl_t *tp, uint32_t flags,
void *arg)
{
if (flags & HANDTPL_SET_FLAGS)
return (cis_no_tuple_handler(co, tp, flags, arg));
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);
}
uint32_t
cistpl_date_handler(cistpl_callout_t *co, cistpl_t *tp,
uint32_t flags, void *arg)
{
if (flags & HANDTPL_SET_FLAGS)
return (cis_no_tuple_handler(co, tp, flags, arg));
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);
}
uint32_t
cistpl_battery_handler(cistpl_callout_t *co, cistpl_t *tp,
uint32_t flags, void *arg)
{
if (flags & HANDTPL_SET_FLAGS)
return (cis_no_tuple_handler(co, tp, flags, arg));
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);
}
uint32_t
cistpl_org_handler(cistpl_callout_t *co, cistpl_t *tp,
uint32_t flags, void *arg)
{
if (flags & HANDTPL_SET_FLAGS)
return (cis_no_tuple_handler(co, tp, flags, arg));
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);
}
uint32_t
cistpl_manfid_handler(cistpl_callout_t *co, cistpl_t *tp,
uint32_t flags, void *arg)
{
if (flags & HANDTPL_SET_FLAGS)
return (cis_no_tuple_handler(co, tp, flags, arg));
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);
}
uint32_t
cistpl_funcid_handler(cistpl_callout_t *co, cistpl_t *tp,
uint32_t flags, void *arg)
{
if (flags & HANDTPL_SET_FLAGS)
return (cis_no_tuple_handler(co, tp, flags, arg));
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);
}
uint32_t
cistpl_funce_serial_handler(cistpl_callout_t *co, cistpl_t *tp,
uint32_t flags, void *arg)
{
int subfunction;
if (flags & HANDTPL_SET_FLAGS)
return (cis_no_tuple_handler(co, tp, flags, arg));
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);
}
uint32_t
cistpl_funce_lan_handler(cistpl_callout_t *co, cistpl_t *tp, uint32_t flags,
void *arg)
{
int subfunction;
if (flags & HANDTPL_SET_FLAGS)
return (cis_no_tuple_handler(co, tp, flags, arg));
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 {
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:
return (CISTPLF_UNKNOWN);
}
}
return (CISTPLF_NOERROR);
}
uint32_t
cistpl_linktarget_handler(cistpl_callout_t *co, cistpl_t *tp, uint32_t flags,
void *arg)
{
if (flags & HANDTPL_SET_FLAGS)
return (cis_no_tuple_handler(co, tp, flags, arg));
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);
cp = GET_BYTE_ADDR(tp);
for (i = 0; i < MIN_LINKTARGET_LENGTH; i++) {
if (GET_BYTE(tp) != *ltm++) {
tp->flags |= CISTPLF_PARAMS_INVALID;
return (HANDTPL_ERROR);
}
}
if (flags & HANDTPL_COPY_DONE)
tp->flags |= CISTPLF_VALID;
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] = '\0';
}
} else {
tp->flags |= CISTPLF_LINK_INVALID;
return (HANDTPL_ERROR);
}
}
return (CISTPLF_NOERROR);
}
uint32_t
cistpl_longlink_ac_handler(cistpl_callout_t *co, cistpl_t *tp, uint32_t flags,
void *arg)
{
if (flags & HANDTPL_SET_FLAGS)
return (cis_no_tuple_handler(co, tp, flags, arg));
if (flags & (HANDTPL_COPY_DONE | HANDTPL_PARSE_LTUPLE)) {
if (tp->len >= (cisdata_t)MIN_LONGLINK_AC_LENGTH) {
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;
}
RESET_TP(tp);
cs->tpll_addr = GET_LONG(tp);
}
} else {
tp->flags |= CISTPLF_LINK_INVALID;
return (HANDTPL_ERROR);
}
}
return (CISTPLF_NOERROR);
}
uint32_t
cistpl_longlink_mfc_handler(cistpl_callout_t *co, cistpl_t *tp,
uint32_t flags, void *arg)
{
if (flags & HANDTPL_SET_FLAGS)
return (cis_no_tuple_handler(co, tp, flags, arg));
if (flags & (HANDTPL_COPY_DONE | HANDTPL_PARSE_LTUPLE)) {
if (tp->len >= (cisdata_t)MIN_LONGLINK_MFC_LENGTH) {
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);
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);
}
for (fn = 0; fn < cs->nregs; fn++) {
cs->function[fn].tas = GET_BYTE(tp);
cs->function[fn].addr = GET_LONG(tp);
}
}
} else {
tp->flags |= CISTPLF_LINK_INVALID;
return (HANDTPL_ERROR);
}
}
return (CISTPLF_NOERROR);
}
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;
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);
}
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);
}
return (HANDTPL_ERROR);
}
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);
while ((*cpp != 0) && (*cpp != 0xff))
cpp++;
*cpp = '\0';
return ((char *)cp);
}
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] = '\0';
}
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);
}