#include "u.h"#include "lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "error.h" #include "netif.h" typedef struct Ram Ram;struct Ram{ QLock lk; Ram *next; int ref; /* simple for now */ uchar **pages; int pagecount; int size; int qref[2]; ulong path;}; struct{ Lock lk; ulong path;} ramalloc; enum{ Qdir, Qdata0, Qctl,}; Dirtab ramdir[] ={ ".", {Qdir,0,QTDIR}, 0, DMDIR|0500, "data", {Qdata0}, 0, 0600, "ctl", {Qctl}, 0, 0600,};#define NPIPEDIR 3 static voidraminit(void){} /* * create a ram, no streams are created until an open */static Chan*ramattach(char *spec){ Ram *p; Chan *c; c = devattach('R', spec); p = malloc(sizeof(Ram)); if(p == 0) exhausted("memory"); p->ref = 1; p->size = 0; p->pagecount = 1; p->pages = mallocz(sizeof(char *), 1); p->pages[0] = mallocz(BY2PG, 1); lock(&ramalloc.lk); p->path = ++ramalloc.path; unlock(&ramalloc.lk); mkqid(&c->qid, NETQID(2*p->path, Qdir), 0, QTDIR); c->aux = p; c->dev = 0; return c;} static intramgen(Chan *c, char *name, Dirtab *tab, int ntab, int i, Dir *dp){ Qid q; int len; Ram *p; if(i == DEVDOTDOT){ devdir(c, c->qid, "#R", 0, eve, DMDIR|0555, dp); return 1; } i++; /* skip . */ if(tab==0 || i>=ntab) return -1; tab += i; p = c->aux; switch((ulong)tab->qid.path){ case Qdata0: len = p->size; break; case Qctl: len = 0; break; default: len = tab->length; break; } mkqid(&q, NETQID(NETID(c->qid.path), tab->qid.path), 0, QTFILE); devdir(c, q, tab->name, len, eve, tab->perm, dp); return 1;} static Walkqid*ramwalk(Chan *c, Chan *nc, char **name, int nname){ Walkqid *wq; Ram *p; wq = devwalk(c, nc, name, nname, ramdir, NPIPEDIR, ramgen); if(wq != nil && wq->clone != nil && wq->clone != c){ p = c->aux; qlock(&p->lk); p->ref++; if(c->flag & COPEN){ print("channel open in ramwalk\n"); switch(NETTYPE(c->qid.path)){ case Qdata0: p->qref[0]++; break; case Qctl: p->qref[1]++; break; } } qunlock(&p->lk); } return wq;} static intramstat(Chan *c, uchar *db, int n){ Ram *p; Dir dir; p = c->aux; switch(NETTYPE(c->qid.path)){ case Qdir: devdir(c, c->qid, ".", 0, eve, DMDIR|0555, &dir); break; case Qdata0: devdir(c, c->qid, "data", p->size, eve, 0600, &dir); break; case Qctl: devdir(c, c->qid, "ctl", 0, eve, 0600, &dir); break; default: panic("ramstat"); } n = convD2M(&dir, db, n); if(n < BIT16SZ) error(Eshortstat); return n;} /* * if the stream doesn't exist, create it */static Chan*ramopen(Chan *c, int omode){ Ram *p; if(c->qid.type & QTDIR){ if(omode != OREAD) error(Ebadarg); c->mode = omode; c->flag |= COPEN; c->offset = 0; return c; } p = c->aux; qlock(&p->lk); switch(NETTYPE(c->qid.path)){ case Qdata0: p->qref[0]++; break; case Qctl: p->qref[1]++; break; } qunlock(&p->lk); c->mode = openmode(omode); c->flag |= COPEN; c->offset = 0; c->iounit = qiomaxatomic; return c;} static voidramclose(Chan *c){ Ram *p; p = c->aux; qlock(&p->lk); if(c->flag & COPEN){ switch(NETTYPE(c->qid.path)){ case Qdata0: p->qref[0]--; break; case Qctl: p->qref[1]--; break; } } /* * free the structure on last close */ p->ref--; if(p->ref == 0){ int i; qunlock(&p->lk); for(i = 0; i < p->pagecount; i++) free(p->pages[i]); free(p->pages); free(p); } else qunlock(&p->lk);} static long rampageread(Ram *p, void *va, long n, vlong offset){ int i; long total, offinpage, leninpage; total = n; /* figure out what range we can actually read */ if(offset > p->size) return 0; if(offset + n > p->size) n = p->size - offset; /* granular copy */ for(i = offset / BY2PG; n > 0; i++) { /* i is the page */ offinpage = offset & (BY2PG - 1); leninpage = BY2PG - offinpage; /* unless there is too little left ... */ if(leninpage > n) leninpage = n; memcpy(va, p->pages[i] + offinpage, leninpage); offset += offinpage; n -= leninpage; va += leninpage; } return total;} static longramread(Chan *c, void *va, long n, vlong offset){ Ram *p; char *buf, *s, *e; p = c->aux; switch(NETTYPE(c->qid.path)){ case Qdir: return devdirread(c, va, n, ramdir, NPIPEDIR, ramgen); case Qdata0: return rampageread(p, va, n, offset); case Qctl: buf = smalloc(8192); s = buf; e = buf + 8192; s = seprint(s, e, "pages %p count %d ", p->pages, p->pagecount); seprint(s, e, "size %d\n", p->size); n = readstr(offset, va, n, buf); free(buf); return n; default: panic("ramread"); } return -1; /* not reached */} /* for the range offset .. offset + n, make sure we have pages */static void pages(Ram *p, long n, vlong offset){ int i; int newpagecount; uchar **newpages; newpagecount = (offset + n + BY2PG-1)/BY2PG; if(newpagecount > p->pagecount) { newpages = mallocz(sizeof(char *) * newpagecount, 1); if(!newpages) error("No more pages in devram"); memcpy(newpages, p->pages, sizeof(char *) * p->pagecount); free(p->pages); p->pages = newpages; p->pagecount = newpagecount; /* now allocate them */ for(i = offset / BY2PG; i < newpagecount; i++) { if(p->pages[i]) continue; p->pages[i] = mallocz(BY2PG, 1); } }} static long rampagewrite(Ram *p, void *va, long n, vlong offset){ int i; long total, offinpage, leninpage; long newsize; total = n; pages(p, n, offset); /* granular copy */ newsize = offset + n; for(i = offset / BY2PG; n > 0; i++) { /* i is the page */ offinpage = offset & (BY2PG - 1); leninpage = BY2PG - offinpage; /* unless there is too little left ... */ if(leninpage > n) leninpage = n; memcpy(p->pages[i] + offinpage, va, leninpage); offset += leninpage; n -= leninpage; va += leninpage; } p->size = newsize > p->size? newsize : p->size; return total;}static longramwrite(Chan *c, void *va, long n, vlong offset){ Ram *p; int i; uchar **new; uchar *page; if(!islo()) print("ramwrite hi %lux\n", getcallerpc(&c)); p = c->aux; switch(NETTYPE(c->qid.path)){ case Qdata0: n = rampagewrite(p, va, n, offset); break; case Qctl: if(strcmp(va, "free") == 0) { new = mallocz(sizeof(char *), 1); page = p->pages[0]; for(i = 1; i < p->pagecount; i++) free(p->pages[i]); free(p->pages); p->pages = new; p->pages[0] = page; p->size = 0; p->pagecount = 1; } else { error("bad command"); } break; default: panic("ramwrite"); } return n;} Dev ramdevtab = { 'R', "ram", devreset, raminit, devshutdown, ramattach, ramwalk, ramstat, ramopen, devcreate, ramclose, ramread, devbread, ramwrite, devbwrite, devremove, devwstat,};
#include "u.h"
#include "lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "error.h"
#include "netif.h"
typedef struct Ram Ram;
struct Ram
{
QLock lk;
Ram *next;
int ref;
/* simple for now */
uchar **pages;
int pagecount;
int size;
int qref[2];
ulong path;
};
struct
{
Lock lk;
ulong path;
} ramalloc;
enum
{
Qdir,
Qdata0,
Qctl,
};
Dirtab ramdir[] =
{
".", {Qdir,0,QTDIR}, 0, DMDIR|0500,
"data", {Qdata0}, 0, 0600,
"ctl", {Qctl}, 0, 0600,
};
#define NPIPEDIR 3
static void
raminit(void)
{
}
/*
* create a ram, no streams are created until an open
*/
static Chan*
ramattach(char *spec)
{
Ram *p;
Chan *c;
c = devattach('R', spec);
p = malloc(sizeof(Ram));
if(p == 0)
exhausted("memory");
p->ref = 1;
p->size = 0;
p->pagecount = 1;
p->pages = mallocz(sizeof(char *), 1);
p->pages[0] = mallocz(BY2PG, 1);
lock(&ramalloc.lk);
p->path = ++ramalloc.path;
unlock(&ramalloc.lk);
mkqid(&c->qid, NETQID(2*p->path, Qdir), 0, QTDIR);
c->aux = p;
c->dev = 0;
return c;
}
static int
ramgen(Chan *c, char *name, Dirtab *tab, int ntab, int i, Dir *dp)
{
Qid q;
int len;
Ram *p;
if(i == DEVDOTDOT){
devdir(c, c->qid, "#R", 0, eve, DMDIR|0555, dp);
return 1;
}
i++; /* skip . */
if(tab==0 || i>=ntab)
return -1;
tab += i;
p = c->aux;
switch((ulong)tab->qid.path){
case Qdata0:
len = p->size;
break;
case Qctl:
len = 0;
break;
default:
len = tab->length;
break;
}
mkqid(&q, NETQID(NETID(c->qid.path), tab->qid.path), 0, QTFILE);
devdir(c, q, tab->name, len, eve, tab->perm, dp);
return 1;
}
static Walkqid*
ramwalk(Chan *c, Chan *nc, char **name, int nname)
{
Walkqid *wq;
Ram *p;
wq = devwalk(c, nc, name, nname, ramdir, NPIPEDIR, ramgen);
if(wq != nil && wq->clone != nil && wq->clone != c){
p = c->aux;
qlock(&p->lk);
p->ref++;
if(c->flag & COPEN){
print("channel open in ramwalk\n");
switch(NETTYPE(c->qid.path)){
case Qdata0:
p->qref[0]++;
break;
case Qctl:
p->qref[1]++;
break;
}
}
qunlock(&p->lk);
}
return wq;
}
static int
ramstat(Chan *c, uchar *db, int n)
{
Ram *p;
Dir dir;
p = c->aux;
switch(NETTYPE(c->qid.path)){
case Qdir:
devdir(c, c->qid, ".", 0, eve, DMDIR|0555, &dir);
break;
case Qdata0:
devdir(c, c->qid, "data", p->size, eve, 0600, &dir);
break;
case Qctl:
devdir(c, c->qid, "ctl", 0, eve, 0600, &dir);
break;
default:
panic("ramstat");
}
n = convD2M(&dir, db, n);
if(n < BIT16SZ)
error(Eshortstat);
return n;
}
/*
* if the stream doesn't exist, create it
*/
static Chan*
ramopen(Chan *c, int omode)
{
Ram *p;
if(c->qid.type & QTDIR){
if(omode != OREAD)
error(Ebadarg);
c->mode = omode;
c->flag |= COPEN;
c->offset = 0;
return c;
}
p = c->aux;
qlock(&p->lk);
switch(NETTYPE(c->qid.path)){
case Qdata0:
p->qref[0]++;
break;
case Qctl:
p->qref[1]++;
break;
}
qunlock(&p->lk);
c->mode = openmode(omode);
c->flag |= COPEN;
c->offset = 0;
c->iounit = qiomaxatomic;
return c;
}
static void
ramclose(Chan *c)
{
Ram *p;
p = c->aux;
qlock(&p->lk);
if(c->flag & COPEN){
switch(NETTYPE(c->qid.path)){
case Qdata0:
p->qref[0]--;
break;
case Qctl:
p->qref[1]--;
break;
}
}
/*
* free the structure on last close
*/
p->ref--;
if(p->ref == 0){
int i;
qunlock(&p->lk);
for(i = 0; i < p->pagecount; i++)
free(p->pages[i]);
free(p->pages);
free(p);
} else
qunlock(&p->lk);
}
static long
rampageread(Ram *p, void *va, long n, vlong offset)
{
int i;
long total, offinpage, leninpage;
total = n;
/* figure out what range we can actually read */
if(offset > p->size)
return 0;
if(offset + n > p->size)
n = p->size - offset;
/* granular copy */
for(i = offset / BY2PG; n > 0; i++) {
/* i is the page */
offinpage = offset & (BY2PG - 1);
leninpage = BY2PG - offinpage;
/* unless there is too little left ... */
if(leninpage > n)
leninpage = n;
memcpy(va, p->pages[i] + offinpage, leninpage);
offset += offinpage;
n -= leninpage;
va += leninpage;
}
return total;
}
static long
ramread(Chan *c, void *va, long n, vlong offset)
{
Ram *p;
char *buf, *s, *e;
p = c->aux;
switch(NETTYPE(c->qid.path)){
case Qdir:
return devdirread(c, va, n, ramdir, NPIPEDIR, ramgen);
case Qdata0:
return rampageread(p, va, n, offset);
case Qctl:
buf = smalloc(8192);
s = buf;
e = buf + 8192;
s = seprint(s, e, "pages %p count %d ", p->pages, p->pagecount);
seprint(s, e, "size %d\n", p->size);
n = readstr(offset, va, n, buf);
free(buf);
return n;
default:
panic("ramread");
}
return -1; /* not reached */
}
/* for the range offset .. offset + n, make sure we have pages */
static
void pages(Ram *p, long n, vlong offset)
{
int i;
int newpagecount;
uchar **newpages;
newpagecount = (offset + n + BY2PG-1)/BY2PG;
if(newpagecount > p->pagecount) {
newpages = mallocz(sizeof(char *) * newpagecount, 1);
if(!newpages)
error("No more pages in devram");
memcpy(newpages, p->pages, sizeof(char *) * p->pagecount);
free(p->pages);
p->pages = newpages;
p->pagecount = newpagecount;
/* now allocate them */
for(i = offset / BY2PG; i < newpagecount; i++) {
if(p->pages[i])
continue;
p->pages[i] = mallocz(BY2PG, 1);
}
}
}
static long
rampagewrite(Ram *p, void *va, long n, vlong offset)
{
int i;
long total, offinpage, leninpage;
long newsize;
total = n;
pages(p, n, offset);
/* granular copy */
newsize = offset + n;
for(i = offset / BY2PG; n > 0; i++) {
/* i is the page */
offinpage = offset & (BY2PG - 1);
leninpage = BY2PG - offinpage;
/* unless there is too little left ... */
if(leninpage > n)
leninpage = n;
memcpy(p->pages[i] + offinpage, va, leninpage);
offset += leninpage;
n -= leninpage;
va += leninpage;
}
p->size = newsize > p->size? newsize : p->size;
return total;
}
static long
ramwrite(Chan *c, void *va, long n, vlong offset)
{
Ram *p;
int i;
uchar **new;
uchar *page;
if(!islo())
print("ramwrite hi %lux\n", getcallerpc(&c));
p = c->aux;
switch(NETTYPE(c->qid.path)){
case Qdata0:
n = rampagewrite(p, va, n, offset);
break;
case Qctl:
if(strcmp(va, "free") == 0) {
new = mallocz(sizeof(char *), 1);
page = p->pages[0];
for(i = 1; i < p->pagecount; i++)
free(p->pages[i]);
free(p->pages);
p->pages = new;
p->pages[0] = page;
p->size = 0;
p->pagecount = 1;
} else {
error("bad command");
}
break;
default:
panic("ramwrite");
}
return n;
}
Dev ramdevtab = {
'R',
"ram",
devreset,
raminit,
devshutdown,
ramattach,
ramwalk,
ramstat,
ramopen,
devcreate,
ramclose,
ramread,
devbread,
ramwrite,
devbwrite,
devremove,
devwstat,
};