Use readstr() for #device text buffers
[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 <slab.h>
11 #include <kmalloc.h>
12 #include <kref.h>
13 #include <string.h>
14 #include <stdio.h>
15 #include <assert.h>
16 #include <error.h>
17 #include <cpio.h>
18 #include <pmap.h>
19 #include <smp.h>
20 #include <net/ip.h>
21 #include <arch/io.h>
22
23 struct dev pcidevtab;
24
25 static char *devname(void)
26 {
27         return pcidevtab.name;
28 }
29
30 enum {
31         Qtopdir = 0,
32
33         Qpcidir,
34         Qpcictl,
35         Qpciraw,
36
37         PCI_CONFIG_SZ = 256,
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.name, 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.name, 0664, dp);
64                 return 1;
65         }
66         return -1;
67 }
68
69 static int
70 pcigen(struct chan *c, char *_1, struct dirtab *_2, int _3, int s,
71        struct dir *dp)
72 {
73         int tbdf;
74         struct pci_device *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, "#%s", pcidevtab.name);
83                         devdir(c, q, get_cur_genbuf(), 0, eve.name, 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, "#%s", pcidevtab.name);
92                         devdir(c, q, get_cur_genbuf(), 0, eve.name, 0555, dp);
93                         return 1;
94                 }
95                 STAILQ_FOREACH(p, &pci_devices, all_dev) {
96                         if (s < 2)
97                                 break;
98                         s -= 2;
99                 }
100                 if (p == NULL)
101                         return -1;
102                 return pcidirgen(c, s + Qpcictl, pci_to_tbdf(p), dp);
103         case Qpcictl:
104         case Qpciraw:
105                 tbdf = MKBUS(BusPCI, 0, 0, 0) | BUSBDF((uint32_t) c->qid.path);
106                 p = pci_match_tbdf(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,
122                         unsigned int nname)
123 {
124         return devwalk(c, nc, name, nname, (struct dirtab *)0, 0, pcigen);
125 }
126
127 static size_t pcistat(struct chan *c, uint8_t *dp, size_t n)
128 {
129         return devstat(c, dp, n, (struct dirtab *)0, 0L, pcigen);
130 }
131
132 static struct chan *pciopen(struct chan *c, int omode)
133 {
134         c = devopen(c, omode, (struct dirtab *)0, 0, pcigen);
135         switch (TYPE(c->qid)) {
136         default:
137                 break;
138         }
139         return c;
140 }
141
142 static void pciclose(struct chan *_)
143 {
144 }
145
146 static size_t pciread(struct chan *c, void *va, size_t n, off64_t offset)
147 {
148         char buf[PCI_CONFIG_SZ], *ebuf, *w, *a;
149         int i, tbdf, r;
150         uint32_t x;
151         struct pci_device *p;
152
153         a = va;
154         switch (TYPE(c->qid)) {
155         case Qtopdir:
156         case Qpcidir:
157                 return devdirread(c, a, n, (struct dirtab *)0, 0L, pcigen);
158         case Qpcictl:
159                 tbdf = MKBUS(BusPCI, 0, 0, 0) | BUSBDF((uint32_t) c->qid.path);
160                 p = pci_match_tbdf(tbdf);
161                 if (p == NULL)
162                         error(EINVAL, ERROR_FIXME);
163                 ebuf = buf + sizeof(buf) - 1;   /* -1 for newline */
164                 w = seprintf(buf, ebuf, "%.2x.%.2x.%.2x %.4x/%.4x %3d",
165                              p->class, p->subclass, p->progif, p->ven_id, p->dev_id,
166                              p->irqline);
167                 for (i = 0; i < COUNT_OF(p->bar); i++) {
168                         if (p->bar[i].mmio_sz == 0)
169                                 continue;
170                         w = seprintf(w, ebuf, " %d:%.8lux %d", i, p->bar[i].pio_base,
171                                      p->bar[i].mmio_sz);
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 = pci_match_tbdf(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 = pcidev_read32(p, r);
188                         PBIT32(a, x);
189                         return 4;
190                 }
191                 if (!(r & 1) && n == 2) {
192                         x = pcidev_read16(p, r);
193                         PBIT16(a, x);
194                         return 2;
195                 }
196                 for (i = 0; i < n; i++) {
197                         x = pcidev_read8(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 size_t pciwrite(struct chan *c, void *va, size_t n, off64_t offset)
210 {
211         uint8_t *a;
212         int i, r, tbdf;
213         uint32_t x;
214         struct pci_device *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 = pci_match_tbdf(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                         pcidev_write32(p, r, x);
234                         return 4;
235                 }
236                 if (!(r & 1) && n == 2) {
237                         x = GBIT16(a);
238                         pcidev_write16(p, r, x);
239                         return 2;
240                 }
241                 for (i = 0; i < n; i++) {
242                         x = GBIT8(a);
243                         pcidev_write8(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 __devtab = {
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 };