Index: .gitignore
===================================================================
--- .gitignore	(revision ca645a25041e245f85f0c75c5695e8546fb409f1)
+++ .gitignore	(revision 7acd787ef6db58d28e222cd14600149ac8c9ff72)
@@ -130,4 +130,5 @@
 uspace/app/nic/nic
 uspace/app/nterm/nterm
+uspace/app/pci/pci
 uspace/app/perf/perf
 uspace/app/ping/ping
Index: abi/include/abi/ipc/interfaces.h
===================================================================
--- abi/include/abi/ipc/interfaces.h	(revision ca645a25041e245f85f0c75c5695e8546fb409f1)
+++ abi/include/abi/ipc/interfaces.h	(revision 7acd787ef6db58d28e222cd14600149ac8c9ff72)
@@ -183,5 +183,7 @@
 	    FOURCC_COMPACT('v', 'b', 'd', ' ') | IFACE_EXCHANGE_SERIALIZE,
 	INTERFACE_IPC_TEST =
-	    FOURCC_COMPACT('i', 'p', 'c', 't') | IFACE_EXCHANGE_SERIALIZE
+	    FOURCC_COMPACT('i', 'p', 'c', 't') | IFACE_EXCHANGE_SERIALIZE,
+	INTERFACE_PCI =
+	    FOURCC_COMPACT('p', 'c', 'i', ' ') | IFACE_EXCHANGE_SERIALIZE
 } iface_t;
 
Index: boot/Makefile.common
===================================================================
--- boot/Makefile.common	(revision ca645a25041e245f85f0c75c5695e8546fb409f1)
+++ boot/Makefile.common	(revision 7acd787ef6db58d28e222cd14600149ac8c9ff72)
@@ -206,4 +206,5 @@
 	netecho \
 	nterm \
+	pci \
 	ping \
 	pkg \
Index: uspace/Makefile
===================================================================
--- uspace/Makefile	(revision ca645a25041e245f85f0c75c5695e8546fb409f1)
+++ uspace/Makefile	(revision 7acd787ef6db58d28e222cd14600149ac8c9ff72)
@@ -67,4 +67,5 @@
 	app/netecho \
 	app/nterm \
+	app/pci \
 	app/perf \
 	app/redir \
