#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
#include "io.h"
static PCMslot slot[2];
int nslot = 2;
struct {
Ref;
} pcmcia;
enum
{
Qdir,
Qmem,
Qattr,
Qctl,
Nents = 3,
};
#define SLOTNO(c) ((c->qid.path>>8)&0xff)
#define TYPE(c) (c->qid.path&0xff)
#define QID(s,t) (((s)<<8)|(t))
static int
pcmgen(Chan *c, Dirtab *, int , int i, Dir *dp)
{
int slotno;
Qid qid;
long len;
PCMslot *pp;
char name[NAMELEN];
if(i == DEVDOTDOT){
devdir(c, (Qid){CHDIR, 0}, "#y", 0, eve, 0555, dp);
return 1;
}
if(i >= Nents*nslot)
return -1;
slotno = i/Nents;
pp = slot + slotno;
len = 0;
switch(i%Nents){
case 0:
qid.path = QID(slotno, Qmem);
sprint(name, "pcm%dmem", slotno);
len = pp->memlen;
break;
case 1:
qid.path = QID(slotno, Qattr);
sprint(name, "pcm%dattr", slotno);
len = pp->memlen;
break;
case 2:
qid.path = QID(slotno, Qctl);
sprint(name, "pcm%dctl", slotno);
break;
}
qid.vers = 0;
devdir(c, qid, name, len, eve, 0660, dp);
return 1;
}
static void
slotinfo(void)
{
ulong x = gpioregs->level;
if(x & GPIO_OPT_IND_i){
/* no expansion pack */
slot[0].occupied = 0;
slot[1].occupied = 0;
} else {
slot[0].occupied = (x & GPIO_CARD_IND0_i) == 0;
slot[1].occupied = (x & GPIO_CARD_IND1_i) == 0;
}
}
static void
increfp(PCMslot *pp)
{
if(incref(&pcmcia) == 1)
exppackpower(1);
lock(pp);
if(pp->ref++ == 0)
;
unlock(pp);
slotinfo();
}
static void
decrefp(PCMslot *pp)
{
slotinfo();
lock(pp);
if(pp->ref-- == 1)
;
unlock(pp);
if(decref(&pcmcia) == 0)
exppackpower(0);
}
/*
* get a map for pc card region, return corrected len
*/
PCMmap*
pcmmap(int slotno, ulong, int, int attr)
{
if(slotno > nslot)
panic("pcmmap");
if(attr)
return &slot[slotno].attrmap;
else
return &slot[slotno].memmap;
}
void
pcmunmap(int, PCMmap*)
{
}
/*
* map in the space for the cards
*/
static void
pcmciareset(void)
{
int j;
PCMslot *pp;
for(j = 0; j < nslot; j++){
pp = &slot[j];
pp->slotno = j;
pp->memlen = 64*OneMeg;
pp->msec = ~0;
pp->verstr[0] = 0;
pp->mem = mapmem(j == 0 ? PYHSPCM0MEM : PYHSPCM1MEM, 64*OneMeg);
pp->memmap.ca = 0;
pp->memmap.cea = 64*MB;
pp->memmap.isa = (ulong)pp->mem;
pp->memmap.len = 64*OneMeg;
pp->memmap.attr = 0;
pp->attr = mapmem(j == 0 ? PYHSPCM0ATTR : PYHSPCM1ATTR, OneMeg);
pp->attrmap.ca = 0;
pp->attrmap.cea = 64*MB;
pp->attrmap.isa = (ulong)pp->attr;
pp->attrmap.len = OneMeg;
pp->attrmap.attr = 1;
pp->regs = mapspecial(j == 0 ? PHYSPCM0REGS : PHYSPCM1REGS, 32*1024);
}
}
static Chan*
pcmciaattach(char *spec)
{
return devattach('y', spec);
}
static int
pcmciawalk(Chan *c, char *name)
{
return devwalk(c, name, 0, 0, pcmgen);
}
static void
pcmciastat(Chan *c, char *db)
{
devstat(c, db, 0, 0, pcmgen);
}
static Chan*
pcmciaopen(Chan *c, int omode)
{
if(c->qid.path == CHDIR){
if(omode != OREAD)
error(Eperm);
} else
increfp(slot + SLOTNO(c));
c->mode = openmode(omode);
c->flag |= COPEN;
c->offset = 0;
return c;
}
static void
pcmciaclose(Chan *c)
{
if(c->flag & COPEN)
if(c->qid.path != CHDIR)
decrefp(slot+SLOTNO(c));
}
/* a memmove using only bytes */
static void
memmoveb(uchar *to, uchar *from, int n)
{
while(n-- > 0)
*to++ = *from++;
}
/* a memmove using only shorts & bytes */
static void
memmoves(uchar *to, uchar *from, int n)
{
ushort *t, *f;
if((((ulong)to) & 1) || (((ulong)from) & 1) || (n & 1)){
while(n-- > 0)
*to++ = *from++;
} else {
n = n/2;
t = (ushort*)to;
f = (ushort*)from;
while(n-- > 0)
*t++ = *f++;
}
}
static long
pcmread(void *a, long n, ulong off, uchar *start, ulong len)
{
if(off > len)
return 0;
if(off + n > len)
n = len - off;
memmoves(a, start+off, n);
return n;
}
static long
pcmctlread(void *a, long n, ulong off, PCMslot *pp)
{
char *p, *buf, *e;
buf = p = malloc(READSTR);
if(waserror()){
free(buf);
nexterror();
}
e = p + READSTR;
buf[0] = 0;
if(pp->occupied){
p = seprint(p, e, "occupied\n");
if(pp->verstr[0])
p = seprint(p, e, "version %s\n", pp->verstr);
}
USED(p);
n = readstr(off, a, n, buf);
free(buf);
poperror();
return n;
}
static long
pcmciaread(Chan *c, void *a, long n, vlong off)
{
PCMslot *pp;
ulong offset = off;
pp = slot + SLOTNO(c);
switch(TYPE(c)){
case Qdir:
return devdirread(c, a, n, 0, 0, pcmgen);
case Qmem:
if(!pp->occupied)
error(Eio);
return pcmread(a, n, offset, pp->mem, 64*OneMeg);
case Qattr:
if(!pp->occupied)
error(Eio);
return pcmread(a, n, offset, pp->attr, OneMeg);
case Qctl:
return pcmctlread(a, n, offset, pp);
}
error(Ebadarg);
return -1; /* not reached */
}
static long
pcmwrite(void *a, long n, ulong off, uchar *start, ulong len)
{
if(off > len)
error(Eio);
if(off + n > len)
error(Eio);
memmoveb(start+off, a, n);
return n;
}
static long
pcmctlwrite(char *p, long n, ulong off, PCMslot *pp)
{
/* nothing yet */
USED(p, n, off, pp);
error(Ebadarg);
return 0;
}
static long
pcmciawrite(Chan *c, void *a, long n, vlong off)
{
PCMslot *pp;
ulong offset = off;
pp = slot + SLOTNO(c);
switch(TYPE(c)){
case Qmem:
if(!pp->occupied)
error(Eio);
return pcmwrite(a, n, offset, pp->mem, 64*OneMeg);
case Qattr:
if(!pp->occupied)
error(Eio);
return pcmwrite(a, n, offset, pp->attr, OneMeg);
case Qctl:
if(!pp->occupied)
error(Eio);
return pcmctlwrite(a, n, offset, pp);
}
error(Ebadarg);
return -1; /* not reached */
}
Dev pcmciadevtab = {
'y',
"pcmcia",
pcmciareset,
devinit,
pcmciaattach,
devclone,
pcmciawalk,
pcmciastat,
pcmciaopen,
devcreate,
pcmciaclose,
pcmciaread,
devbread,
pcmciawrite,
devbwrite,
devremove,
devwstat,
};