/*
* i2c
*
* Copyright © 1998, 2003 Vita Nuova Limited.
*/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"
typedef struct I2Cdir I2Cdir;
enum{
Qdir,
Qdata,
Qctl,
};
static
Dirtab i2ctab[]={
".", {Qdir, 0, QTDIR}, 0, 0555,
"i2cdata", {Qdata, 0}, 256, 0660,
"i2cctl", {Qctl, 0}, 0, 0660,
};
struct I2Cdir {
Ref;
I2Cdev;
Dirtab tab[nelem(i2ctab)];
};
static void
i2creset(void)
{
i2csetup(0);
}
static Chan*
i2cattach(char* spec)
{
char *s;
ulong addr;
I2Cdir *d;
Chan *c;
addr = strtoul(spec, &s, 16);
if(*spec == 0 || *s || addr >= (1<<10))
error("invalid i2c address");
d = malloc(sizeof(I2Cdir));
if(d == nil)
error(Enomem);
d->ref = 1;
d->addr = addr;
d->salen = 0;
d->tenbit = addr >= 128;
memmove(d->tab, i2ctab, sizeof(d->tab));
sprint(d->tab[1].name, "i2c.%lux.data", addr);
sprint(d->tab[2].name, "i2c.%lux.ctl", addr);
c = devattach('J', spec);
c->aux = d;
return c;
}
static Walkqid*
i2cwalk(Chan* c, Chan *nc, char **name, int nname)
{
Walkqid *wq;
I2Cdir *d;
d = c->aux;
wq = devwalk(c, nc, name, nname, d->tab, nelem(d->tab), devgen);
if(wq != nil && wq->clone != nil && wq->clone != c)
incref(d);
return wq;
}
static int
i2cstat(Chan* c, uchar *dp, int n)
{
I2Cdir *d;
d = c->aux;
return devstat(c, dp, n, d->tab, nelem(d->tab), devgen);
}
static Chan*
i2copen(Chan* c, int omode)
{
I2Cdir *d;
d = c->aux;
return devopen(c, omode, d->tab, nelem(d->tab), devgen);
}
static void
i2cclose(Chan *c)
{
I2Cdir *d;
d = c->aux;
if(decref(d) == 0)
free(d);
}
static long
i2cread(Chan *c, void *a, long n, vlong offset)
{
I2Cdir *d;
char *s, *e;
ulong len;
d = c->aux;
switch((ulong)c->qid.path){
case Qdir:
return devdirread(c, a, n, d->tab, nelem(d->tab), devgen);
case Qdata:
len = d->tab[1].length;
if(offset+n >= len){
n = len - offset;
if(n <= 0)
return 0;
}
n = i2crecv(d, a, n, offset);
break;
case Qctl:
s = smalloc(READSTR);
if(waserror()){
free(s);
nexterror();
}
e = seprint(s, s+READSTR, "size %lud\n", (ulong)d->tab[1].length);
if(d->salen)
e = seprint(e, s+READSTR, "subaddress %d\n", d->salen);
if(d->tenbit)
seprint(e, s+READSTR, "a10\n");
n = readstr(offset, a, n, s);
poperror();
free(s);
return n;
default:
n=0;
break;
}
return n;
}
static long
i2cwrite(Chan *c, void *a, long n, vlong offset)
{
I2Cdir *d;
long len;
Cmdbuf *cb;
USED(offset);
switch((ulong)c->qid.path){
case Qdata:
d = c->aux;
len = d->tab[1].length;
if(offset+n >= len){
n = len - offset;
if(n <= 0)
return 0;
}
n = i2csend(d, a, n, offset);
break;
case Qctl:
cb = parsecmd(a, n);
if(waserror()){
free(cb);
nexterror();
}
if(cb->nf < 1)
error(Ebadctl);
d = c->aux;
if(strcmp(cb->f[0], "subaddress") == 0){
if(cb->nf > 1){
len = strtol(cb->f[1], nil, 0);
if(len <= 0)
len = 0;
if(len > 4)
cmderror(cb, "subaddress too long");
}else
len = 1;
d->salen = len;
}else if(cb->nf > 1 && strcmp(cb->f[0], "size") == 0){
len = strtol(cb->f[1], nil, 0);
if(len < 0)
cmderror(cb, "size is negative");
d->tab[1].length = len;
}else if(strcmp(cb->f[0], "a10") == 0)
d->tenbit = 1;
else
cmderror(cb, "unknown control request");
poperror();
free(cb);
break;
default:
error(Ebadusefd);
}
return n;
}
Dev i2cdevtab = {
'J',
"i2c",
i2creset,
devinit,
devshutdown,
i2cattach,
i2cwalk,
i2cstat,
i2copen,
devcreate,
i2cclose,
i2cread,
devbread,
i2cwrite,
devbwrite,
devremove,
devwstat,
};
|