Index: uspace/app/pci/Makefile
===================================================================
--- uspace/app/pci/Makefile	(revision 7acd787ef6db58d28e222cd14600149ac8c9ff72)
+++ uspace/app/pci/Makefile	(revision 7acd787ef6db58d28e222cd14600149ac8c9ff72)
@@ -0,0 +1,36 @@
+#
+# Copyright (c) 2019 Jiri Svoboda
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+USPACE_PREFIX = ../..
+LIBS = ext4 fs block crypto
+BINARY = pci
+
+SOURCES = \
+	pci.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/pci/doc/doxygroups.h
===================================================================
--- uspace/app/pci/doc/doxygroups.h	(revision 7acd787ef6db58d28e222cd14600149ac8c9ff72)
+++ uspace/app/pci/doc/doxygroups.h	(revision 7acd787ef6db58d28e222cd14600149ac8c9ff72)
@@ -0,0 +1,4 @@
+/** @addtogroup pci pci
+ * @brief Tool for listing PCI devices
+ * @ingroup apps
+ */
Index: uspace/app/pci/pci.c
===================================================================
--- uspace/app/pci/pci.c	(revision 7acd787ef6db58d28e222cd14600149ac8c9ff72)
+++ uspace/app/pci/pci.c	(revision 7acd787ef6db58d28e222cd14600149ac8c9ff72)
@@ -0,0 +1,271 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup pci
+ * @{
+ */
+
+/**
+ * @file
+ * @brief Tool for listing PCI devices.
+ */
+
+#include <devman.h>
+#include <errno.h>
+#include <io/table.h>
+#include <loc.h>
+#include <pci.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <str.h>
+
+#define NAME	"mkext4"
+
+#define MAX_NAME_LENGTH 1024
+
+static void syntax_print(void);
+static errno_t pci_list(void);
+static errno_t pci_list_bridge(const char *);
+static errno_t pci_list_bridge_id(service_id_t);
+
+int main(int argc, char **argv)
+{
+	errno_t rc;
+	const char *bridge = NULL;
+
+	--argc;
+	++argv;
+
+	while (argc > 0 && argv[0][0] == '-') {
+		if (str_cmp(argv[0], "--bridge") == 0) {
+			--argc;
+			++argv;
+			if (argc < 1) {
+				printf("Option argument missing.\n");
+				return 1;
+			}
+
+			bridge = argv[0];
+			--argc;
+			++argv;
+		} else {
+			syntax_print();
+			return 1;
+		}
+	}
+
+	if (argc != 0) {
+		syntax_print();
+		return 1;
+	}
+
+	if (bridge != NULL)
+		rc = pci_list_bridge(bridge);
+	else
+		rc = pci_list();
+
+	if (rc != EOK)
+		return 1;
+
+	return 0;
+}
+
+static void syntax_print(void)
+{
+	printf("syntax: pci [<options>]\n");
+	printf("options:\n"
+	    "\t--bridge <svc-name> Only devices under host bridge <svc-name>\n");
+}
+
+/** List PCI devices. */
+static errno_t pci_list(void)
+{
+	errno_t rc;
+	category_id_t pci_cat_id;
+	service_id_t *svc_ids = NULL;
+	size_t svc_cnt;
+	size_t i;
+
+	rc = loc_category_get_id("pci", &pci_cat_id, 0);
+	if (rc != EOK) {
+		printf("Error getting 'pci' category ID.\n");
+		goto error;
+	}
+
+	rc = loc_category_get_svcs(pci_cat_id, &svc_ids, &svc_cnt);
+	if (rc != EOK) {
+		printf("Error getting list of PCI services.\n");
+		goto error;
+	}
+
+	for (i = 0; i < svc_cnt; i++) {
+		if (i > 0)
+			putchar('\n');
+
+		rc = pci_list_bridge_id(svc_ids[i]);
+		if (rc != EOK)
+			goto error;
+	}
+
+	free(svc_ids);
+	return EOK;
+error:
+	if (svc_ids != NULL)
+		free(svc_ids);
+	return rc;
+}
+
+/** List PCI devices under a host bridge specified by name.
+ *
+ * @param svc_name PCI service name
+ * @return EOK on success or an error code
+ */
+static errno_t pci_list_bridge(const char *svc_name)
+{
+	errno_t rc;
+	service_id_t svc_id;
+
+	rc = loc_service_get_id(svc_name, &svc_id, 0);
+	if (rc != EOK) {
+		printf("Error looking up host bridge '%s'.\n", svc_name);
+		return rc;
+	}
+
+	return pci_list_bridge_id(svc_id);
+}
+
+/** List PCI devices under a host bridge specified by ID.
+ *
+ * @param svc_id PCI service ID
+ * @return EOK on success or an error code
+ */
+static errno_t pci_list_bridge_id(service_id_t svc_id)
+{
+	errno_t rc;
+	devman_handle_t *dev_ids = NULL;
+	size_t dev_cnt;
+	pci_dev_info_t dev_info;
+	size_t i;
+	pci_t *pci = NULL;
+	char *svc_name = NULL;
+	char *drv_name = NULL;
+	table_t *table = NULL;
+
+	drv_name = malloc(MAX_NAME_LENGTH);
+	if (drv_name == NULL) {
+		printf("Out of memory.\n");
+		rc = ENOMEM;
+		goto error;
+	}
+
+	rc = table_create(&table);
+	if (rc != EOK) {
+		printf("Out of memory.\n");
+		rc = ENOMEM;
+		goto error;
+	}
+
+	table_header_row(table);
+	table_printf(table, "Address\t" "Type\t" "Driver\n");
+
+	rc = loc_service_get_name(svc_id, &svc_name);
+	if (rc != EOK) {
+		printf("Error getting service name.\n");
+		goto error;
+	}
+
+	rc = pci_open(svc_id, &pci);
+	if (rc != EOK) {
+		printf("Error opening PCI service '%s'.\n", svc_name);
+		goto error;
+	}
+
+	rc = pci_get_devices(pci, &dev_ids, &dev_cnt);
+	if (rc != EOK) {
+		printf("Error getting PCI device list.\n");
+		goto error;
+	}
+
+	for (i = 0; i < dev_cnt; i++) {
+		rc = pci_dev_get_info(pci, dev_ids[i], &dev_info);
+		if (rc != EOK) {
+			printf("Error getting PCI device info.\n");
+			goto error;
+		}
+
+		rc = devman_fun_get_driver_name(dev_info.dev_handle,
+		    drv_name, MAX_NAME_LENGTH);
+		if (rc != EOK && rc != EINVAL) {
+			printf("Error getting driver name.\n");
+			goto error;
+		}
+
+		if (rc == EINVAL)
+			drv_name[0] = '\0';
+
+		table_printf(table, "%02x.%02x.%x\t" "%04x:%04x\t"
+		    "%s\n", dev_info.bus_num, dev_info.dev_num,
+		    dev_info.fn_num, dev_info.vendor_id,
+		    dev_info.device_id, drv_name);
+	}
+
+	printf("Device listing for host bridge %s:\n\n", svc_name);
+	rc = table_print_out(table, stdout);
+	if (rc != EOK) {
+		printf("Error printing table.\n");
+		goto error;
+	}
+
+	table_destroy(table);
+	table = NULL;
+	free(dev_ids);
+	dev_ids = NULL;
+	pci_close(pci);
+	pci = NULL;
+	free(svc_name);
+	svc_name = NULL;
+
+	free(drv_name);
+	return EOK;
+error:
+	if (drv_name != NULL)
+		free(drv_name);
+	if (table != NULL)
+		table_destroy(table);
+	if (pci != NULL)
+		pci_close(pci);
+	if (svc_name != NULL)
+		free(svc_name);
+	if (dev_ids != NULL)
+		free(dev_ids);
+	return rc;
+}
+
+/**
+ * @}
+ */
Index: uspace/drv/bus/pci/pciintel/Makefile
===================================================================
--- uspace/drv/bus/pci/pciintel/Makefile	(revision ca645a25041e245f85f0c75c5695e8546fb409f1)
+++ uspace/drv/bus/pci/pciintel/Makefile	(revision 7acd787ef6db58d28e222cd14600149ac8c9ff72)
@@ -32,4 +32,5 @@
 
 SOURCES = \
+	ctl.c \
 	pci.c
 
Index: uspace/drv/bus/pci/pciintel/ctl.c
===================================================================
--- uspace/drv/bus/pci/pciintel/ctl.c	(revision 7acd787ef6db58d28e222cd14600149ac8c9ff72)
+++ uspace/drv/bus/pci/pciintel/ctl.c	(revision 7acd787ef6db58d28e222cd14600149ac8c9ff72)
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @addtogroup pciintel
+ * @{
+ */
+
+/** @file
+ */
+
+#include <async.h>
+#include <ddf/log.h>
+#include <ipc/pci.h>
+#include <macros.h>
+#include <types/pci.h>
+#include "ctl.h"
+#include "pci.h"
+
+static void pci_ctl_get_devices_srv(pci_bus_t *, ipc_call_t *);
+static void pci_ctl_dev_get_info_srv(pci_bus_t *, ipc_call_t *);
+static errno_t pci_ctl_get_devices(pci_bus_t *, devman_handle_t *, size_t,
+    size_t *);
+static errno_t pci_ctl_dev_get_info(pci_fun_t *, pci_dev_info_t *);
+
+/** Handle control service connection */
+void pci_ctl_connection(ipc_call_t *icall, void *arg)
+{
+	ipc_call_t call;
+	pci_bus_t *bus;
+
+	/*
+	 * Answer the first IPC_M_CONNECT_ME_TO call.
+	 */
+	async_accept_0(icall);
+
+	bus = pci_bus(ddf_fun_get_dev((ddf_fun_t *) arg));
+
+	while (true) {
+		async_get_call(&call);
+
+		if (!IPC_GET_IMETHOD(call))
+			break;
+
+		switch (IPC_GET_IMETHOD(call)) {
+		case PCI_GET_DEVICES:
+			pci_ctl_get_devices_srv(bus, &call);
+			break;
+		case PCI_DEV_GET_INFO:
+			pci_ctl_dev_get_info_srv(bus, &call);
+			break;
+		default:
+			async_answer_0(&call, EINVAL);
+			break;
+		}
+	}
+}
+
+/** Handle request to get list of PCI devices.
+ *
+ * @param bus PCI bus
+ * @param call Async request
+ */
+static void pci_ctl_get_devices_srv(pci_bus_t *bus, ipc_call_t *icall)
+{
+	ipc_call_t call;
+	size_t size;
+	size_t act_size;
+	devman_handle_t *buf;
+	errno_t rc;
+
+	if (!async_data_read_receive(&call, &size)) {
+		async_answer_0(&call, EREFUSED);
+		async_answer_0(icall, EREFUSED);
+		return;
+	}
+
+	if ((size % sizeof(devman_handle_t)) != 0) {
+		async_answer_0(&call, EINVAL);
+		async_answer_0(icall, EINVAL);
+		return;
+	}
+
+	buf = malloc(max(1, size));
+	if (buf == NULL) {
+		async_answer_0(&call, ENOMEM);
+		async_answer_0(icall, ENOMEM);
+		return;
+	}
+
+	rc = pci_ctl_get_devices(bus, buf, size, &act_size);
+	if (rc != EOK) {
+		free(buf);
+		async_answer_0(&call, rc);
+		async_answer_0(icall, rc);
+		return;
+	}
+
+	errno_t retval = async_data_read_finalize(&call, buf, size);
+
+	free(buf);
+	async_answer_1(icall, retval, act_size);
+}
+
+/** Handle request to get PCI device information.
+ *
+ * @param bus PCI bus
+ * @param call Async request
+ */
+static void pci_ctl_dev_get_info_srv(pci_bus_t *bus, ipc_call_t *icall)
+{
+	devman_handle_t dev_handle;
+	pci_fun_t *fun;
+	pci_dev_info_t info;
+	errno_t rc;
+
+	dev_handle = IPC_GET_ARG1(*icall);
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "pci_dev_get_info_srv(%zu)",
+	    dev_handle);
+	fun = pci_fun_first(bus);
+	while (fun != NULL) {
+		if (ddf_fun_get_handle(fun->fnode) == dev_handle)
+			break;
+
+		fun = pci_fun_next(fun);
+	}
+
+	if (fun == NULL) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "pci_dev_get_info_srv: "
+		    "device %zu not found", dev_handle);
+		async_answer_0(icall, ENOENT);
+		return;
+	}
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "pci_dev_get_info_srv: "
+	    "vol_volume_get_info");
+	rc = pci_ctl_dev_get_info(fun, &info);
+	if (rc != EOK) {
+		async_answer_0(icall, EIO);
+		return;
+	}
+
+	ipc_call_t call;
+	size_t size;
+	if (!async_data_read_receive(&call, &size)) {
+		async_answer_0(&call, EREFUSED);
+		async_answer_0(icall, EREFUSED);
+		return;
+	}
+
+	if (size != sizeof(pci_dev_info_t)) {
+		async_answer_0(&call, EINVAL);
+		async_answer_0(icall, EINVAL);
+		return;
+	}
+
+	rc = async_data_read_finalize(&call, &info,
+	    min(size, sizeof(info)));
+	if (rc != EOK) {
+		async_answer_0(&call, rc);
+		async_answer_0(icall, rc);
+		return;
+	}
+
+	async_answer_0(icall, EOK);
+}
+
+/** Get list of PCI devices.
+ *
+ * @param bus PCI bus
+ * @param call Async request
+ */
+static errno_t pci_ctl_get_devices(pci_bus_t *bus, devman_handle_t *id_buf,
+    size_t size, size_t *act_size)
+{
+	pci_fun_t *fun;
+	size_t cnt;
+
+	ddf_msg(LVL_NOTE, "pci_ctl_get_devices(%p, %p, %zu, %p)\n",
+	    bus, id_buf, size, act_size);
+
+	fun = pci_fun_first(bus);
+	cnt = 0;
+	while (fun != NULL) {
+		if (sizeof(devman_handle_t) * cnt < size)
+			id_buf[cnt] = ddf_fun_get_handle(fun->fnode);
+		++cnt;
+		fun = pci_fun_next(fun);
+	}
+
+	*act_size = sizeof(devman_handle_t) * cnt;
+	return EOK;
+}
+
+/** Get PCI device information.
+ *
+ * @param fun PCI function
+ * @param info Place to store information
+ *
+ * @return EOK on success or an error code
+ */
+static errno_t pci_ctl_dev_get_info(pci_fun_t *fun, pci_dev_info_t *info)
+{
+	info->dev_handle = ddf_fun_get_handle(fun->fnode);
+	info->bus_num = fun->bus;
+	info->dev_num = fun->dev;
+	info->fn_num = fun->fn;
+	info->vendor_id = fun->vendor_id;
+	info->device_id = fun->device_id;
+	return EOK;
+}
+
+/**
+ * @}
+ */
Index: uspace/drv/bus/pci/pciintel/ctl.h
===================================================================
--- uspace/drv/bus/pci/pciintel/ctl.h	(revision 7acd787ef6db58d28e222cd14600149ac8c9ff72)
+++ uspace/drv/bus/pci/pciintel/ctl.h	(revision 7acd787ef6db58d28e222cd14600149ac8c9ff72)
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup pciintel
+ * @{
+ */
+/** @file
+ */
+
+#ifndef CTL_H_
+#define CTL_H_
+
+#include <async.h>
+
+extern void pci_ctl_connection(ipc_call_t *icall, void *arg);
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/drv/bus/pci/pciintel/pci.c
===================================================================
--- uspace/drv/bus/pci/pciintel/pci.c	(revision ca645a25041e245f85f0c75c5695e8546fb409f1)
+++ uspace/drv/bus/pci/pciintel/pci.c	(revision 7acd787ef6db58d28e222cd14600149ac8c9ff72)
@@ -1,5 +1,5 @@
 /*
+ * Copyright (c) 2019 Jiri Svoboda
  * Copyright (c) 2010 Lenka Trochtova
- * Copyright (c) 2018 Jiri Svoboda
  * All rights reserved.
  *
@@ -58,5 +58,7 @@
 #include <pci_dev_iface.h>
 
+#include "ctl.h"
 #include "pci.h"
+#include "pci_regs.h"
 
 #define NAME "pciintel"
@@ -73,10 +75,8 @@
 
 /** Obtain PCI bus soft-state from DDF device node */
-#if 0
-static pci_bus_t *pci_bus(ddf_dev_t *dnode)
+pci_bus_t *pci_bus(ddf_dev_t *dnode)
 {
 	return ddf_dev_data_get(dnode);
 }
-#endif
 
 /** Obtain PCI bus soft-state from function soft-state */
@@ -448,4 +448,36 @@
 
 	/* TODO add subsys ids, but those exist only in header type 0 */
+}
+
+/** Get first PCI function.
+ *
+ * @param bus PCI bus
+ * @return First PCI function on @a bus or @c NULL if there is none
+ */
+pci_fun_t *pci_fun_first(pci_bus_t *bus)
+{
+	link_t *link;
+
+	link = list_first(&bus->funs);
+	if (link == NULL)
+		return NULL;
+
+	return list_get_instance(link, pci_fun_t, lfuns);
+}
+
+/** Get next PCI function.
+ *
+ * @param cur Current function
+ * @return Next PCI function on the same bus or @c NULL if there is none
+ */
+pci_fun_t *pci_fun_next(pci_fun_t *cur)
+{
+	link_t *link;
+
+	link = list_next(&cur->lfuns, &cur->busptr->funs);
+	if (link == NULL)
+		return NULL;
+
+	return list_get_instance(link, pci_fun_t, lfuns);
 }
 
