|  | /* | 
|  | * This file is part of the UCB release of Plan 9. It is subject to the license | 
|  | * terms in the LICENSE file found in the top-level directory of this | 
|  | * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No | 
|  | * part of the UCB release of Plan 9, including this file, may be copied, | 
|  | * modified, propagated, or distributed except according to the terms contained | 
|  | * in the LICENSE file. | 
|  | */ | 
|  |  | 
|  | #include <slab.h> | 
|  | #include <kmalloc.h> | 
|  | #include <kref.h> | 
|  | #include <string.h> | 
|  | #include <stdio.h> | 
|  | #include <assert.h> | 
|  | #include <error.h> | 
|  | #include <cpio.h> | 
|  | #include <pmap.h> | 
|  | #include <smp.h> | 
|  | #include <net/ip.h> | 
|  | #include <arch/io.h> | 
|  |  | 
|  | struct dev pcidevtab; | 
|  |  | 
|  | static char *devname(void) | 
|  | { | 
|  | return pcidevtab.name; | 
|  | } | 
|  |  | 
|  | enum { | 
|  | Qtopdir = 0, | 
|  |  | 
|  | Qpcidir, | 
|  | Qpcictl, | 
|  | Qpciraw, | 
|  |  | 
|  | PCI_CONFIG_SZ = 256, | 
|  | }; | 
|  |  | 
|  | #define TYPE(q)		((uint32_t)(q).path & 0x0F) | 
|  | #define QID(c, t)	(((c)<<4)|(t)) | 
|  |  | 
|  | static struct dirtab topdir[] = { | 
|  | {".", {Qtopdir, 0, QTDIR}, 0, 0555}, | 
|  | {"pci", {Qpcidir, 0, QTDIR}, 0, 0555}, | 
|  | }; | 
|  |  | 
|  | static int pcidirgen(struct chan *c, int t, int tbdf, struct dir *dp) | 
|  | { | 
|  | struct qid q; | 
|  |  | 
|  | q = (struct qid) { | 
|  | BUSBDF(tbdf) | t, 0, 0}; | 
|  | switch (t) { | 
|  | case Qpcictl: | 
|  | snprintf(get_cur_genbuf(), GENBUF_SZ, "%d.%d.%dctl", | 
|  | BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf)); | 
|  | devdir(c, q, get_cur_genbuf(), 0, eve.name, 0444, dp); | 
|  | return 1; | 
|  | case Qpciraw: | 
|  | snprintf(get_cur_genbuf(), GENBUF_SZ, "%d.%d.%draw", | 
|  | BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf)); | 
|  | devdir(c, q, get_cur_genbuf(), 128, eve.name, 0664, dp); | 
|  | return 1; | 
|  | } | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | static int | 
|  | pcigen(struct chan *c, char *_1, struct dirtab *_2, int _3, int s, | 
|  | struct dir *dp) | 
|  | { | 
|  | int tbdf; | 
|  | struct pci_device *p; | 
|  | struct qid q; | 
|  |  | 
|  | switch (TYPE(c->qid)) { | 
|  | case Qtopdir: | 
|  | if (s == DEVDOTDOT) { | 
|  | q = (struct qid) { QID(0, Qtopdir), 0, QTDIR }; | 
|  | snprintf(get_cur_genbuf(), GENBUF_SZ, "#%s", | 
|  | pcidevtab.name); | 
|  | devdir(c, q, get_cur_genbuf(), 0, eve.name, 0555, dp); | 
|  | return 1; | 
|  | } | 
|  | return devgen(c, NULL, topdir, ARRAY_SIZE(topdir), s, dp); | 
|  | case Qpcidir: | 
|  | if (s == DEVDOTDOT) { | 
|  | q = (struct qid) { | 
|  | QID(0, Qtopdir), 0, QTDIR}; | 
|  | snprintf(get_cur_genbuf(), GENBUF_SZ, "#%s", | 
|  | pcidevtab.name); | 
|  | devdir(c, q, get_cur_genbuf(), 0, eve.name, 0555, dp); | 
|  | return 1; | 
|  | } | 
|  | STAILQ_FOREACH(p, &pci_devices, all_dev) { | 
|  | if (s < 2) | 
|  | break; | 
|  | s -= 2; | 
|  | } | 
|  | if (p == NULL) | 
|  | return -1; | 
|  | return pcidirgen(c, s + Qpcictl, pci_to_tbdf(p), dp); | 
|  | case Qpcictl: | 
|  | case Qpciraw: | 
|  | tbdf = MKBUS(BusPCI, 0, 0, 0) | BUSBDF((uint32_t) c->qid.path); | 
|  | p = pci_match_tbdf(tbdf); | 
|  | if (p == NULL) | 
|  | return -1; | 
|  | return pcidirgen(c, TYPE(c->qid), tbdf, dp); | 
|  | default: | 
|  | break; | 
|  | } | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | static struct chan *pciattach(char *spec) | 
|  | { | 
|  | return devattach(devname(), spec); | 
|  | } | 
|  |  | 
|  | struct walkqid *pciwalk(struct chan *c, struct chan *nc, char **name, | 
|  | unsigned int nname) | 
|  | { | 
|  | return devwalk(c, nc, name, nname, (struct dirtab *)0, 0, pcigen); | 
|  | } | 
|  |  | 
|  | static size_t pcistat(struct chan *c, uint8_t *dp, size_t n) | 
|  | { | 
|  | return devstat(c, dp, n, (struct dirtab *)0, 0L, pcigen); | 
|  | } | 
|  |  | 
|  | static struct chan *pciopen(struct chan *c, int omode) | 
|  | { | 
|  | c = devopen(c, omode, (struct dirtab *)0, 0, pcigen); | 
|  | switch (TYPE(c->qid)) { | 
|  | default: | 
|  | break; | 
|  | } | 
|  | return c; | 
|  | } | 
|  |  | 
|  | static void pciclose(struct chan *_) | 
|  | { | 
|  | } | 
|  |  | 
|  | static size_t pciread(struct chan *c, void *va, size_t n, off64_t offset) | 
|  | { | 
|  | char buf[PCI_CONFIG_SZ], *ebuf, *w, *a; | 
|  | int i, tbdf, r; | 
|  | uint32_t x; | 
|  | struct pci_device *p; | 
|  |  | 
|  | a = va; | 
|  | switch (TYPE(c->qid)) { | 
|  | case Qtopdir: | 
|  | case Qpcidir: | 
|  | return devdirread(c, a, n, (struct dirtab *)0, 0L, pcigen); | 
|  | case Qpcictl: | 
|  | tbdf = MKBUS(BusPCI, 0, 0, 0) | BUSBDF((uint32_t) c->qid.path); | 
|  | p = pci_match_tbdf(tbdf); | 
|  | if (p == NULL) | 
|  | error(EINVAL, ERROR_FIXME); | 
|  | ebuf = buf + sizeof(buf) - 1;	/* -1 for newline */ | 
|  | w = seprintf(buf, ebuf, "%.2x.%.2x.%.2x %.4x/%.4x %3d", | 
|  | p->class, p->subclass, p->progif, p->ven_id, | 
|  | p->dev_id, p->irqline); | 
|  | for (i = 0; i < COUNT_OF(p->bar); i++) { | 
|  | if (p->bar[i].mmio_sz == 0) | 
|  | continue; | 
|  | w = seprintf(w, ebuf, " %d:%.8lux %d", i, | 
|  | p->bar[i].pio_base, p->bar[i].mmio_sz); | 
|  | } | 
|  | *w++ = '\n'; | 
|  | *w = '\0'; | 
|  | return readstr(offset, a, n, buf); | 
|  | case Qpciraw: | 
|  | tbdf = MKBUS(BusPCI, 0, 0, 0) | BUSBDF((uint32_t) c->qid.path); | 
|  | p = pci_match_tbdf(tbdf); | 
|  | if (p == NULL) | 
|  | error(EINVAL, ERROR_FIXME); | 
|  | if (n + offset > 256) | 
|  | n = 256 - offset; | 
|  | if (n < 0) | 
|  | return 0; | 
|  | r = offset; | 
|  | if (!(r & 3) && n == 4) { | 
|  | x = pcidev_read32(p, r); | 
|  | PBIT32(a, x); | 
|  | return 4; | 
|  | } | 
|  | if (!(r & 1) && n == 2) { | 
|  | x = pcidev_read16(p, r); | 
|  | PBIT16(a, x); | 
|  | return 2; | 
|  | } | 
|  | for (i = 0; i < n; i++) { | 
|  | x = pcidev_read8(p, r); | 
|  | PBIT8(a, x); | 
|  | a++; | 
|  | r++; | 
|  | } | 
|  | return i; | 
|  | default: | 
|  | error(EINVAL, ERROR_FIXME); | 
|  | } | 
|  | return n; | 
|  | } | 
|  |  | 
|  | static size_t pciwrite(struct chan *c, void *va, size_t n, off64_t offset) | 
|  | { | 
|  | uint8_t *a; | 
|  | int i, r, tbdf; | 
|  | uint32_t x; | 
|  | struct pci_device *p; | 
|  |  | 
|  | if (n > PCI_CONFIG_SZ) | 
|  | n = PCI_CONFIG_SZ; | 
|  | a = va; | 
|  |  | 
|  | switch (TYPE(c->qid)) { | 
|  | case Qpciraw: | 
|  | tbdf = MKBUS(BusPCI, 0, 0, 0) | BUSBDF((uint32_t) c->qid.path); | 
|  | p = pci_match_tbdf(tbdf); | 
|  | if (p == NULL) | 
|  | error(EINVAL, ERROR_FIXME); | 
|  | if (offset > PCI_CONFIG_SZ) | 
|  | return 0; | 
|  | if (n + offset > PCI_CONFIG_SZ) | 
|  | n = PCI_CONFIG_SZ - offset; | 
|  | r = offset; | 
|  | if (!(r & 3) && n == 4) { | 
|  | x = GBIT32(a); | 
|  | pcidev_write32(p, r, x); | 
|  | return 4; | 
|  | } | 
|  | if (!(r & 1) && n == 2) { | 
|  | x = GBIT16(a); | 
|  | pcidev_write16(p, r, x); | 
|  | return 2; | 
|  | } | 
|  | for (i = 0; i < n; i++) { | 
|  | x = GBIT8(a); | 
|  | pcidev_write8(p, r, x); | 
|  | a++; | 
|  | r++; | 
|  | } | 
|  | return i; | 
|  | default: | 
|  | error(EINVAL, ERROR_FIXME); | 
|  | } | 
|  | return n; | 
|  | } | 
|  |  | 
|  | struct dev pcidevtab __devtab = { | 
|  | .name = "pci", | 
|  |  | 
|  | .reset = devreset, | 
|  | .init = devinit, | 
|  | .shutdown = devshutdown, | 
|  | .attach = pciattach, | 
|  | .walk = pciwalk, | 
|  | .stat = pcistat, | 
|  | .open = pciopen, | 
|  | .create = devcreate, | 
|  | .close = pciclose, | 
|  | .read = pciread, | 
|  | .bread = devbread, | 
|  | .write = pciwrite, | 
|  | .bwrite = devbwrite, | 
|  | .remove = devremove, | 
|  | .wstat = devwstat, | 
|  | }; |