text: #include "u.h"
  #include "../port/lib.h"
  #include "mem.h"
  #include "dat.h"
  #include "fns.h"
  #include "../port/error.h"
  #include "io.h"
  
  /*
   *  read and crack the card information structure enough to set
   *  important parameters like power
   */
  /* cis memory walking */
  typedef struct Cisdat {
  	uchar	*cisbase;
  	int	cispos;
  	int	cisskip;
  	int	cislen;
  } Cisdat;
  
  static void	tcfig(PCMslot*, Cisdat*, int);
  static void	tentry(PCMslot*, Cisdat*, int);
  static void	tvers1(PCMslot*, Cisdat*, int);
  
  static int
  readc(Cisdat *cis, uchar *x)
  {
  	if(cis->cispos >= cis->cislen)
  		return 0;
  	*x = cis->cisbase[cis->cisskip*cis->cispos];
  	cis->cispos++;
  	return 1;
  }
  
  static int
  xcistuple(int slotno, int tuple, int subtuple, void *v, int nv, int attr)
  {
  	PCMmap *m;
  	Cisdat cis;
  	int i, l;
  	uchar *p;
  	uchar type, link, n, c;
  	int this, subtype;
  
  	m = pcmmap(slotno, 0, 0, attr);
  	if(m == 0)
  		return -1;
  
  	cis.cisbase = KADDR(m->isa);
  	cis.cispos = 0;
  	cis.cisskip = attr ? 2 : 1;
  	cis.cislen = m->len;
  
  	/* loop through all the tuples */
  	for(i = 0; i < 1000; i++){
  		this = cis.cispos;
  		if(readc(&cis, &type) != 1)
  			break;
  		if(type == 0xFF)
  			break;
  		if(readc(&cis, &link) != 1)
  			break;
  		if(link == 0xFF)
  			break;
  
  		n = link;
  		if(link > 1 && subtuple != -1){
  			if(readc(&cis, &c) != 1)
  				break;
  			subtype = c;
  			n--;
  		}else
  			subtype = -1;
  
  		if(type == tuple && subtype == subtuple){
  			p = v;
  			for(l=0; l<nv && l<n; l++)
  				if(readc(&cis, p++) != 1)
  					break;
  			pcmunmap(slotno, m);
  			return nv;
  		}
  		cis.cispos = this + (2+link);
  	}
  	pcmunmap(slotno, m);
  	return -1;
  }
  
  int
  pcmcistuple(int slotno, int tuple, int subtuple, void *v, int nv)
  {
  	int n;
  
  	/* try attribute space, then memory */
  	if((n = xcistuple(slotno, tuple, subtuple, v, nv, 1)) >= 0)
  		return n;
  	return xcistuple(slotno, tuple, subtuple, v, nv, 0);
  }
  
  void
  pcmcisread(PCMslot *pp)
  {
  	int this;
  	Cisdat cis;
  	PCMmap *m;
  	uchar type, link;
  
  	memset(pp->ctab, 0, sizeof(pp->ctab));
  	pp->caddr = 0;
  	pp->cpresent = 0;
  	pp->configed = 0;
  	pp->nctab = 0;
  	pp->verstr[0] = 0;
  
  	/*
  	 * Read all tuples in attribute space.
  	 */
  	m = pcmmap(pp->slotno, 0, 0, 1);
  	if(m == 0)
  		return;
  
  	cis.cisbase = KADDR(m->isa);
  	cis.cispos = 0;
  	cis.cisskip = 2;
  	cis.cislen = m->len;
  
  	/* loop through all the tuples */
  	for(;;){
  		this = cis.cispos;
  		if(readc(&cis, &type) != 1)
  			break;
  		if(type == 0xFF)
  			break;
  		if(readc(&cis, &link) != 1)
  			break;
  
  		switch(type){
  		default:
  			break;
  		case 0x15:
  			tvers1(pp, &cis, type);
  			break;
  		case 0x1A:
  			tcfig(pp, &cis, type);
  			break;
  		case 0x1B:
  			tentry(pp, &cis, type);
  			break;
  		}
  
  		if(link == 0xFF)
  			break;
  		cis.cispos = this + (2+link);
  	}
  	pcmunmap(pp->slotno, m);
  }
  
  static ulong
  getlong(Cisdat *cis, int size)
  {
  	uchar c;
  	int i;
  	ulong x;
  
  	x = 0;
  	for(i = 0; i < size; i++){
  		if(readc(cis, &c) != 1)
  			break;
  		x |= c<<(i*8);
  	}
  	return x;
  }
  
  static void
  tcfig(PCMslot *pp, Cisdat *cis, int )
  {
  	uchar size, rasize, rmsize;
  	uchar last;
  
  	if(readc(cis, &size) != 1)
  		return;
  	rasize = (size&0x3) + 1;
  	rmsize = ((size>>2)&0xf) + 1;
  	if(readc(cis, &last) != 1)
  		return;
  	pp->caddr = getlong(cis, rasize);
  	pp->cpresent = getlong(cis, rmsize);
  }
  
  static ulong vexp[8] =
  {
  	1, 10, 100, 1000, 10000, 100000, 1000000, 10000000
  };
  static ulong vmant[16] =
  {
  	10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80, 90,
  };
  
  static ulong
  microvolt(Cisdat *cis)
  {
  	uchar c;
  	ulong microvolts;
  	ulong exp;
  
  	if(readc(cis, &c) != 1)
  		return 0;
  	exp = vexp[c&0x7];
  	microvolts = vmant[(c>>3)&0xf]*exp;
  	while(c & 0x80){
  		if(readc(cis, &c) != 1)
  			return 0;
  		switch(c){
  		case 0x7d:
  			break;		/* high impedence when sleeping */
  		case 0x7e:
  		case 0x7f:
  			microvolts = 0;	/* no connection */
  			break;
  		default:
  			exp /= 10;
  			microvolts += exp*(c&0x7f);
  		}
  	}
  	return microvolts;
  }
  
  static ulong
  nanoamps(Cisdat *cis)
  {
  	uchar c;
  	ulong nanoamps;
  
  	if(readc(cis, &c) != 1)
  		return 0;
  	nanoamps = vexp[c&0x7]*vmant[(c>>3)&0xf];
  	while(c & 0x80){
  		if(readc(cis, &c) != 1)
  			return 0;
  		if(c == 0x7d || c == 0x7e || c == 0x7f)
  			nanoamps = 0;
  	}
  	return nanoamps;
  }
  
  /*
   * only nominal voltage (feature 1) is important for config,
   * other features must read card to stay in sync.
   */
  static ulong
  power(Cisdat *cis)
  {
  	uchar feature;
  	ulong mv;
  
  	mv = 0;
  	if(readc(cis, &feature) != 1)
  		return 0;
  	if(feature & 1)
  		mv = microvolt(cis);
  	if(feature & 2)
  		microvolt(cis);
  	if(feature & 4)
  		microvolt(cis);
  	if(feature & 8)
  		nanoamps(cis);
  	if(feature & 0x10)
  		nanoamps(cis);
  	if(feature & 0x20)
  		nanoamps(cis);
  	if(feature & 0x40)
  		nanoamps(cis);
  	return mv/1000000;
  }
  
  static ulong mantissa[16] =
  { 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80, };
  
  static ulong exponent[8] =
  { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, };
  
  static ulong
  ttiming(Cisdat *cis, int scale)
  {
  	uchar unscaled;
  	ulong nanosecs;
  
  	if(readc(cis, &unscaled) != 1)
  		return 0;
  	nanosecs = (mantissa[(unscaled>>3)&0xf]*exponent[unscaled&7])/10;
  	nanosecs = nanosecs * vexp[scale];
  	return nanosecs;
  }
  
  static void
  timing(Cisdat *cis, PCMconftab *ct)
  {
  	uchar c, i;
  
  	if(readc(cis, &c) != 1)
  		return;
  	i = c&0x3;
  	if(i != 3)
  		ct->maxwait = ttiming(cis, i);		/* max wait */
  	i = (c>>2)&0x7;
  	if(i != 7)
  		ct->readywait = ttiming(cis, i);		/* max ready/busy wait */
  	i = (c>>5)&0x7;
  	if(i != 7)
  		ct->otherwait = ttiming(cis, i);		/* reserved wait */
  }
  
  static void
  iospaces(Cisdat *cis, PCMconftab *ct)
  {
  	uchar c;
  	int i, nio;
  
  	ct->nio = 0;
  	if(readc(cis, &c) != 1)
  		return;
  
  	ct->bit16 = ((c>>5)&3) >= 2;
  	if(!(c & 0x80)){
  		ct->io[0].start = 0;
  		ct->io[0].len = 1<<(c&0x1f);
  		ct->nio = 1;
  		return;
  	}
  
  	if(readc(cis, &c) != 1)
  		return;
  
  	/*
  	 * For each of the range descriptions read the
  	 * start address and the length (value is length-1).
  	 */
  	nio = (c&0xf)+1;
  	for(i = 0; i < nio; i++){
  		ct->io[i].start = getlong(cis, (c>>4)&0x3);
  		ct->io[i].len = getlong(cis, (c>>6)&0x3)+1;
  	}
  	ct->nio = nio;
  }
  
  static void
  irq(Cisdat *cis, PCMconftab *ct)
  {
  	uchar c;
  
  	if(readc(cis, &c) != 1)
  		return;
  	ct->irqtype = c & 0xe0;
  	if(c & 0x10)
  		ct->irqs = getlong(cis, 2);
  	else
  		ct->irqs = 1<<(c&0xf);
  	ct->irqs &= 0xDEB8;		/* levels available to card */
  }
  
  static void
  memspace(Cisdat *cis, int asize, int lsize, int host)
  {
  	ulong haddress, address, len;
  
  	len = getlong(cis, lsize)*256;
  	address = getlong(cis, asize)*256;
  	USED(len, address);
  	if(host){
  		haddress = getlong(cis, asize)*256;
  		USED(haddress);
  	}
  }
  
  static void
  tentry(PCMslot *pp, Cisdat *cis, int )
  {
  	uchar c, i, feature;
  	PCMconftab *ct;
  
  	if(pp->nctab >= nelem(pp->ctab))
  		return;
  	if(readc(cis, &c) != 1)
  		return;
  	ct = &pp->ctab[pp->nctab++];
  
  	/* copy from last default config */
  	if(pp->def)
  		*ct = *pp->def;
  
  	ct->index = c & 0x3f;
  
  	/* is this the new default? */
  	if(c & 0x40)
  		pp->def = ct;
  
  	/* memory wait specified? */
  	if(c & 0x80){
  		if(readc(cis, &i) != 1)
  			return;
  		if(i&0x80)
  			ct->memwait = 1;
  	}
  
  	if(readc(cis, &feature) != 1)
  		return;
  	switch(feature&0x3){
  	case 1:
  		ct->vpp1 = ct->vpp2 = power(cis);
  		break;
  	case 2:
  		power(cis);
  		ct->vpp1 = ct->vpp2 = power(cis);
  		break;
  	case 3:
  		power(cis);
  		ct->vpp1 = power(cis);
  		ct->vpp2 = power(cis);
  		break;
  	default:
  		break;
  	}
  	if(feature&0x4)
  		timing(cis, ct);
  	if(feature&0x8)
  		iospaces(cis, ct);
  	if(feature&0x10)
  		irq(cis, ct);
  	switch((feature>>5)&0x3){
  	case 1:
  		memspace(cis, 0, 2, 0);
  		break;
  	case 2:
  		memspace(cis, 2, 2, 0);
  		break;
  	case 3:
  		if(readc(cis, &c) != 1)
  			return;
  		for(i = 0; i <= (c&0x7); i++)
  			memspace(cis, (c>>5)&0x3, (c>>3)&0x3, c&0x80);
  		break;
  	}
  	pp->configed++;
  }
  
  static void
  tvers1(PCMslot *pp, Cisdat *cis, int )
  {
  	uchar c, major, minor, last;
  	int  i;
  
  	if(readc(cis, &major) != 1)
  		return;
  	if(readc(cis, &minor) != 1)
  		return;
  	last = 0;
  	for(i = 0; i < sizeof(pp->verstr)-1; i++){
  		if(readc(cis, &c) != 1)
  			return;
  		if(c == 0)
  			c = ';';
  		if(c == '\n')
  			c = ';';
  		if(c == 0xff)
  			break;
  		if(c == ';' && last == ';')
  			continue;
  		pp->verstr[i] = c;
  		last = c;
  	}
  	pp->verstr[i] = 0;
  }
  