@@ -691,4 +723,6 @@
 				continue;
 			}
+
+			list_append(&fun->lfuns, &bus->funs);
 		}
 	}
@@ -718,4 +752,6 @@
 		goto fail;
 	}
+
+	list_initialize(&bus->funs);
 	fibril_mutex_initialize(&bus->conf_mutex);
 
@@ -802,4 +838,6 @@
 	}
 
+	ddf_fun_set_conn_handler(ctl, pci_ctl_connection);
+
 	/* Enumerate functions. */
 	ddf_msg(LVL_DEBUG, "Enumerating the bus");
@@ -816,4 +854,11 @@
 	}
 
+	rc = ddf_fun_add_to_category(ctl, "pci");
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Failed adding control function to category "
+		    "'pci'.");
+		goto fail;
+	}
+
 	hw_res_clean_resource_list(&hw_resources);
 
Index: uspace/drv/bus/pci/pciintel/pci.h
===================================================================
--- uspace/drv/bus/pci/pciintel/pci.h	(revision ca645a25041e245f85f0c75c5695e8546fb409f1)
+++ uspace/drv/bus/pci/pciintel/pci.h	(revision 7acd787ef6db58d28e222cd14600149ac8c9ff72)
@@ -37,6 +37,8 @@
 #define PCI_H_
 
