diff --git a/Makefile.am b/Makefile.am index 43d9e9d..38043fe 100644 --- a/Makefile.am +++ b/Makefile.am @@ -178,6 +178,7 @@ libuterm_la_SOURCES = \ src/uterm.h \ src/uterm_input.h \ src/uterm_video.h \ + src/uterm_pci.h \ src/uterm_video.c \ src/uterm_monitor.c \ src/uterm_vt.c \ @@ -205,6 +206,12 @@ libuterm_la_CPPFLAGS += $(UDEV_CFLAGS) libuterm_la_LIBADD += $(UDEV_LIBS) endif +if BUILD_ENABLE_PCIACCESS +libuterm_la_CPPFLAGS += $(PCIACCESS_CFLAGS) +libuterm_la_LIBADD += $(PCIACCESS_LIBS) +libuterm_la_SOURCES += src/uterm_pci.c +endif + if BUILD_ENABLE_VIDEO_FBDEV libuterm_la_SOURCES += src/uterm_video_fbdev.c endif diff --git a/configure.ac b/configure.ac index ca6e4bb..ab16640 100644 --- a/configure.ac +++ b/configure.ac @@ -103,6 +103,11 @@ PKG_CHECK_MODULES([FUSE], [fuse], AC_SUBST(FUSE_CFLAGS) AC_SUBST(FUSE_LIBS) +PKG_CHECK_MODULES([PCIACCESS], [pciaccess], + [have_pciaccess=yes], [have_pciaccess=no]) +AC_SUBST(PCIACCESS_CFLAGS) +AC_SUBST(PCIACCESS_LIBS) + # # Parse arguments # This parses all arguments that are given via "--enable-XY" or "--with-XY" and @@ -200,6 +205,16 @@ if test "x$enable_hotplug" = "x" ; then fi AC_MSG_RESULT([$enable_hotplug]) +# pciaccess +AC_MSG_CHECKING([whether user wants pciaccess device detection]) +AC_ARG_ENABLE([pciaccess], + [AS_HELP_STRING([--enable-pciaccess], + [enable device detection via pciaccess])]) +if test "x$enable_pciaccess" = "x" ; then + enable_pciaccess="yes (default)" +fi +AC_MSG_RESULT([$enable_pciaccess]) + # eloop-dbus AC_MSG_CHECKING([whether user wants eloop dbus support]) AC_ARG_ENABLE([eloop-dbus], @@ -455,6 +470,16 @@ if test ! "x$enable_hotplug" = "xno" ; then fi fi +# pciaccess +pciaccess_avail=no +if test ! "x$enable_pciaccess" = "xno" ; then + if test "x$have_pciaccess" = "xyes" ; then + pciaccess_avail=yes + elif test "x$enable_pciaccess" = "xyes" ; then + AC_ERROR([pciaccess libraries not found for pciaccess device detection]) + fi +fi + # renderer bblit renderer_bblit_avail=no if test ! "x$enable_renderer_bblit" = "xno" ; then @@ -714,6 +739,14 @@ if test "x$renderer_bblit_avail" = "xyes" ; then fi fi +# pciaccess +pciaccess_enabled=no +if test "x$pciaccess_avail" = "xyes" ; then + if test "x${enable_pciaccess% *}" = "xyes" ; then + pciaccess_enabled=yes + fi +fi + # hotplug hotplug_enabled=no if test "x$hotplug_avail" = "xyes" ; then @@ -885,6 +918,15 @@ fi AM_CONDITIONAL([BUILD_ENABLE_HOTPLUG], [test "x$hotplug_enabled" = "xyes"]) +# pciaccess +if test "x$pciaccess_enabled" = "xyes" ; then + AC_DEFINE([BUILD_ENABLE_PCIACCESS], [1], + [Use pciaccess for device detection]) +fi + +AM_CONDITIONAL([BUILD_ENABLE_PCIACCESS], + [test "x$pciaccess_enabled" = "xyes"]) + # renderer bblit if test "x$renderer_bblit_enabled" = "xyes" ; then AC_DEFINE([BUILD_ENABLE_RENDERER_BBLIT], [1], @@ -1060,6 +1102,7 @@ AC_MSG_NOTICE([Build configuration: optimizations: $optimizations_enabled ($optimizations_avail) multi-seat: $multi_seat_enabled ($multi_seat_avail) hotplug: $hotplug_enabled ($hotplug_avail) + pciaccess: $pciaccess_enabled ($pciaccess_avail) eloop-dbus: $eloop_dbus_enabled ($eloop_dbus_avail) Video Backends: diff --git a/src/uterm_monitor.c b/src/uterm_monitor.c index 2ba0565..36ce425 100644 --- a/src/uterm_monitor.c +++ b/src/uterm_monitor.c @@ -43,6 +43,7 @@ #include "log.h" #include "shl_dlist.h" #include "uterm.h" +#include "uterm_pci.h" #ifdef BUILD_ENABLE_MULTI_SEAT #include @@ -77,6 +78,8 @@ struct uterm_monitor { struct ev_fd *sd_mon_fd; #endif + char *pci_primary_id; + struct udev *udev; struct udev_monitor *umon; struct ev_fd *umon_fd; @@ -669,6 +672,10 @@ int uterm_monitor_new(struct uterm_monitor **out, mon->data = data; shl_dlist_init(&mon->seats); + ret = uterm_pci_get_primary_id(&mon->pci_primary_id); + if (ret) + log_warning("cannot get PCI primary ID"); + ret = monitor_sd_init(mon); if (ret) goto err_free; @@ -760,6 +767,7 @@ err_udev: err_sd: monitor_sd_deinit(mon); err_free: + free(mon->pci_primary_id); free(mon); return ret; } @@ -791,6 +799,7 @@ void uterm_monitor_unref(struct uterm_monitor *mon) udev_unref(mon->udev); monitor_sd_deinit(mon); ev_eloop_unref(mon->eloop); + free(mon->pci_primary_id); free(mon); } diff --git a/src/uterm_pci.c b/src/uterm_pci.c new file mode 100644 index 0000000..7bb59e1 --- /dev/null +++ b/src/uterm_pci.c @@ -0,0 +1,213 @@ +/* + * uterm - Linux User-Space Terminal + * + * Copyright (c) 2012 David Herrmann + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * PCI Helpers + * This uses the pciaccess library to retrieve information from the PCI bus. + */ + +#include +#include +#include +#include +#include +#include +#include "log.h" +#include "uterm.h" +#include "uterm_pci.h" + +#define LOG_SUBSYSTEM "pci" + +/* pci classes */ +#define UTERM_PCI_CLASS_PREHISTORIC 0x00 +#define UTERM_PCI_CLASS_DISPLAY 0x03 +#define UTERM_PCI_CLASS_MULTIMEDIA 0x04 +#define UTERM_PCI_CLASS_PROCESSOR 0x0b + +/* pci sub-classes */ +#define UTERM_PCI_SUBCLASS_DISPLAY_VGA 0x00 +#define UTERM_PCI_SUBCLASS_MULTIMEDIA_VIDEO 0x00 +#define UTERM_PCI_SUBCLASS_PROCESSOR_COPROC 0x40 + +/* pci registers */ +#define UTERM_PCI_CMD_MEM_ENABLE 0x02 + +static bool uterm_pci_is_gpu(struct pci_device *dev) +{ + uint32_t class = dev->device_class; + uint32_t match; + + match = UTERM_PCI_CLASS_PREHISTORIC << 16; + if ((class & 0x00ff0000) == match) + return true; + + match = UTERM_PCI_CLASS_DISPLAY << 16; + if ((class & 0x00ff0000) == match) + return true; + + match = UTERM_PCI_CLASS_MULTIMEDIA << 16; + match |= UTERM_PCI_SUBCLASS_MULTIMEDIA_VIDEO << 8; + if ((class & 0x00ffff00) == match) + return true; + + match = UTERM_PCI_CLASS_PROCESSOR << 16; + match |= UTERM_PCI_SUBCLASS_PROCESSOR_COPROC << 8; + if ((class & 0x00ffff00) == match) + return true; + + return false; +} + +static bool uterm_pci_is_vga(struct pci_device *dev) +{ + uint32_t class = dev->device_class; + uint32_t match; + + match = UTERM_PCI_CLASS_DISPLAY << 16; + match |= UTERM_PCI_SUBCLASS_DISPLAY_VGA << 8; + return (class & 0x00ffff00) == match; +} + +static const struct pci_slot_match uterm_pci_match = { + .domain = PCI_MATCH_ANY, + .bus = PCI_MATCH_ANY, + .dev = PCI_MATCH_ANY, + .func = PCI_MATCH_ANY, + .match_data = 0, +}; + +#define UTERM_PCI_FORMAT "pci:%04x:%02x:%02x.%d" + +int uterm_pci_get_primary_id(char **out) +{ + int ret; + struct pci_device_iterator *iter; + struct pci_device *dev; + char *buf; + uint16_t cmd; + unsigned int num; + + if (!out) + return -EINVAL; + + ret = pci_system_init(); + if (ret) { + log_error("cannot initialize pciaccess library (%d/%d): %m", + ret, errno); + return -EFAULT; + } + + iter = pci_slot_match_iterator_create(&uterm_pci_match); + if (!iter) { + log_error("cannot create pci-slot iterator (%d): %m", + errno); + ret = -EFAULT; + goto out_cleanup; + } + + buf = NULL; + num = 0; + while ((dev = pci_device_next(iter))) { + if (!uterm_pci_is_gpu(dev)) + continue; + + ++num; + if (!pci_device_is_boot_vga(dev)) + continue; + + log_debug("primary PCI GPU: " UTERM_PCI_FORMAT, + dev->domain, dev->bus, dev->dev, dev->func); + + if (buf) { + log_warning("multiple primary PCI GPUs found"); + continue; + } + + ret = asprintf(&buf, UTERM_PCI_FORMAT, dev->domain, dev->bus, + dev->dev, dev->func); + if (ret < 0) { + log_error("cannot allocate memory for PCI name"); + goto out_iter; + } + } + + free(iter); + if (buf) { + ret = 0; + *out = buf; + goto out_cleanup; + } + + /* If no GPU is marked as boot_vga, we try finding a VGA card */ + iter = pci_slot_match_iterator_create(&uterm_pci_match); + if (!iter) { + log_error("cannot create pci-slot iterator (%d): %m", + errno); + ret = -EFAULT; + goto out_cleanup; + } + + while ((dev = pci_device_next(iter))) { + if (!uterm_pci_is_gpu(dev)) + continue; + + ret = pci_device_cfg_read_u16(dev, &cmd, 4); + if (ret) + continue; + if (!(cmd & UTERM_PCI_CMD_MEM_ENABLE)) + continue; + if (num != 1 && !uterm_pci_is_vga(dev)) + continue; + + log_debug("primary PCI VGA GPU: " UTERM_PCI_FORMAT, + dev->domain, dev->bus, dev->dev, dev->func); + + if (buf) { + log_warning("multiple primary PCI VGA GPUs found"); + continue; + } + + ret = asprintf(&buf, UTERM_PCI_FORMAT, dev->domain, dev->bus, + dev->dev, dev->func); + if (ret < 0) { + log_error("cannot allocate memory for PCI name"); + goto out_iter; + } + } + + if (buf) { + ret = 0; + *out = buf; + } else { + log_warning("no primary PCI GPU found"); + ret = -ENOENT; + } + +out_iter: + free(iter); +out_cleanup: + pci_system_cleanup(); + return ret; +} diff --git a/src/uterm_pci.h b/src/uterm_pci.h new file mode 100644 index 0000000..dbb28e2 --- /dev/null +++ b/src/uterm_pci.h @@ -0,0 +1,50 @@ +/* + * uterm - Linux User-Space Terminal + * + * Copyright (c) 2012 David Herrmann + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * PCI Helpers + * This uses the pciaccess library to retrieve information from the PCI bus. + */ + +#ifndef UTERM_PCI_H +#define UTERM_PCI_H + +#include +#include "uterm.h" + +#ifdef BUILD_ENABLE_PCIACCESS + +int uterm_pci_get_primary_id(char **out); + +#else + +static inline int uterm_pci_get_primary_id(char **out) +{ + return -EOPNOTSUPP; +} + +#endif + +#endif /* UTERM_PCI_H */