/* * 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,}; staticDirtab 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 voidi2creset(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 inti2cstat(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 voidi2cclose(Chan *c){ I2Cdir *d; d = c->aux; if(decref(d) == 0) free(d);} static longi2cread(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 longi2cwrite(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,};
/*
* 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,
};