+#include <adt/list.h>
+#include <ddi.h>
 #include <ddf/driver.h>
-#include "pci_regs.h"
+#include <fibril_synch.h>
 
 #define PCI_MAX_HW_RES 10
@@ -50,4 +52,6 @@
 	pio_window_t pio_win;
 	fibril_mutex_t conf_mutex;
+	/** List of functions (of pci_fun_t) */
+	list_t funs;
 } pci_bus_t;
 
@@ -55,4 +59,6 @@
 	pci_bus_t *busptr;
 	ddf_fun_t *fnode;
+	/** Link to @c busptr->funs */
+	link_t lfuns;
 
 	int bus;
@@ -71,5 +77,9 @@
 } pci_fun_t;
 
+extern pci_bus_t *pci_bus(ddf_dev_t *);
+
 extern void pci_fun_create_match_ids(pci_fun_t *);
+extern pci_fun_t *pci_fun_first(pci_bus_t *);
+extern pci_fun_t *pci_fun_next(pci_fun_t *);
 
 extern uint8_t pci_conf_read_8(pci_fun_t *, int);
Index: uspace/lib/c/Makefile
===================================================================
--- uspace/lib/c/Makefile	(revision ca645a25041e245f85f0c75c5695e8546fb409f1)
+++ uspace/lib/c/Makefile	(revision 7acd787ef6db58d28e222cd14600149ac8c9ff72)
@@ -165,4 +165,5 @@
 	generic/assert.c \
 	generic/bsearch.c \
