summaryrefslogtreecommitdiff
path: root/src/pci.c
diff options
context:
space:
mode:
authorBob Gilligan <gilligan@vyatta.com>2010-02-05 09:54:11 -0800
committerBob Gilligan <gilligan@vyatta.com>2010-02-05 09:54:11 -0800
commite1971e4774a6ebb5ed33a09bdd60afa2c0534b6f (patch)
treef4fc5aaccb4d9dac43ae7a825df9006532c1a059 /src/pci.c
downloadvyatta-biosdevname-e1971e4774a6ebb5ed33a09bdd60afa2c0534b6f.tar.gz
vyatta-biosdevname-e1971e4774a6ebb5ed33a09bdd60afa2c0534b6f.zip
Initial commit.debian/0.1
Diffstat (limited to 'src/pci.c')
-rw-r--r--src/pci.c244
1 files changed, 244 insertions, 0 deletions
diff --git a/src/pci.c b/src/pci.c
new file mode 100644
index 0000000..c5ae788
--- /dev/null
+++ b/src/pci.c
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 2006 Dell, Inc.
+ * by Matt Domsch <Matt_Domsch@dell.com>
+ * Licensed under the GNU General Public license, version 2.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include "pirq.h"
+#include <pci/pci.h>
+#include "pci.h"
+
+static int
+is_parent_bridge(struct pci_dev *p, unsigned int target_bus)
+{
+ unsigned int primary, secondary;
+
+ if ( (pci_read_word(p, PCI_HEADER_TYPE) & 0x7f) != PCI_HEADER_TYPE_BRIDGE)
+ return 0;
+
+ primary=pci_read_byte(p, PCI_PRIMARY_BUS);
+ secondary=pci_read_byte(p, PCI_SECONDARY_BUS);
+
+ if (secondary != target_bus)
+ return 0;
+
+ return 1;
+}
+
+static struct pci_dev *
+find_parent(struct pci_access *pacc, unsigned int target_bus)
+{
+ struct pci_dev *p;
+
+ for (p=pacc->devices; p; p=p->next)
+ if (is_parent_bridge(p, target_bus))
+ return p;
+
+ return NULL;
+}
+
+/*
+ * Check our parent in case the device itself isn't listed
+ * in the PCI IRQ Routing Table. This has a problem, as
+ * our parent bridge on a card may not be included
+ * in the $PIR table. In that case, it falls back to "unknown".
+ */
+static int pci_dev_to_slot(struct routing_table *table, struct pci_access *pacc, struct pci_dev *p)
+{
+ int rc;
+ rc = pirq_pci_dev_to_slot(table, p->bus, p->dev);
+ if (rc == INT_MAX) {
+ p = find_parent(pacc, p->bus);
+ if (p)
+ rc = pirq_pci_dev_to_slot(table, p->bus, p->dev);
+ }
+ return rc;
+}
+
+static void add_pci_dev(struct libbiosdevname_state *state,
+ struct routing_table *table,
+ struct pci_access *pacc, struct pci_dev *p)
+{
+ struct pci_device *dev;
+ dev = malloc(sizeof(*dev));
+ if (!dev) {
+ fprintf(stderr, "out of memory\n");
+ return;
+ }
+ memset(dev, 0, sizeof(*dev));
+ INIT_LIST_HEAD(&dev->node);
+ memcpy(&dev->pci_dev, p, sizeof(*p)); /* This doesn't allow us to call PCI functions though */
+ dev->physical_slot = pci_dev_to_slot(table, pacc, p);
+ dev->class = pci_read_word(p, PCI_CLASS_DEVICE);
+ list_add(&dev->node, &state->pci_devices);
+}
+
+void free_pci_devices(struct libbiosdevname_state *state)
+{
+ struct pci_device *pos, *next;
+ list_for_each_entry_safe(pos, next, &state->pci_devices, node) {
+ list_del(&pos->node);
+ free(pos);
+ }
+}
+
+int get_pci_devices(struct libbiosdevname_state *state)
+{
+ struct pci_access *pacc;
+ struct pci_dev *p;
+ struct pci_device *dev;
+ struct routing_table *table;
+ int rc=0;
+
+ pacc = pci_alloc();
+ if (!pacc)
+ return rc;
+
+ pci_init(pacc);
+ pci_scan_bus(pacc);
+
+ table = pirq_alloc_read_table();
+ if (!table)
+ goto out;
+
+ for (p=pacc->devices; p; p=p->next) {
+ dev = find_dev_by_pci(state, p);
+ if (!dev)
+ add_pci_dev(state, table, pacc, p);
+ }
+
+ pirq_free_table(table);
+out:
+ pci_cleanup(pacc);
+ return rc;
+}
+
+
+static int parse_pci_name(const char *s, int *domain, int *bus, int *dev, int *func)
+{
+ int err;
+/* The domain part was added in 2.6 kernels. Test for that first. */
+ err = sscanf(s, "%x:%2x:%2x.%x", domain, bus, dev, func);
+ if (err != 4) {
+ err = sscanf(s, "%2x:%2x.%x", bus, dev, func);
+ if (err != 3) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int unparse_pci_name(char *buf, int size, const struct pci_dev *pdev)
+{
+ return snprintf(buf, size, "%04x:%02x:%02x.%d",
+ pci_domain_nr(pdev), pdev->bus, pdev->dev, pdev->func);
+}
+
+static int unparse_location(char *buf, const int size, const int location)
+{
+ char *s = buf;
+ if (location == 0)
+ s += snprintf(s, size-(s-buf), "embedded");
+ else if (location == INT_MAX)
+ s += snprintf(s, size-(s-buf), "unknown");
+ else if (location > 0)
+ s += snprintf(s, size-(s-buf), "%u", location);
+ else
+ s += snprintf(s, size-(s-buf), "unknown");
+ return (s-buf);
+}
+
+static int unparse_smbios_type41_type(char *buf, const int size, const int type)
+{
+ char *s = buf;
+ const char *msg[] = {"Other",
+ "Unknown",
+ "Video",
+ "SCSI Controller",
+ "Ethernet",
+ "Token Ring",
+ "Sound",
+ "PATA Controller",
+ "SATA Controller",
+ "SAS Controller",
+ };
+ if (type > 0 && type <= sizeof(msg))
+ s += snprintf(s, size-(s-buf), "%s\n", msg[type-1]);
+ else
+ s += snprintf(s, size-(s-buf), "<OUT OF SPEC>\n");
+ return (s-buf);
+}
+
+
+int unparse_pci_device(char *buf, const int size, const struct pci_device *p)
+{
+ char *s = buf;
+ s += snprintf(s, size-(s-buf), "PCI name : ");
+ s += unparse_pci_name(s, size-(s-buf), &p->pci_dev);
+ s += snprintf(s, size-(s-buf), "\n");
+ s += snprintf(s, size-(s-buf), "PCI Slot : ");
+ s += unparse_location(s, size-(s-buf), p->physical_slot);
+ s += snprintf(s, size-(s-buf), "\n");
+ if (p->smbios_type) {
+ s += snprintf(s, size-(s-buf), "SMBIOS Device Type: ");
+ s += unparse_smbios_type41_type(s, size-(s-buf), p->smbios_type);
+ s += snprintf(s, size-(s-buf), "SMBIOS Instance: %u\n", p->smbios_instance);
+ s += snprintf(s, size-(s-buf), "SMBIOS Enabled: %s\n", p->smbios_instance?"True":"False");
+ }
+ return (s-buf);
+}
+
+static int is_same_pci(const struct pci_dev *a, const struct pci_dev *b)
+{
+ if (pci_domain_nr(a) == pci_domain_nr(b) &&
+ a->bus == b->bus &&
+ a->dev == b->dev &&
+ a->func == b->func)
+ return 1;
+ return 0;
+}
+
+struct pci_device * find_dev_by_pci(const struct libbiosdevname_state *state,
+ const struct pci_dev *p)
+{
+ struct pci_device *dev;
+ list_for_each_entry(dev, &state->pci_devices, node) {
+ if (is_same_pci(p, &dev->pci_dev))
+ return dev;
+ }
+ return NULL;
+}
+
+struct pci_device * find_pci_dev_by_pci_addr(const struct libbiosdevname_state *state,
+ const int domain, const int bus, const int device, const int func)
+{
+ struct pci_device *dev;
+ struct pci_device p;
+
+#ifdef HAVE_STRUCT_PCI_DEV_DOMAIN
+ p.pci_dev.domain = domain;
+#endif
+ p.pci_dev.bus = bus;
+ p.pci_dev.dev = device;
+ p.pci_dev.func = func;
+
+ list_for_each_entry(dev, &state->pci_devices, node) {
+ if (is_same_pci(&p.pci_dev, &dev->pci_dev))
+ return dev;
+ }
+ return NULL;
+}
+
+struct pci_device * find_dev_by_pci_name(const struct libbiosdevname_state *state,
+ const char *s)
+{
+ int domain=0, bus=0, device=0, func=0;
+ if (parse_pci_name(s, &domain, &bus, &device, &func))
+ return NULL;
+
+ return find_pci_dev_by_pci_addr(state, domain, bus, device, func);
+}