+ if (a->fd >= 0)
+ {
+ close(a->fd);
+ a->fd = -1;
+ }
+ if (a->fd_rw >= 0)
+ {
+ close(a->fd_rw);
+ a->fd_rw = -1;
+ }
+}
+
+static void
+fbsd_scan(struct pci_access *a)
+{
+ struct pci_conf_io conf;
+ struct pci_conf *matches;
+ struct pci_dev *t;
+ uint32_t offset = 0;
+ unsigned int i;
+
+ matches = calloc(32, sizeof(struct pci_conf));
+ if (matches == NULL)
+ {
+ a->error("calloc: %s", strerror(errno));
+ return;
+ }
+
+ conf.generation = 0;
+ do
+ {
+ conf.pat_buf_len = 0;
+ conf.num_patterns = 0;
+ conf.patterns = NULL;
+ conf.match_buf_len = 32 * sizeof(struct pci_conf);
+ conf.num_matches = 32;
+ conf.matches = matches;
+ conf.offset = offset;
+ conf.status = 0;
+ if (ioctl(a->fd_rw >= 0 ? a->fd_rw : a->fd, PCIOCGETCONF, &conf) < 0)
+ {
+ if (errno == ENODEV)
+ break;
+ a->error("fbsd_scan: ioctl(PCIOCGETCONF) failed: %s",
+ strerror(errno));
+ }
+ /* PCI_GETCONF_LIST_CHANGED would require us to start over. */
+ if (conf.status == PCI_GETCONF_ERROR ||
+ conf.status == PCI_GETCONF_LIST_CHANGED)
+ {
+ a->error("fbsd_scan: ioctl(PCIOCGETCONF) failed");
+ break;
+ }
+ for (i = 0; i < conf.num_matches; i++)
+ {
+ t = pci_alloc_dev(a);
+ t->bus = matches[i].pc_sel.pc_bus;
+ t->dev = matches[i].pc_sel.pc_dev;
+ t->dev = matches[i].pc_sel.pc_dev;
+ t->domain = matches[i].pc_sel.pc_domain;
+ t->domain_16 = matches[i].pc_sel.pc_domain;
+ t->vendor_id = matches[i].pc_vendor;
+ t->device_id = matches[i].pc_device;
+ t->known_fields = PCI_FILL_IDENT;
+ t->hdrtype = matches[i].pc_hdr;
+ pci_link_dev(a, t);
+ }
+ offset += conf.num_matches;
+ }
+ while (conf.status == PCI_GETCONF_MORE_DEVS);
+
+ free(matches);
+}
+
+static int
+fbsd_fill_info(struct pci_dev *d, int flags)
+{
+ struct pci_conf_io conf;
+ struct pci_bar_io bar;
+ struct pci_match_conf pattern;
+ struct pci_conf match;
+ int i;
+
+ if (d->access->fd_rw >= 0)
+ return pci_generic_fill_info(d, flags);
+
+ /*
+ * Can only handle PCI_FILL_IDENT, PCI_FILL_CLASS, PCI_FILL_BASES and
+ * PCI_FILL_SIZES requests with the PCIOCGETCONF and PCIOCGETBAR IOCTLs.
+ */
+
+ conf.pat_buf_len = sizeof(struct pci_match_conf);
+ conf.num_patterns = 1;
+ conf.patterns = &pattern;
+ conf.match_buf_len = sizeof(struct pci_conf);
+ conf.num_matches = 1;
+ conf.matches = &match;
+ conf.offset = 0;
+ conf.generation = 0;
+ conf.status = 0;
+
+ pattern.pc_sel.pc_domain = d->domain;
+ pattern.pc_sel.pc_bus = d->bus;
+ pattern.pc_sel.pc_dev = d->dev;
+ pattern.pc_sel.pc_func = d->func;
+ pattern.flags = PCI_GETCONF_MATCH_DOMAIN | PCI_GETCONF_MATCH_BUS |
+ PCI_GETCONF_MATCH_DEV | PCI_GETCONF_MATCH_FUNC;
+
+ if (ioctl(d->access->fd, PCIOCGETCONF, &conf) < 0)
+ {
+ if (errno == ENODEV)
+ return 0;
+ d->access->error("fbsd_fill_info: ioctl(PCIOCGETCONF) failed: %s", strerror(errno));
+ }
+
+ if (flags & PCI_FILL_IDENT)
+ {
+ d->vendor_id = match.pc_vendor;
+ d->device_id = match.pc_device;
+ }
+ if (flags & PCI_FILL_CLASS)
+ {
+ d->device_class = match.pc_class | (match.pc_subclass << 8);
+ }
+ if (flags & (PCI_FILL_BASES | PCI_FILL_SIZES))
+ {
+ d->rom_base_addr = 0;
+ d->rom_size = 0;
+ for (i = 0; i < 6; i++)
+ {
+ bar.pbi_sel.pc_domain = d->domain;
+ bar.pbi_sel.pc_bus = d->bus;
+ bar.pbi_sel.pc_dev = d->dev;
+ bar.pbi_sel.pc_func = d->func;
+ bar.pbi_reg = 0x10 + 4*i;
+ bar.pbi_enabled = 0;
+ bar.pbi_base = 0;
+ bar.pbi_length = 0;
+ if (ioctl(d->access->fd, PCIOCGETBAR, &bar) < 0)
+ {
+ if (errno == ENODEV)
+ return 0;
+ if (errno == EINVAL)
+ {
+ d->base_addr[i] = 0;
+ d->size[i] = 0;
+ }
+ else
+ d->access->error("fbsd_fill_info: ioctl(PCIOCGETBAR) failed: %s", strerror(errno));
+ }
+ else
+ {
+ d->base_addr[i] = bar.pbi_base;
+ d->size[i] = bar.pbi_length;
+ }
+ }
+ }
+
+ return flags & (PCI_FILL_IDENT | PCI_FILL_CLASS | PCI_FILL_BASES |
+ PCI_FILL_SIZES);