+	generic/pci.c \
 	generic/pio_trace.c \
 	generic/qsort.c \
Index: uspace/lib/c/generic/pci.c
===================================================================
--- uspace/lib/c/generic/pci.c	(revision 7acd787ef6db58d28e222cd14600149ac8c9ff72)
+++ uspace/lib/c/generic/pci.c	(revision 7acd787ef6db58d28e222cd14600149ac8c9ff72)
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/** @file PCI control service API
+ */
+
+#include <abi/ipc/interfaces.h>
+#include <errno.h>
+#include <ipc/devman.h>
+#include <ipc/services.h>
+#include <ipc/pci.h>
+#include <loc.h>
+#include <pci.h>
+#include <stdlib.h>
+#include <types/pci.h>
+
+/** Open PCI service.
+ *
+ * @param svc_id PCI sevice ID
+ * @param rpci   Place to store pointer to PCI service object.
+ *
+ * @return EOK on success, ENOMEM if out of memory, EIO if service
+ *         cannot be contacted.
+ */
+errno_t pci_open(service_id_t svc_id, pci_t **rpci)
+{
+	pci_t *pci;
+	errno_t rc;
+
+	pci = calloc(1, sizeof(pci_t));
+	if (pci == NULL) {
+		rc = ENOMEM;
+		goto error;
+	}
+
+	pci->sess = loc_service_connect(svc_id, INTERFACE_PCI, 0);
+	if (pci->sess == NULL) {
+		rc = EIO;
+		goto error;
+	}
+
+	*rpci = pci;
+	return EOK;
+error:
+	free(pci);
+	return rc;
+}
+
+/** Close PCI service.
+ *
+ * @param pci PCI service object or @c NULL
+ */
+void pci_close(pci_t *pci)
+{
+	if (pci == NULL)
+		return;
+
+	async_hangup(pci->sess);
+	free(pci);
+}
+
+/** Get list of IDs into a buffer of fixed size.
+ *
+ * @param pci      PCI service
+ * @param method   IPC method
+ * @param arg1     First argument
+ * @param id_buf   Buffer to store IDs
+ * @param buf_size Buffer size
+ * @param act_size Place to store actual size of complete data.
+ *
+ * @return EOK on success or an error code.
+ */
+static errno_t pci_get_ids_once(pci_t *pci, sysarg_t method, sysarg_t arg1,
+    sysarg_t *id_buf, size_t buf_size, size_t *act_size)
+{
+	async_exch_t *exch = async_exchange_begin(pci->sess);
+
+	ipc_call_t answer;
+	aid_t req = async_send_1(exch, method, arg1, &answer);
+	errno_t rc = async_data_read_start(exch, id_buf, buf_size);
+
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+
+	if (retval != EOK) {
+		return retval;
+	}
+
+	*act_size = IPC_GET_ARG1(answer);
+	return EOK;
+}
+
+/** Get list of IDs.
+ *
+ * Returns an allocated array of service IDs.
+ *
+ * @param pci    PCI service
+ * @param method IPC method
+ * @param arg1   IPC argument 1
+ * @param data   Place to store pointer to array of IDs
+ * @param count  Place to store number of IDs
+ * @return       EOK on success or an error code
+ */
+static errno_t pci_get_ids_internal(pci_t *pci, sysarg_t method, sysarg_t arg1,
+    sysarg_t **data, size_t *count)
+{
+	*data = NULL;
+	*count = 0;
+
+	size_t act_size = 0;
+	errno_t rc = pci_get_ids_once(pci, method, arg1, NULL, 0, &act_size);
+	if (rc != EOK)
+		return rc;
+
+	size_t alloc_size = act_size;
+	service_id_t *ids = malloc(alloc_size);
+	if (ids == NULL)
+		return ENOMEM;
+
+	while (true) {
+		rc = pci_get_ids_once(pci, method, arg1, ids, alloc_size,
+		    &act_size);
+		if (rc != EOK)
+			return rc;
+
+		if (act_size <= alloc_size)
+			break;
+
+		alloc_size = act_size;
+		ids = realloc(ids, alloc_size);
+		if (ids == NULL)
+			return ENOMEM;
+	}
+
+	*count = act_size / sizeof(service_id_t);
+	*data = ids;
+	return EOK;
+}
+
+/** Get list of PCI devices as array of devman handles.
+ *
+ * @param pci PCI service
+ * @param data Place to store pointer to array
+ * @param count Place to store length of array (number of entries)
+ *
+ * @return EOK on success or an error code
+ */
+errno_t pci_get_devices(pci_t *pci, devman_handle_t **data, size_t *count)
+{
+	return pci_get_ids_internal(pci, PCI_GET_DEVICES, 0, data, count);
+}
+
+extern errno_t pci_dev_get_info(pci_t *, devman_handle_t, pci_dev_info_t *);
+
+/** Get PCI device information.
+ *
+ * @param pci PCI service
+ * @param dev_handle Device handle
+ * @param info Place to store device information
+ * @return EOK on success or an error code
+ */
+errno_t pci_dev_get_info(pci_t *pci, devman_handle_t dev_handle,
+    pci_dev_info_t *info)
+{
+	async_exch_t *exch;
+	errno_t retval;
+	ipc_call_t answer;
+
+	exch = async_exchange_begin(pci->sess);
+	aid_t req = async_send_1(exch, PCI_DEV_GET_INFO, dev_handle, &answer);
+
+	errno_t rc = async_data_read_start(exch, info, sizeof(pci_dev_info_t));
+	async_exchange_end(exch);
+	if (rc != EOK) {
+		async_forget(req);
+		return EIO;
+	}
+
+	async_wait_for(req, &retval);
+	if (retval != EOK)
+		return EIO;
+
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/lib/c/include/ipc/pci.h
===================================================================
--- uspace/lib/c/include/ipc/pci.h	(revision 7acd787ef6db58d28e222cd14600149ac8c9ff72)
+++ uspace/lib/c/include/ipc/pci.h	(revision 7acd787ef6db58d28e222cd14600149ac8c9ff72)
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libcipc
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBC_IPC_PCI_H_
+#define LIBC_IPC_PCI_H_
+
+#include <ipc/common.h>
+
+/** PCI control service requests */
+typedef enum {
+	PCI_GET_DEVICES = IPC_FIRST_USER_METHOD,
+	PCI_DEV_GET_INFO
+} pci_request_t;
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/lib/c/include/pci.h
===================================================================
--- uspace/lib/c/include/pci.h	(revision 7acd787ef6db58d28e222cd14600149ac8c9ff72)
+++ uspace/lib/c/include/pci.h	(revision 7acd787ef6db58d28e222cd14600149ac8c9ff72)
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBC_PCI_H_
+#define LIBC_PCI_H_
+
+#include <errno.h>
+#include <ipc/devman.h>
+#include <loc.h>
+#include <types/pci.h>
+
+extern errno_t pci_open(service_id_t, pci_t **);
+extern void pci_close(pci_t *);
+extern errno_t pci_get_devices(pci_t *, devman_handle_t **, size_t *);
+extern errno_t pci_dev_get_info(pci_t *, devman_handle_t, pci_dev_info_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/c/include/types/pci.h
===================================================================
--- uspace/lib/c/include/types/pci.h	(revision 7acd787ef6db58d28e222cd14600149ac8c9ff72)
+++ uspace/lib/c/include/types/pci.h	(revision 7acd787ef6db58d28e222cd14600149ac8c9ff72)
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2019 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBC_TYPES_PCI_H_
+#define LIBC_TYPES_PCI_H_
+
+#include <async.h>
+#include <ipc/devman.h>
+#include <stdint.h>
+
+/** PCI device information */
+typedef struct {
+	/** Devman function handle */
+	devman_handle_t dev_handle;
+	/** Bus number */
+	uint8_t bus_num;
+	/** Device number */
+	uint8_t dev_num;
+	/** Function number */
+	uint8_t fn_num;
+	/** Vendor ID */
+	uint16_t vendor_id;
+	/** Device ID */
+	uint16_t device_id;
+} pci_dev_info_t;
+
+/** PCI service */
+typedef struct {
+	/** Session with PCI control service */
+	async_sess_t *sess;
+} pci_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/locsrv/locsrv.c
===================================================================
--- uspace/srv/locsrv/locsrv.c	(revision ca645a25041e245f85f0c75c5695e8546fb409f1)
+++ uspace/srv/locsrv/locsrv.c	(revision 7acd787ef6db58d28e222cd14600149ac8c9ff72)
@@ -1390,4 +1390,7 @@
 	categ_dir_add_cat(&cdir, cat);
 
+	cat = category_new("pci");
+	categ_dir_add_cat(&cdir, cat);
+
 	return true;
 }
