error: return usable error strings, not nothing.
[akaros.git] / kern / drivers / dev / pci.c
1 /* 
2  * This file is part of the UCB release of Plan 9. It is subject to the license
3  * terms in the LICENSE file found in the top-level directory of this
4  * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
5  * part of the UCB release of Plan 9, including this file, may be copied,
6  * modified, propagated, or distributed except according to the terms contained
7  * in the LICENSE file.
8  */
9
10 #include <vfs.h>
11 #include <kfs.h>
12 #include <slab.h>
13 #include <kmalloc.h>
14 #include <kref.h>
15 #include <string.h>
16 #include <stdio.h>
17 #include <assert.h>
18 #include <error.h>
19 #include <cpio.h>
20 #include <pmap.h>
21 #include <smp.h>
22 #include <ip.h>
23 #include <arch/io.h>
24
25 struct dev pcidevtab;
26
27 static char *devname(void)
28 {
29         return pcidevtab.name;
30 }
31
32 enum {
33         Qtopdir = 0,
34
35         Qpcidir,
36         Qpcictl,
37         Qpciraw,
38
39         PCI_CONFIG_SZ = 256,
40 };
41
42 #define TYPE(q)         ((uint32_t)(q).path & 0x0F)
43 #define QID(c, t)       (((c)<<4)|(t))
44
45 static struct dirtab topdir[] = {
46         {".", {Qtopdir, 0, QTDIR}, 0, 0555},
47         {"pci", {Qpcidir, 0, QTDIR}, 0, 0555},
48 };
49
50 static int pcidirgen(struct chan *c, int t, int tbdf, struct dir *dp)
51 {
52         struct qid q;
53
54         q = (struct qid) {
55         BUSBDF(tbdf) | t, 0, 0};
56         switch (t) {
57                 case Qpcictl:
58                         snprintf(get_cur_genbuf(), GENBUF_SZ, "%d.%d.%dctl",
59                                          BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf));
60                         devdir(c, q, get_cur_genbuf(), 0, eve, 0444, dp);
61                         return 1;
62                 case Qpciraw:
63                         snprintf(get_cur_genbuf(), GENBUF_SZ, "%d.%d.%draw",
64                                          BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf));
65                         devdir(c, q, get_cur_genbuf(), 128, eve, 0664, dp);
66                         return 1;
67         }
68         return -1;
69 }
70
71 static int
72 pcigen(struct chan *c, char *, struct dirtab *, int unused_int, int s,
73            struct dir *dp)
74 {
75         int tbdf;
76         Pcidev *p;
77         struct qid q;
78
79         switch (TYPE(c->qid)) {
80                 case Qtopdir:
81                         if (s == DEVDOTDOT) {
82                                 q = (struct qid) {
83                                 QID(0, Qtopdir), 0, QTDIR};
84                                 snprintf(get_cur_genbuf(), GENBUF_SZ, "#%C", pcidevtab.dc);
85                                 devdir(c, q, get_cur_genbuf(), 0, eve, 0555, dp);
86                                 return 1;
87                         }
88                         return devgen(c, NULL, topdir, ARRAY_SIZE(topdir), s, dp);
89                 case Qpcidir:
90                         if (s == DEVDOTDOT) {
91                                 q = (struct qid) {
92                                 QID(0, Qtopdir), 0, QTDIR};
93                                 snprintf(get_cur_genbuf(), GENBUF_SZ, "#%C", pcidevtab.dc);
94                                 devdir(c, q, get_cur_genbuf(), 0, eve, 0555, dp);
95                                 return 1;
96                         }
97                         p = pcimatch(NULL, 0, 0);
98                         while (s >= 2 && p != NULL) {
99                                 p = pcimatch(p, 0, 0);
100                                 s -= 2;
101                         }
102                         if (p == NULL)
103                                 return -1;
104                         return pcidirgen(c, s + Qpcictl, p->tbdf, dp);
105                 case Qpcictl:
106                 case Qpciraw:
107                         tbdf = MKBUS(BusPCI, 0, 0, 0) | BUSBDF((uint32_t) c->qid.path);
108                         p = pcimatchtbdf(tbdf);
109                         if (p == NULL)
110                                 return -1;
111                         return pcidirgen(c, TYPE(c->qid), tbdf, dp);
112                 default:
113                         break;
114         }
115         return -1;
116 }
117
118 static struct chan *pciattach(char *spec)
119 {
120         return devattach(devname(), spec);
121 }
122
123 struct walkqid *pciwalk(struct chan *c, struct chan *nc, char **name, int nname)
124 {
125         return devwalk(c, nc, name, nname, (struct dirtab *)0, 0, pcigen);
126 }
127
128 static long pcistat(struct chan *c, uint8_t * dp, long n)
129 {
130         return devstat(c, dp, n, (struct dirtab *)0, 0L, pcigen);
131 }
132
133 static struct chan *pciopen(struct chan *c, int omode)
134 {
135         c = devopen(c, omode, (struct dirtab *)0, 0, pcigen);
136         switch (TYPE(c->qid)) {
137                 default:
138                         break;
139         }
140         return c;
141 }
142
143 static void pciclose(struct chan *)
144 {
145 }
146
147 static long pciread(struct chan *c, void *va, long n, int64_t offset)
148 {
149         char buf[PCI_CONFIG_SZ], *ebuf, *w, *a;
150         int i, tbdf, r;
151         uint32_t x;
152         Pcidev *p;
153
154         a = va;
155         switch (TYPE(c->qid)) {
156                 case Qtopdir:
157                 case Qpcidir:
158                         return devdirread(c, a, n, (struct dirtab *)0, 0L, pcigen);
159                 case Qpcictl:
160                         tbdf = MKBUS(BusPCI, 0, 0, 0) | BUSBDF((uint32_t) c->qid.path);
161                         p = pcimatchtbdf(tbdf);
162                         if (p == NULL)
163                                 error(EINVAL, ERROR_FIXME);
164                         ebuf = buf + sizeof buf - 1;    /* -1 for newline */
165                         w = seprintf(buf, ebuf, "%.2x.%.2x.%.2x %.4x/%.4x %3d",
166                                                  p->ccrb, p->ccru, p->ccrp, p->vid, p->did, p->intl);
167                         for (i = 0; i < ARRAY_SIZE(p->mem); i++) {
168                                 if (p->mem[i].size == 0)
169                                         continue;
170                                 w = seprintf(w, ebuf, " %d:%.8lux %d", i, p->mem[i].bar,
171                                                          p->mem[i].size);
172                         }
173                         *w++ = '\n';
174                         *w = '\0';
175                         return readstr(offset, a, n, buf);
176                 case Qpciraw:
177                         tbdf = MKBUS(BusPCI, 0, 0, 0) | BUSBDF((uint32_t) c->qid.path);
178                         p = pcimatchtbdf(tbdf);
179                         if (p == NULL)
180                                 error(EINVAL, ERROR_FIXME);
181                         if (n + offset > 256)
182                                 n = 256 - offset;
183                         if (n < 0)
184                                 return 0;
185                         r = offset;
186                         if (!(r & 3) && n == 4) {
187                                 x = pcicfgr32(p, r);
188                                 PBIT32(a, x);
189                                 return 4;
190                         }
191                         if (!(r & 1) && n == 2) {
192                                 x = pcicfgr16(p, r);
193                                 PBIT16(a, x);
194                                 return 2;
195                         }
196                         for (i = 0; i < n; i++) {
197                                 x = pcicfgr8(p, r);
198                                 PBIT8(a, x);
199                                 a++;
200                                 r++;
201                         }
202                         return i;
203                 default:
204                         error(EINVAL, ERROR_FIXME);
205         }
206         return n;
207 }
208
209 static long pciwrite(struct chan *c, void *va, long n, int64_t offset)
210 {
211         uint8_t *a;
212         int i, r, tbdf;
213         uint32_t x;
214         Pcidev *p;
215
216         if (n > PCI_CONFIG_SZ)
217                 n = PCI_CONFIG_SZ;
218         a = va;
219
220         switch (TYPE(c->qid)) {
221                 case Qpciraw:
222                         tbdf = MKBUS(BusPCI, 0, 0, 0) | BUSBDF((uint32_t) c->qid.path);
223                         p = pcimatchtbdf(tbdf);
224                         if (p == NULL)
225                                 error(EINVAL, ERROR_FIXME);
226                         if (offset > PCI_CONFIG_SZ)
227                                 return 0;
228                         if (n + offset > PCI_CONFIG_SZ)
229                                 n = PCI_CONFIG_SZ - offset;
230                         r = offset;
231                         if (!(r & 3) && n == 4) {
232                                 x = GBIT32(a);
233                                 pcicfgw32(p, r, x);
234                                 return 4;
235                         }
236                         if (!(r & 1) && n == 2) {
237                                 x = GBIT16(a);
238                                 pcicfgw16(p, r, x);
239                                 return 2;
240                         }
241                         for (i = 0; i < n; i++) {
242                                 x = GBIT8(a);
243                                 pcicfgw8(p, r, x);
244                                 a++;
245                                 r++;
246                         }
247                         return i;
248                 default:
249                         error(EINVAL, ERROR_FIXME);
250         }
251         return n;
252 }
253
254 struct dev pcidevtab = {
255         .name = "pci",
256
257         .reset = devreset,
258         .init = devinit,
259         .shutdown = devshutdown,
260         .attach = pciattach,
261         .walk = pciwalk,
262         .stat = pcistat,
263         .open = pciopen,
264         .create = devcreate,
265         .close = pciclose,
266         .read = pciread,
267         .bread = devbread,
268         .write = pciwrite,
269         .bwrite = devbwrite,
270         .remove = devremove,
271         .wstat = devwstat,
272 };