Added explicit errno reporting from error() API.
[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
40 #define TYPE(q)         ((uint32_t)(q).path & 0x0F)
41 #define QID(c, t)       (((c)<<4)|(t))
42
43 static struct dirtab topdir[] = {
44         {".", {Qtopdir, 0, QTDIR}, 0, 0555},
45         {"pci", {Qpcidir, 0, QTDIR}, 0, 0555},
46 };
47
48 static int pcidirgen(struct chan *c, int t, int tbdf, struct dir *dp)
49 {
50         struct qid q;
51
52         q = (struct qid) {
53         BUSBDF(tbdf) | t, 0, 0};
54         switch (t) {
55                 case Qpcictl:
56                         snprintf(get_cur_genbuf(), GENBUF_SZ, "%d.%d.%dctl",
57                                          BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf));
58                         devdir(c, q, get_cur_genbuf(), 0, eve, 0444, dp);
59                         return 1;
60                 case Qpciraw:
61                         snprintf(get_cur_genbuf(), GENBUF_SZ, "%d.%d.%draw",
62                                          BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf));
63                         devdir(c, q, get_cur_genbuf(), 128, eve, 0664, dp);
64                         return 1;
65         }
66         return -1;
67 }
68
69 static int
70 pcigen(struct chan *c, char *, struct dirtab *, int unused_int, int s,
71            struct dir *dp)
72 {
73         int tbdf;
74         Pcidev *p;
75         struct qid q;
76
77         switch (TYPE(c->qid)) {
78                 case Qtopdir:
79                         if (s == DEVDOTDOT) {
80                                 q = (struct qid) {
81                                 QID(0, Qtopdir), 0, QTDIR};
82                                 snprintf(get_cur_genbuf(), GENBUF_SZ, "#%C", pcidevtab.dc);
83                                 devdir(c, q, get_cur_genbuf(), 0, eve, 0555, dp);
84                                 return 1;
85                         }
86                         return devgen(c, NULL, topdir, ARRAY_SIZE(topdir), s, dp);
87                 case Qpcidir:
88                         if (s == DEVDOTDOT) {
89                                 q = (struct qid) {
90                                 QID(0, Qtopdir), 0, QTDIR};
91                                 snprintf(get_cur_genbuf(), GENBUF_SZ, "#%C", pcidevtab.dc);
92                                 devdir(c, q, get_cur_genbuf(), 0, eve, 0555, dp);
93                                 return 1;
94                         }
95                         p = pcimatch(NULL, 0, 0);
96                         while (s >= 2 && p != NULL) {
97                                 p = pcimatch(p, 0, 0);
98                                 s -= 2;
99                         }
100                         if (p == NULL)
101                                 return -1;
102                         return pcidirgen(c, s + Qpcictl, p->tbdf, dp);
103                 case Qpcictl:
104                 case Qpciraw:
105                         tbdf = MKBUS(BusPCI, 0, 0, 0) | BUSBDF((uint32_t) c->qid.path);
106                         p = pcimatchtbdf(tbdf);
107                         if (p == NULL)
108                                 return -1;
109                         return pcidirgen(c, TYPE(c->qid), tbdf, dp);
110                 default:
111                         break;
112         }
113         return -1;
114 }
115
116 static struct chan *pciattach(char *spec)
117 {
118         return devattach(devname(), spec);
119 }
120
121 struct walkqid *pciwalk(struct chan *c, struct chan *nc, char **name, int nname)
122 {
123         return devwalk(c, nc, name, nname, (struct dirtab *)0, 0, pcigen);
124 }
125
126 static long pcistat(struct chan *c, uint8_t * dp, long n)
127 {
128         return devstat(c, dp, n, (struct dirtab *)0, 0L, pcigen);
129 }
130
131 static struct chan *pciopen(struct chan *c, int omode)
132 {
133         c = devopen(c, omode, (struct dirtab *)0, 0, pcigen);
134         switch (TYPE(c->qid)) {
135                 default:
136                         break;
137         }
138         return c;
139 }
140
141 static void pciclose(struct chan *)
142 {
143 }
144
145 static long pciread(struct chan *c, void *va, long n, int64_t offset)
146 {
147         char buf[256], *ebuf, *w, *a;
148         int i, tbdf, r;
149         uint32_t x;
150         Pcidev *p;
151
152         a = va;
153         switch (TYPE(c->qid)) {
154                 case Qtopdir:
155                 case Qpcidir:
156                         return devdirread(c, a, n, (struct dirtab *)0, 0L, pcigen);
157                 case Qpcictl:
158                         tbdf = MKBUS(BusPCI, 0, 0, 0) | BUSBDF((uint32_t) c->qid.path);
159                         p = pcimatchtbdf(tbdf);
160                         if (p == NULL)
161                                 error(EINVAL, NULL);
162                         ebuf = buf + sizeof buf - 1;    /* -1 for newline */
163                         w = seprintf(buf, ebuf, "%.2x.%.2x.%.2x %.4x/%.4x %3d",
164                                                  p->ccrb, p->ccru, p->ccrp, p->vid, p->did, p->intl);
165                         for (i = 0; i < ARRAY_SIZE(p->mem); i++) {
166                                 if (p->mem[i].size == 0)
167                                         continue;
168                                 w = seprintf(w, ebuf, " %d:%.8lux %d", i, p->mem[i].bar,
169                                                          p->mem[i].size);
170                         }
171                         *w++ = '\n';
172                         *w = '\0';
173                         return readstr(offset, a, n, buf);
174                 case Qpciraw:
175                         tbdf = MKBUS(BusPCI, 0, 0, 0) | BUSBDF((uint32_t) c->qid.path);
176                         p = pcimatchtbdf(tbdf);
177                         if (p == NULL)
178                                 error(EINVAL, NULL);
179                         if (n + offset > 256)
180                                 n = 256 - offset;
181                         if (n < 0)
182                                 return 0;
183                         r = offset;
184                         if (!(r & 3) && n == 4) {
185                                 x = pcicfgr32(p, r);
186                                 PBIT32(a, x);
187                                 return 4;
188                         }
189                         if (!(r & 1) && n == 2) {
190                                 x = pcicfgr16(p, r);
191                                 PBIT16(a, x);
192                                 return 2;
193                         }
194                         for (i = 0; i < n; i++) {
195                                 x = pcicfgr8(p, r);
196                                 PBIT8(a, x);
197                                 a++;
198                                 r++;
199                         }
200                         return i;
201                 default:
202                         error(EINVAL, NULL);
203         }
204         return n;
205 }
206
207 static long pciwrite(struct chan *c, void *va, long n, int64_t offset)
208 {
209         char buf[256];
210         uint8_t *a;
211         int i, r, tbdf;
212         uint32_t x;
213         Pcidev *p;
214
215         if (n >= sizeof(buf))
216                 n = sizeof(buf) - 1;
217         a = va;
218         strncpy(buf, (char *unused_char_p_t)a, n);
219         buf[n] = 0;
220
221         switch (TYPE(c->qid)) {
222                 case Qpciraw:
223                         tbdf = MKBUS(BusPCI, 0, 0, 0) | BUSBDF((uint32_t) c->qid.path);
224                         p = pcimatchtbdf(tbdf);
225                         if (p == NULL)
226                                 error(EINVAL, NULL);
227                         if (offset > 256)
228                                 return 0;
229                         if (n + offset > 256)
230                                 n = 256 - offset;
231                         r = offset;
232                         if (!(r & 3) && n == 4) {
233                                 x = GBIT32(a);
234                                 pcicfgw32(p, r, x);
235                                 return 4;
236                         }
237                         if (!(r & 1) && n == 2) {
238                                 x = GBIT16(a);
239                                 pcicfgw16(p, r, x);
240                                 return 2;
241                         }
242                         for (i = 0; i < n; i++) {
243                                 x = GBIT8(a);
244                                 pcicfgw8(p, r, x);
245                                 a++;
246                                 r++;
247                         }
248                         return i;
249                 default:
250                         error(EINVAL, NULL);
251         }
252         return n;
253 }
254
255 struct dev pcidevtab = {
256         "pci",
257
258         devreset,
259         devinit,
260         devshutdown,
261         pciattach,
262         pciwalk,
263         pcistat,
264         pciopen,
265         devcreate,
266         pciclose,
267         pciread,
268         devbread,
269         pciwrite,
270         devbwrite,
271         devremove,
272         devwstat,
273 };