summaryrefslogtreecommitdiff
path: root/os/port/devi2c.c
diff options
context:
space:
mode:
Diffstat (limited to 'os/port/devi2c.c')
-rw-r--r--os/port/devi2c.c227
1 files changed, 227 insertions, 0 deletions
diff --git a/os/port/devi2c.c b/os/port/devi2c.c
new file mode 100644
index 00000000..50512248
--- /dev/null
+++ b/os/port/devi2c.c
@@ -0,0 +1,227 @@
+/*
+ * 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,
+};