1*5c47fe09SDavid du Colombier /*
2*5c47fe09SDavid du Colombier * i2c
3*5c47fe09SDavid du Colombier *
4*5c47fe09SDavid du Colombier * Copyright © 1998, 2003 Vita Nuova Limited.
5*5c47fe09SDavid du Colombier */
6*5c47fe09SDavid du Colombier
7*5c47fe09SDavid du Colombier #include "u.h"
8*5c47fe09SDavid du Colombier #include "../port/lib.h"
9*5c47fe09SDavid du Colombier #include "mem.h"
10*5c47fe09SDavid du Colombier #include "dat.h"
11*5c47fe09SDavid du Colombier #include "fns.h"
12*5c47fe09SDavid du Colombier #include "io.h"
13*5c47fe09SDavid du Colombier #include "../port/error.h"
14*5c47fe09SDavid du Colombier
15*5c47fe09SDavid du Colombier typedef struct I2Cdir I2Cdir;
16*5c47fe09SDavid du Colombier
17*5c47fe09SDavid du Colombier enum{
18*5c47fe09SDavid du Colombier Qdir,
19*5c47fe09SDavid du Colombier Qdata,
20*5c47fe09SDavid du Colombier Qctl,
21*5c47fe09SDavid du Colombier };
22*5c47fe09SDavid du Colombier
23*5c47fe09SDavid du Colombier static
24*5c47fe09SDavid du Colombier Dirtab i2ctab[]={
25*5c47fe09SDavid du Colombier ".", {Qdir, 0, QTDIR}, 0, 0555,
26*5c47fe09SDavid du Colombier "i2cdata", {Qdata, 0}, 256, 0660,
27*5c47fe09SDavid du Colombier "i2cctl", {Qctl, 0}, 0, 0660,
28*5c47fe09SDavid du Colombier };
29*5c47fe09SDavid du Colombier
30*5c47fe09SDavid du Colombier struct I2Cdir {
31*5c47fe09SDavid du Colombier Ref;
32*5c47fe09SDavid du Colombier I2Cdev;
33*5c47fe09SDavid du Colombier Dirtab tab[nelem(i2ctab)];
34*5c47fe09SDavid du Colombier };
35*5c47fe09SDavid du Colombier
36*5c47fe09SDavid du Colombier static void
i2creset(void)37*5c47fe09SDavid du Colombier i2creset(void)
38*5c47fe09SDavid du Colombier {
39*5c47fe09SDavid du Colombier i2csetup(0);
40*5c47fe09SDavid du Colombier }
41*5c47fe09SDavid du Colombier
42*5c47fe09SDavid du Colombier static Chan*
i2cattach(char * spec)43*5c47fe09SDavid du Colombier i2cattach(char* spec)
44*5c47fe09SDavid du Colombier {
45*5c47fe09SDavid du Colombier char *s;
46*5c47fe09SDavid du Colombier ulong addr;
47*5c47fe09SDavid du Colombier I2Cdir *d;
48*5c47fe09SDavid du Colombier Chan *c;
49*5c47fe09SDavid du Colombier
50*5c47fe09SDavid du Colombier addr = strtoul(spec, &s, 16);
51*5c47fe09SDavid du Colombier if(*spec == 0 || *s || addr >= (1<<10))
52*5c47fe09SDavid du Colombier error("invalid i2c address");
53*5c47fe09SDavid du Colombier d = malloc(sizeof(I2Cdir));
54*5c47fe09SDavid du Colombier if(d == nil)
55*5c47fe09SDavid du Colombier error(Enomem);
56*5c47fe09SDavid du Colombier d->ref = 1;
57*5c47fe09SDavid du Colombier d->addr = addr;
58*5c47fe09SDavid du Colombier d->salen = 0;
59*5c47fe09SDavid du Colombier d->tenbit = addr >= 128;
60*5c47fe09SDavid du Colombier memmove(d->tab, i2ctab, sizeof(d->tab));
61*5c47fe09SDavid du Colombier sprint(d->tab[1].name, "i2c.%lux.data", addr);
62*5c47fe09SDavid du Colombier sprint(d->tab[2].name, "i2c.%lux.ctl", addr);
63*5c47fe09SDavid du Colombier
64*5c47fe09SDavid du Colombier c = devattach('J', spec);
65*5c47fe09SDavid du Colombier c->aux = d;
66*5c47fe09SDavid du Colombier return c;
67*5c47fe09SDavid du Colombier }
68*5c47fe09SDavid du Colombier
69*5c47fe09SDavid du Colombier static Walkqid*
i2cwalk(Chan * c,Chan * nc,char ** name,int nname)70*5c47fe09SDavid du Colombier i2cwalk(Chan* c, Chan *nc, char **name, int nname)
71*5c47fe09SDavid du Colombier {
72*5c47fe09SDavid du Colombier Walkqid *wq;
73*5c47fe09SDavid du Colombier I2Cdir *d;
74*5c47fe09SDavid du Colombier
75*5c47fe09SDavid du Colombier d = c->aux;
76*5c47fe09SDavid du Colombier wq = devwalk(c, nc, name, nname, d->tab, nelem(d->tab), devgen);
77*5c47fe09SDavid du Colombier if(wq != nil && wq->clone != nil && wq->clone != c)
78*5c47fe09SDavid du Colombier incref(d);
79*5c47fe09SDavid du Colombier return wq;
80*5c47fe09SDavid du Colombier }
81*5c47fe09SDavid du Colombier
82*5c47fe09SDavid du Colombier static int
i2cstat(Chan * c,uchar * dp,int n)83*5c47fe09SDavid du Colombier i2cstat(Chan* c, uchar *dp, int n)
84*5c47fe09SDavid du Colombier {
85*5c47fe09SDavid du Colombier I2Cdir *d;
86*5c47fe09SDavid du Colombier
87*5c47fe09SDavid du Colombier d = c->aux;
88*5c47fe09SDavid du Colombier return devstat(c, dp, n, d->tab, nelem(d->tab), devgen);
89*5c47fe09SDavid du Colombier }
90*5c47fe09SDavid du Colombier
91*5c47fe09SDavid du Colombier static Chan*
i2copen(Chan * c,int omode)92*5c47fe09SDavid du Colombier i2copen(Chan* c, int omode)
93*5c47fe09SDavid du Colombier {
94*5c47fe09SDavid du Colombier I2Cdir *d;
95*5c47fe09SDavid du Colombier
96*5c47fe09SDavid du Colombier d = c->aux;
97*5c47fe09SDavid du Colombier return devopen(c, omode, d->tab, nelem(d->tab), devgen);
98*5c47fe09SDavid du Colombier }
99*5c47fe09SDavid du Colombier
100*5c47fe09SDavid du Colombier static void
i2cclose(Chan * c)101*5c47fe09SDavid du Colombier i2cclose(Chan *c)
102*5c47fe09SDavid du Colombier {
103*5c47fe09SDavid du Colombier I2Cdir *d;
104*5c47fe09SDavid du Colombier
105*5c47fe09SDavid du Colombier d = c->aux;
106*5c47fe09SDavid du Colombier if(decref(d) == 0)
107*5c47fe09SDavid du Colombier free(d);
108*5c47fe09SDavid du Colombier }
109*5c47fe09SDavid du Colombier
110*5c47fe09SDavid du Colombier static long
i2cread(Chan * c,void * a,long n,vlong offset)111*5c47fe09SDavid du Colombier i2cread(Chan *c, void *a, long n, vlong offset)
112*5c47fe09SDavid du Colombier {
113*5c47fe09SDavid du Colombier I2Cdir *d;
114*5c47fe09SDavid du Colombier char *s, *e;
115*5c47fe09SDavid du Colombier ulong len;
116*5c47fe09SDavid du Colombier
117*5c47fe09SDavid du Colombier d = c->aux;
118*5c47fe09SDavid du Colombier switch((ulong)c->qid.path){
119*5c47fe09SDavid du Colombier case Qdir:
120*5c47fe09SDavid du Colombier return devdirread(c, a, n, d->tab, nelem(d->tab), devgen);
121*5c47fe09SDavid du Colombier case Qdata:
122*5c47fe09SDavid du Colombier len = d->tab[1].length;
123*5c47fe09SDavid du Colombier if(offset+n >= len){
124*5c47fe09SDavid du Colombier n = len - offset;
125*5c47fe09SDavid du Colombier if(n <= 0)
126*5c47fe09SDavid du Colombier return 0;
127*5c47fe09SDavid du Colombier }
128*5c47fe09SDavid du Colombier n = i2crecv(d, a, n, offset);
129*5c47fe09SDavid du Colombier break;
130*5c47fe09SDavid du Colombier case Qctl:
131*5c47fe09SDavid du Colombier s = smalloc(READSTR);
132*5c47fe09SDavid du Colombier if(waserror()){
133*5c47fe09SDavid du Colombier free(s);
134*5c47fe09SDavid du Colombier nexterror();
135*5c47fe09SDavid du Colombier }
136*5c47fe09SDavid du Colombier e = seprint(s, s+READSTR, "size %lud\n", (ulong)d->tab[1].length);
137*5c47fe09SDavid du Colombier if(d->salen)
138*5c47fe09SDavid du Colombier e = seprint(e, s+READSTR, "subaddress %d\n", d->salen);
139*5c47fe09SDavid du Colombier if(d->tenbit)
140*5c47fe09SDavid du Colombier seprint(e, s+READSTR, "a10\n");
141*5c47fe09SDavid du Colombier n = readstr(offset, a, n, s);
142*5c47fe09SDavid du Colombier poperror();
143*5c47fe09SDavid du Colombier free(s);
144*5c47fe09SDavid du Colombier return n;
145*5c47fe09SDavid du Colombier default:
146*5c47fe09SDavid du Colombier n=0;
147*5c47fe09SDavid du Colombier break;
148*5c47fe09SDavid du Colombier }
149*5c47fe09SDavid du Colombier return n;
150*5c47fe09SDavid du Colombier }
151*5c47fe09SDavid du Colombier
152*5c47fe09SDavid du Colombier static long
i2cwrite(Chan * c,void * a,long n,vlong offset)153*5c47fe09SDavid du Colombier i2cwrite(Chan *c, void *a, long n, vlong offset)
154*5c47fe09SDavid du Colombier {
155*5c47fe09SDavid du Colombier I2Cdir *d;
156*5c47fe09SDavid du Colombier long len;
157*5c47fe09SDavid du Colombier Cmdbuf *cb;
158*5c47fe09SDavid du Colombier
159*5c47fe09SDavid du Colombier USED(offset);
160*5c47fe09SDavid du Colombier switch((ulong)c->qid.path){
161*5c47fe09SDavid du Colombier case Qdata:
162*5c47fe09SDavid du Colombier d = c->aux;
163*5c47fe09SDavid du Colombier len = d->tab[1].length;
164*5c47fe09SDavid du Colombier if(offset+n >= len){
165*5c47fe09SDavid du Colombier n = len - offset;
166*5c47fe09SDavid du Colombier if(n <= 0)
167*5c47fe09SDavid du Colombier return 0;
168*5c47fe09SDavid du Colombier }
169*5c47fe09SDavid du Colombier n = i2csend(d, a, n, offset);
170*5c47fe09SDavid du Colombier break;
171*5c47fe09SDavid du Colombier case Qctl:
172*5c47fe09SDavid du Colombier cb = parsecmd(a, n);
173*5c47fe09SDavid du Colombier if(waserror()){
174*5c47fe09SDavid du Colombier free(cb);
175*5c47fe09SDavid du Colombier nexterror();
176*5c47fe09SDavid du Colombier }
177*5c47fe09SDavid du Colombier if(cb->nf < 1)
178*5c47fe09SDavid du Colombier error(Ebadctl);
179*5c47fe09SDavid du Colombier d = c->aux;
180*5c47fe09SDavid du Colombier if(strcmp(cb->f[0], "subaddress") == 0){
181*5c47fe09SDavid du Colombier if(cb->nf > 1){
182*5c47fe09SDavid du Colombier len = strtol(cb->f[1], nil, 0);
183*5c47fe09SDavid du Colombier if(len <= 0)
184*5c47fe09SDavid du Colombier len = 0;
185*5c47fe09SDavid du Colombier if(len > 4)
186*5c47fe09SDavid du Colombier cmderror(cb, "subaddress too long");
187*5c47fe09SDavid du Colombier }else
188*5c47fe09SDavid du Colombier len = 1;
189*5c47fe09SDavid du Colombier d->salen = len;
190*5c47fe09SDavid du Colombier }else if(cb->nf > 1 && strcmp(cb->f[0], "size") == 0){
191*5c47fe09SDavid du Colombier len = strtol(cb->f[1], nil, 0);
192*5c47fe09SDavid du Colombier if(len < 0)
193*5c47fe09SDavid du Colombier cmderror(cb, "size is negative");
194*5c47fe09SDavid du Colombier d->tab[1].length = len;
195*5c47fe09SDavid du Colombier }else if(strcmp(cb->f[0], "a10") == 0)
196*5c47fe09SDavid du Colombier d->tenbit = 1;
197*5c47fe09SDavid du Colombier else
198*5c47fe09SDavid du Colombier cmderror(cb, "unknown control request");
199*5c47fe09SDavid du Colombier poperror();
200*5c47fe09SDavid du Colombier free(cb);
201*5c47fe09SDavid du Colombier break;
202*5c47fe09SDavid du Colombier default:
203*5c47fe09SDavid du Colombier error(Ebadusefd);
204*5c47fe09SDavid du Colombier }
205*5c47fe09SDavid du Colombier return n;
206*5c47fe09SDavid du Colombier }
207*5c47fe09SDavid du Colombier
208*5c47fe09SDavid du Colombier Dev i2cdevtab = {
209*5c47fe09SDavid du Colombier 'J',
210*5c47fe09SDavid du Colombier "i2c",
211*5c47fe09SDavid du Colombier
212*5c47fe09SDavid du Colombier i2creset,
213*5c47fe09SDavid du Colombier devinit,
214*5c47fe09SDavid du Colombier devshutdown,
215*5c47fe09SDavid du Colombier i2cattach,
216*5c47fe09SDavid du Colombier i2cwalk,
217*5c47fe09SDavid du Colombier i2cstat,
218*5c47fe09SDavid du Colombier i2copen,
219*5c47fe09SDavid du Colombier devcreate,
220*5c47fe09SDavid du Colombier i2cclose,
221*5c47fe09SDavid du Colombier i2cread,
222*5c47fe09SDavid du Colombier devbread,
223*5c47fe09SDavid du Colombier i2cwrite,
224*5c47fe09SDavid du Colombier devbwrite,
225*5c47fe09SDavid du Colombier devremove,
226*5c47fe09SDavid du Colombier devwstat,
227*5c47fe09SDavid du Colombier };
228