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