xref: /inferno-os/os/port/devi2c.c (revision 4eb166cf184c1f102fb79e31b1465ea3e2021c39)
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
37 i2creset(void)
38 {
39 	i2csetup(0);
40 }
41 
42 static Chan*
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*
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
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*
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
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
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
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