b24386c18b49b425c5d1080f455383d0bd1f4098
[akaros.git] / kern / drivers / dev / dev.c
1 // INFERNO
2 #include <vfs.h>
3 #include <kfs.h>
4 #include <slab.h>
5 #include <kmalloc.h>
6 #include <kref.h>
7 #include <string.h>
8 #include <stdio.h>
9 #include <assert.h>
10 #include <error.h>
11 #include <cpio.h>
12 #include <pmap.h>
13 #include <smp.h>
14 #include <ip.h>
15
16 extern uint32_t kerndate;
17 extern char *eve;
18
19 void
20 mkqid(struct qid *q, int64_t path, uint32_t vers, int type)
21 {
22         q->type = type;
23         q->vers = vers;
24         q->path = path;
25 }
26
27 int
28 devno(int c, int user)
29 {
30         int i;
31
32         for(i = 0; devtab[i] != NULL; i++) {
33                 if(devtab[i]->dc == c)
34                         return i;
35         }
36         if(user == 0)
37                 panic("devno %C 0x%ux", c, c);
38
39         return -1;
40 }
41
42 void
43 devdir(struct chan *c, struct qid qid, char *n,
44        int64_t length, char *user, long perm, struct dir *db)
45 {
46         db->name = n;
47         if(c->flag&CMSG)
48                 qid.type |= QTMOUNT;
49         db->qid = qid;
50         db->type = devtab[c->type]->dc;
51         db->dev = c->dev;
52         db->mode = perm;
53         db->mode |= qid.type << 24;
54         db->atime = seconds();
55         db->mtime = kerndate;
56         db->length = length;
57         db->uid = user;
58         db->gid = eve;
59         db->muid = user;
60 }
61
62 /*
63  * the zeroth element of the table MUST be the directory itself for ..
64 */
65 int
66 devgen(struct chan *c, char *unused_char_p_t, struct dirtab *tab, int ntab, int i,
67        struct dir *dp)
68 {
69         if(tab == 0)
70                 return -1;
71         if(i != DEVDOTDOT){
72                 /* skip over the first element, that for . itself */
73                 i++;
74                 if(i >= ntab)
75                         return -1;
76                 tab += i;
77         }
78         devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp);
79         return 1;
80 }
81
82 void
83 devreset(void)
84 {
85 }
86
87 void
88 devinit(void)
89 {
90 }
91
92 void
93 devshutdown(void)
94 {
95 }
96
97 struct chan*
98 devattach(int tc, char *spec)
99 {
100         struct chan *c;
101         char *buf;
102
103         c = newchan();
104         mkqid(&c->qid, 0, 0, QTDIR);
105         c->type = devno(tc, 0);
106         if(spec == NULL)
107                 spec = "";
108         buf = kzmalloc(4 + strlen(spec) + 1, 0);
109         snprintf(buf, sizeof(buf), "#%C%s", tc, spec);
110         c->name = newcname(buf);
111         kfree(buf);
112         return c;
113 }
114
115
116 struct chan*
117 devclone(struct chan *c)
118 {
119         struct chan *nc;
120
121         if(c->flag & COPEN)
122                 panic("clone of open file type %C\n", devtab[c->type]->dc);
123
124         nc = newchan();
125
126         nc->type = c->type;
127         nc->dev = c->dev;
128         nc->mode = c->mode;
129         nc->qid = c->qid;
130         nc->offset = c->offset;
131         nc->umh = NULL;
132         nc->mountid = c->mountid;
133         nc->aux = c->aux;
134         nc->mqid = c->mqid;
135         nc->mcp = c->mcp;
136         return nc;
137 }
138
139 struct walkqid*
140 devwalk(struct chan *c,
141         struct chan *nc, char **name, int nname,
142         struct dirtab *tab, int ntab, Devgen *gen)
143 {
144         ERRSTACK(2);
145         int i, j;
146         volatile int alloc; /* to keep waserror from optimizing this out */
147         struct walkqid *wq;
148         char *n;
149         struct dir dir;
150
151         if(nname > 0)
152                 isdir(c);
153
154         alloc = 0;
155         wq = kzmalloc(sizeof(struct walkqid) + (nname - 1) * sizeof(struct qid), 0);
156         if(waserror()){
157                 if(alloc && wq->clone!=NULL)
158                         cclose(wq->clone);
159                 kfree(wq);
160                 return NULL;
161         }
162         if(nc == NULL){
163                 nc = devclone(c);
164                 nc->type = 0;   /* device doesn't know about this channel yet */
165                 alloc = 1;
166         }
167         wq->clone = nc;
168
169         for(j=0; j<nname; j++){
170                 if(!(nc->qid.type&QTDIR)){
171                         if(j==0)
172                                 error(Enotdir);
173                         goto Done;
174                 }
175                 n = name[j];
176                 if(strcmp(n, ".") == 0){
177     Accept:
178                         wq->qid[wq->nqid++] = nc->qid;
179                         continue;
180                 }
181                 if(strcmp(n, "..") == 0){
182                         (*gen)(nc, NULL, tab, ntab, DEVDOTDOT, &dir);
183                         nc->qid = dir.qid;
184                         goto Accept;
185                 }
186                 /*
187                  * Ugly problem: If we're using devgen, make sure we're
188                  * walking the directory itself, represented by the first
189                  * entry in the table, and not trying to step into a sub-
190                  * directory of the table, e.g. /net/net. Devgen itself
191                  * should take care of the problem, but it doesn't have
192                  * the necessary information (that we're doing a walk).
193                  */
194                 if(gen==devgen && nc->qid.path!=tab[0].qid.path)
195                         goto Notfound;
196                 for(i=0;; i++) {
197                         switch((*gen)(nc, n, tab, ntab, i, &dir)){
198                         case -1:
199                         Notfound:
200                                 if(j == 0)
201                                         error(Enonexist);
202                                 set_errstr(Enonexist);
203                                 goto Done;
204                         case 0:
205                                 continue;
206                         case 1:
207                                 if(strcmp(n, dir.name) == 0){
208                                         nc->qid = dir.qid;
209                                         goto Accept;
210                                 }
211                                 continue;
212                         }
213                 }
214         }
215         /*
216          * We processed at least one name, so will return some data.
217          * If we didn't process all nname entries succesfully, we drop
218          * the cloned channel and return just the Qids of the walks.
219          */
220 Done:
221         poperror();
222         if(wq->nqid < nname){
223                 if(alloc)
224                         cclose(wq->clone);
225                 wq->clone = NULL;
226         }else if(wq->clone){
227                 /* attach cloned channel to same device */
228                 wq->clone->type = c->type;
229         }
230         return wq;
231 }
232
233 int
234 devstat(struct chan *c, uint8_t *db, int n,
235         struct dirtab *tab, int ntab, Devgen *gen)
236 {
237         int i;
238         struct dir dir;
239         char *p, *elem;
240
241         for(i=0;; i++)
242                 switch((*gen)(c, NULL, tab, ntab, i, &dir)){
243                 case -1:
244                         if(c->qid.type & QTDIR){
245                                 if(c->name == NULL)
246                                         elem = "???";
247                                 else if(strcmp(c->name->s, "/") == 0)
248                                         elem = "/";
249                                 else
250                                         for(elem=p=c->name->s; *p; p++)
251                                                 if(*p == '/')
252                                                         elem = p+1;
253                                 devdir(c, c->qid, elem, 0, eve, DMDIR|0555, &dir);
254                                 n = convD2M(&dir, db, n);
255                                 if(n == 0)
256                                         error(Ebadarg);
257                                 return n;
258                         }
259                         printd("%s %s: devstat %C %llux\n",
260                                 up->text, up->env->user,
261                                 devtab[c->type]->dc, c->qid.path);
262
263                         error(Enonexist);
264                 case 0:
265                         break;
266                 case 1:
267                         if(c->qid.path == dir.qid.path) {
268                                 if(c->flag&CMSG)
269                                         dir.mode |= DMMOUNT;
270                                 n = convD2M(&dir, db, n);
271                                 if(n == 0)
272                                         error(Ebadarg);
273                                 return n;
274                         }
275                         break;
276                 }
277 }
278
279 long
280 devdirread(struct chan *c, char *d, long n,
281            struct dirtab *tab, int ntab, Devgen *gen)
282 {
283         long m, dsz;
284         /* this is gross. Make it 2 so we have room at the end for
285          * bad things.
286          */
287         struct dir dir[4];
288
289         for(m=0; m<n; c->dri++) {
290                 switch((*gen)(c, NULL, tab, ntab, c->dri, &dir[0])){
291                 case -1:
292                         return m;
293
294                 case 0:
295                         break;
296
297                 case 1:
298                         dsz = convD2M(&dir[0], ( uint8_t *)d, n-m);
299                         if(dsz <= BIT16SZ){     /* <= not < because this isn't stat; read is stuck */
300                                 if(m == 0)
301                                         error(Eshort);
302                                 return m;
303                         }
304                         m += dsz;
305                         d += dsz;
306                         break;
307                 }
308         }
309
310         return m;
311 }
312
313 /*
314  * error(Eperm) if open permission not granted for up->env->user.
315  */
316 void
317 devpermcheck(char *fileuid, uint32_t perm, int omode)
318 {
319         uint32_t t;
320         static int access[] = { 0400, 0200, 0600, 0100 };
321
322         if(strcmp(current->user, fileuid) == 0)
323                 perm <<= 0;
324         else
325         if(strcmp(current->user, eve) == 0)
326                 perm <<= 3;
327         else
328                 perm <<= 6;
329
330         t = access[omode&3];
331         if((t&perm) != t)
332                 error(Eperm);
333 }
334
335 struct chan*
336 devopen(struct chan *c, int omode, struct dirtab *tab, int ntab, Devgen *gen)
337 {
338         int i;
339         struct dir dir;
340
341         for(i=0;; i++) {
342                 switch((*gen)(c, NULL, tab, ntab, i, &dir)){
343                 case -1:
344                         goto Return;
345                 case 0:
346                         break;
347                 case 1:
348                         if(c->qid.path == dir.qid.path) {
349                                 devpermcheck(dir.uid, dir.mode, omode);
350                                 goto Return;
351                         }
352                         break;
353                 }
354         }
355 Return:
356         c->offset = 0;
357         if((c->qid.type&QTDIR) && omode!=OREAD)
358                 error(Eperm);
359         c->mode = openmode(omode);
360         c->flag |= COPEN;
361         return c;
362 }
363
364 void
365 devcreate(struct chan*c, char *unused_char_p_t, int unused_int, uint32_t u)
366 {
367         error(Eperm);
368 }
369
370 struct block*
371 devbread(struct chan *c, long n, uint32_t offset)
372 {
373         ERRSTACK(2);
374         struct block *bp;
375
376         bp = allocb(n);
377         if(bp == 0)
378                 error(Enomem);
379         if(waserror()) {
380                 freeb(bp);
381                 nexterror();
382         }
383         bp->wp += devtab[c->type]->read(c, bp->wp, n, offset);
384         poperror();
385         return bp;
386 }
387
388 long
389 devbwrite(struct chan *c, struct block *bp, uint32_t offset)
390 {
391         ERRSTACK(2);
392         long n;
393
394         if(waserror()) {
395                 freeb(bp);
396                 nexterror();
397         }
398         n = devtab[c->type]->write(c, bp->rp, BLEN(bp), offset);
399         poperror();
400         freeb(bp);
401
402         return n;
403 }
404
405 void
406 devremove(struct chan*c)
407 {
408         error(Eperm);
409 }
410
411 int
412 devwstat(struct chan*c, uint8_t *unused_uint8_p_t, int i)
413 {
414         error(Eperm);
415         return 0;
416 }
417
418 void
419 devpower(int i)
420 {
421         error(Eperm);
422 }
423
424 #if 0
425 int
426 devconfig( int unused_int, char *c, DevConf *)
427 {
428         error(Eperm);
429         return 0;
430 }
431 #endif
432 /*
433  * check that the name in a wstat is plausible
434  */
435 void
436 validwstatname(char *name)
437 {
438         validname(name, 0);
439         if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
440                 error(Efilename);
441 }
442
443 struct dev*
444 devbyname(char *name)
445 {
446         int i;
447
448         for(i = 0; devtab[i] != NULL; i++)
449                 if(strcmp(devtab[i]->name, name) == 0)
450                         return devtab[i];
451         return NULL;
452 }
453
454