From d6731c0378ee1a716b1e0ac14b0eeecfa84febcd Mon Sep 17 00:00:00 2001 From: Raboneko <119771935+raboneko@users.noreply.github.com> Date: Tue, 29 Apr 2025 10:07:30 -0700 Subject: [PATCH] feat(switcheroo-control): bump release number, update discrete patch (#4521) (#4527) - for F42 the release was bumped to 8, needed to bump it again for the terra package to be updated to - the discrete patch was updated to support the Intel Xe Driver (cherry picked from commit f907735d06a4186812e945c46cf10564ffe610c7) Signed-off-by: Jan200101 Co-authored-by: Jan --- ...pdate-GPUs-comment-for-dbus-property.patch | 27 - .../0002-main-add-Discrete-key.patch | 379 ----- ...x-integration-tests-without-UMockdev.patch | 27 - ...for-discrete-detection-with-mock-lib.patch | 278 ---- ...05-main-remove-leftover-and-fix-typo.patch | 39 - ...screte-dependencies-out-of-main-deps.patch | 48 - ...-for-discrete-command-line-arguments.patch | 137 -- .../0008-main-add-udev-rule-for-i915.patch | 25 - ...in-use-Discrete-key-in-switcherooctl.patch | 34 - ...0010-main-use-new-GPU-list-on-uevent.patch | 45 - anda/apps/switcheroo-control/discrete.patch | 1335 +++++++++++++++++ .../switcheroo-control.spec | 31 +- 12 files changed, 1350 insertions(+), 1055 deletions(-) delete mode 100644 anda/apps/switcheroo-control/0001-main-update-GPUs-comment-for-dbus-property.patch delete mode 100644 anda/apps/switcheroo-control/0002-main-add-Discrete-key.patch delete mode 100644 anda/apps/switcheroo-control/0003-tests-fix-integration-tests-without-UMockdev.patch delete mode 100644 anda/apps/switcheroo-control/0004-tests-add-tests-for-discrete-detection-with-mock-lib.patch delete mode 100644 anda/apps/switcheroo-control/0005-main-remove-leftover-and-fix-typo.patch delete mode 100644 anda/apps/switcheroo-control/0006-main-move-discrete-dependencies-out-of-main-deps.patch delete mode 100644 anda/apps/switcheroo-control/0007-main-use-glib-for-discrete-command-line-arguments.patch delete mode 100644 anda/apps/switcheroo-control/0008-main-add-udev-rule-for-i915.patch delete mode 100644 anda/apps/switcheroo-control/0009-main-use-Discrete-key-in-switcherooctl.patch delete mode 100644 anda/apps/switcheroo-control/0010-main-use-new-GPU-list-on-uevent.patch create mode 100644 anda/apps/switcheroo-control/discrete.patch diff --git a/anda/apps/switcheroo-control/0001-main-update-GPUs-comment-for-dbus-property.patch b/anda/apps/switcheroo-control/0001-main-update-GPUs-comment-for-dbus-property.patch deleted file mode 100644 index 825ca07b19..0000000000 --- a/anda/apps/switcheroo-control/0001-main-update-GPUs-comment-for-dbus-property.patch +++ /dev/null @@ -1,27 +0,0 @@ -From 44046bfbcb30a19c45416113a2a82a4d17a1a998 Mon Sep 17 00:00:00 2001 -From: Jan200101 -Date: Mon, 14 Aug 2023 14:06:45 +0200 -Subject: [PATCH 01/10] main: update GPUs comment for dbus property - -Signed-off-by: Jan200101 ---- - src/net.hadess.SwitcherooControl.xml | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/src/net.hadess.SwitcherooControl.xml b/src/net.hadess.SwitcherooControl.xml -index e52bc1a..59a8896 100644 ---- a/src/net.hadess.SwitcherooControl.xml -+++ b/src/net.hadess.SwitcherooControl.xml -@@ -38,7 +38,8 @@ - will contain a user-facing name for the GPU, the "Environment" (as) key will - contain an array of even number of strings, each being an environment - variable to set to use the GPU, followed by its value, the "Default" (b) key -- will tag the default (usually integrated) GPU. -+ will tag the default GPU, the "Discrete" (b) key tags if the GPU is a -+ dedicated component. - --> - - --- -2.46.0 - diff --git a/anda/apps/switcheroo-control/0002-main-add-Discrete-key.patch b/anda/apps/switcheroo-control/0002-main-add-Discrete-key.patch deleted file mode 100644 index b67ca67fae..0000000000 --- a/anda/apps/switcheroo-control/0002-main-add-Discrete-key.patch +++ /dev/null @@ -1,379 +0,0 @@ -From 4f31415cb61a50c2bcba1510a7511518417d0970 Mon Sep 17 00:00:00 2001 -From: Jan200101 -Date: Mon, 11 Sep 2023 15:21:46 +0200 -Subject: [PATCH 02/10] main: add Discrete key - -Signed-off-by: Jan200101 ---- - .gitlab-ci.yml | 1 + - data/30-discrete-gpu.rules.in | 3 + - data/meson.build | 7 ++ - meson.build | 9 +++ - meson_options.txt | 24 +++++++ - src/discrete-detection/amdgpu.c | 46 +++++++++++++ - src/discrete-detection/meson.build | 18 +++++ - src/discrete-detection/nouveau.c | 105 +++++++++++++++++++++++++++++ - src/meson.build | 4 +- - src/switcheroo-control.c | 16 +++++ - 10 files changed, 232 insertions(+), 1 deletion(-) - create mode 100644 data/30-discrete-gpu.rules.in - create mode 100644 src/discrete-detection/amdgpu.c - create mode 100644 src/discrete-detection/meson.build - create mode 100644 src/discrete-detection/nouveau.c - -diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml -index a6aa3c7..a09fe20 100644 ---- a/.gitlab-ci.yml -+++ b/.gitlab-ci.yml -@@ -3,6 +3,7 @@ image: fedora:rawhide - variables: - DEPENDENCIES: glib2-devel - libgudev-devel -+ libdrm-devel - gtk-doc - gcc - gcc-c++ -diff --git a/data/30-discrete-gpu.rules.in b/data/30-discrete-gpu.rules.in -new file mode 100644 -index 0000000..a803ed4 ---- /dev/null -+++ b/data/30-discrete-gpu.rules.in -@@ -0,0 +1,3 @@ -+DRIVERS=="amdgpu", SUBSYSTEM=="drm", PROGRAM="@libexecdir@/check-discrete-amdgpu $env{DEVNAME}", TAG+="switcheroo-discrete-gpu" -+DRIVERS=="nouveau", SUBSYSTEM=="drm", PROGRAM="@libexecdir@/check-discrete-nouveau $env{DEVNAME}", TAG+="switcheroo-discrete-gpu" -+DRIVERS=="nvidia", TAG+="switcheroo-discrete-gpu" -diff --git a/data/meson.build b/data/meson.build -index 85e5c93..38cf96c 100644 ---- a/data/meson.build -+++ b/data/meson.build -@@ -17,3 +17,10 @@ install_data( - '30-pci-intel-gpu.hwdb', - install_dir: hwdb_dir, - ) -+ -+configure_file( -+ input: '30-discrete-gpu.rules.in', -+ output: '30-discrete-gpu.rules', -+ configuration: data_conf, -+ install_dir: rules_dir, -+) -diff --git a/meson.build b/meson.build -index b8f4bff..b3aaf0c 100644 ---- a/meson.build -+++ b/meson.build -@@ -20,6 +20,9 @@ gnome = import('gnome') - glib = dependency('glib-2.0', version: '>= 2.56.0') - gio = dependency('gio-2.0', version: '>= 2.56.0') - gudev = dependency('gudev-1.0', version: '>= 232') -+libdrm = dependency('libdrm', version: '>= 2.4.97', required: get_option('libdrm')) -+libdrm_nouveau = dependency('libdrm_nouveau', version: '>= 2.4.97', required: get_option('libdrm_nouveau')) -+libdrm_amdgpu = dependency('libdrm_amdgpu', version: '>= 2.4.97', required: get_option('libdrm_amdgpu')) - - systemd_systemunitdir = get_option('systemdsystemunitdir') - if systemd_systemunitdir == '' -@@ -32,6 +35,12 @@ if hwdb_dir == '' - hwdb_dir = udevdir / 'hwdb.d' - endif - -+rules_dir = get_option('rulesdir') -+if rules_dir == '' -+ udevdir = dependency('udev').get_pkgconfig_variable('udevdir') -+ rules_dir = udevdir / 'rules.d' -+endif -+ - # Make like license available in the build root for docs - configure_file( - input: 'COPYING', -diff --git a/meson_options.txt b/meson_options.txt -index c8d9619..b8d671a 100644 ---- a/meson_options.txt -+++ b/meson_options.txt -@@ -10,6 +10,12 @@ option('hwdbdir', - description: 'Directory for hwdb files', - ) - -+option('rulesdir', -+ type: 'string', -+ value: '', -+ description: 'Directory for ruke files', -+) -+ - option('gtk_doc', - type: 'boolean', - value: false, -@@ -21,3 +27,21 @@ option('tests', - type: 'boolean', - value: false - ) -+ -+option('libdrm', -+ description: 'Whether libdrm should be used to probe GPUs', -+ type: 'feature', -+ value: 'auto' -+) -+ -+option('libdrm_nouveau', -+ description: 'Whether libdrm_nouveau should be used to probe Nvidia GPUs', -+ type: 'feature', -+ value: 'auto' -+) -+ -+option('libdrm_amdgpu', -+ description: 'Whether libdrm_amdgpu should be used to probe AMD GPUs', -+ type: 'feature', -+ value: 'auto' -+) -diff --git a/src/discrete-detection/amdgpu.c b/src/discrete-detection/amdgpu.c -new file mode 100644 -index 0000000..2d9804f ---- /dev/null -+++ b/src/discrete-detection/amdgpu.c -@@ -0,0 +1,46 @@ -+ -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+typedef int handle; -+G_DEFINE_AUTO_CLEANUP_FREE_FUNC(handle, close, -1) -+G_DEFINE_AUTOPTR_CLEANUP_FUNC(amdgpu_device_handle, free); -+ -+int main(int argc, char** argv) -+{ -+ if (argc < 2) -+ { -+ puts ("check-discrete-amdgpu [DEVNAME]"); -+ return EXIT_FAILURE; -+ } -+ -+ const char *devname; -+ g_auto(handle) fd = -1; -+ g_autoptr(GUdevDevice) parent = NULL; -+ struct drm_amdgpu_info_device device_info = {0}; -+ amdgpu_device_handle device = NULL; -+ uint32_t drm_major, drm_minor; -+ -+ devname = argv[1]; -+ fd = open (devname, O_RDWR); -+ if (fd < 0) -+ return EXIT_FAILURE; -+ -+ if (amdgpu_device_initialize (fd, &drm_major, &drm_minor, &device)) -+ return EXIT_FAILURE; -+ -+ if (amdgpu_query_info (device, AMDGPU_INFO_DEV_INFO, sizeof(device_info), &device_info)) -+ return EXIT_FAILURE; -+ -+ /* AMDGPU_IDS_FLAGS_FUSION is set for all APUs */ -+ if (device_info.ids_flags & AMDGPU_IDS_FLAGS_FUSION) -+ return EXIT_FAILURE; -+ -+ return EXIT_SUCCESS; -+} -diff --git a/src/discrete-detection/meson.build b/src/discrete-detection/meson.build -new file mode 100644 -index 0000000..3a6c03f ---- /dev/null -+++ b/src/discrete-detection/meson.build -@@ -0,0 +1,18 @@ -+ -+if libdrm_amdgpu.found() -+ executable('check-discrete-amdgpu', -+ files('amdgpu.c'), -+ dependencies: deps, -+ install: true, -+ install_dir: libexecdir, -+ ) -+endif -+ -+if libdrm.found() and libdrm_nouveau.found() -+ executable('check-discrete-nouveau', -+ files('nouveau.c'), -+ dependencies: deps, -+ install: true, -+ install_dir: libexecdir, -+ ) -+endif -diff --git a/src/discrete-detection/nouveau.c b/src/discrete-detection/nouveau.c -new file mode 100644 -index 0000000..0a1f220 ---- /dev/null -+++ b/src/discrete-detection/nouveau.c -@@ -0,0 +1,105 @@ -+ -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+ -+typedef int handle; -+G_DEFINE_AUTO_CLEANUP_FREE_FUNC(handle, close, -1) -+ -+int main(int argc, char** argv) -+{ -+ if (argc < 2) -+ { -+ puts ("check-discrete-nouveau [DEVNAME]"); -+ return EXIT_FAILURE; -+ } -+ -+ const char *devname; -+ g_auto(handle) fd = -1; -+ -+ devname = argv[1]; -+ fd = open (devname, O_RDWR); -+ if (fd < 0) -+ return EXIT_FAILURE; -+ -+ g_autofree void *device = malloc(352); -+ -+ /* Init device */ -+ { -+ struct { -+ struct nvif_ioctl_v0 ioctl; -+ struct nvif_ioctl_new_v0 new; -+ struct nv_device_v0 dev; -+ } init_args = { -+ .ioctl = { -+ .object = 0, -+ .owner = NVIF_IOCTL_V0_OWNER_ANY, -+ .route = 0x00, -+ .type = NVIF_IOCTL_V0_NEW, -+ .version = 0, -+ }, -+ .new = { -+ .handle = 0, -+ .object = (uintptr_t)device, -+ .oclass = NV_DEVICE, -+ .route = NVIF_IOCTL_V0_ROUTE_NVIF, -+ .token = (uintptr_t)device, -+ .version = 0, -+ }, -+ .dev = { -+ .device = ~0ULL, -+ }, -+ }; -+ -+ if (drmCommandWrite (fd, DRM_NOUVEAU_NVIF, &init_args, sizeof(init_args))) -+ return EXIT_FAILURE; -+ } -+ -+ /* Query device info */ -+ struct { -+ struct nvif_ioctl_v0 ioctl; -+ struct nvif_ioctl_mthd_v0 mthd; -+ struct nv_device_info_v0 info; -+ } args = { -+ .ioctl = { -+ .object = (uintptr_t)device, -+ .owner = NVIF_IOCTL_V0_OWNER_ANY, -+ .route = 0x00, -+ .type = NVIF_IOCTL_V0_MTHD, -+ .version = 0, -+ }, -+ .mthd = { -+ .method = NV_DEVICE_V0_INFO, -+ .version = 0, -+ }, -+ .info = { -+ .version = 0, -+ }, -+ }; -+ -+ if (drmCommandWriteRead (fd, DRM_NOUVEAU_NVIF, &args, sizeof(args))) -+ return EXIT_FAILURE; -+ -+ -+ switch (args.info.platform) -+ { -+ case NV_DEVICE_INFO_V0_IGP: -+ case NV_DEVICE_INFO_V0_SOC: -+ return EXIT_FAILURE; -+ -+ case NV_DEVICE_INFO_V0_PCI: -+ case NV_DEVICE_INFO_V0_AGP: -+ case NV_DEVICE_INFO_V0_PCIE: -+ default: -+ return EXIT_SUCCESS; -+ } -+ return EXIT_FAILURE; -+} -diff --git a/src/meson.build b/src/meson.build -index ab3a77d..da4267f 100644 ---- a/src/meson.build -+++ b/src/meson.build -@@ -1,4 +1,4 @@ --deps = [glib, gio, gudev] -+deps = [glib, gio, gudev, libdrm, libdrm_nouveau, libdrm_amdgpu] - - sources = [ - 'info-cleanup.c', -@@ -34,3 +34,5 @@ configure_file( - configuration: switcherooctl_conf, - install_dir: get_option('bindir') - ) -+ -+subdir('discrete-detection') -diff --git a/src/switcheroo-control.c b/src/switcheroo-control.c -index abd8154..e407bfb 100644 ---- a/src/switcheroo-control.c -+++ b/src/switcheroo-control.c -@@ -31,6 +31,7 @@ typedef struct { - char *name; - GPtrArray *env; - gboolean is_default; -+ gboolean is_discrete; - } CardData; - - typedef struct { -@@ -94,6 +95,8 @@ build_gpus_variant (ControlData *data) - g_variant_new_strv ((const gchar * const *) card->env->pdata, card->env->len)); - g_variant_builder_add (&asv_builder, "{sv}", "Default", - g_variant_new_boolean (card->is_default)); -+ g_variant_builder_add (&asv_builder, "{sv}", "Discrete", -+ g_variant_new_boolean (card->is_discrete)); - - g_variant_builder_add (&builder, "a{sv}", &asv_builder); - } -@@ -312,6 +315,18 @@ get_card_is_default (GUdevDevice *d) - return g_udev_device_get_sysfs_attr_as_boolean (parent, "boot_vga"); - } - -+static gboolean -+get_card_is_discrete (GUdevDevice *d) -+{ -+ const char * const * tags; -+ g_autoptr (GUdevDevice) platform_device = NULL; -+ -+ tags = g_udev_device_get_tags (d); -+ if (tags && g_strv_contains (tags, "switcheroo-discrete-gpu")) -+ return TRUE; -+ return FALSE; -+} -+ - static CardData * - get_card_data (GUdevClient *client, - GUdevDevice *d) -@@ -328,6 +343,7 @@ get_card_data (GUdevClient *client, - data->name = get_card_name (d); - data->env = env; - data->is_default = get_card_is_default (d); -+ data->is_discrete = get_card_is_discrete (d); - - return data; - } --- -2.46.0 - diff --git a/anda/apps/switcheroo-control/0003-tests-fix-integration-tests-without-UMockdev.patch b/anda/apps/switcheroo-control/0003-tests-fix-integration-tests-without-UMockdev.patch deleted file mode 100644 index e8ee844d53..0000000000 --- a/anda/apps/switcheroo-control/0003-tests-fix-integration-tests-without-UMockdev.patch +++ /dev/null @@ -1,27 +0,0 @@ -From 1b115ed72e03ff1169cbfddd79ef10890baca133 Mon Sep 17 00:00:00 2001 -From: Jan200101 -Date: Tue, 12 Sep 2023 15:53:40 +0200 -Subject: [PATCH 03/10] tests: fix integration tests without UMockdev - `gi.require_version` throws ValueError if the dependency cannot be found - -Signed-off-by: Jan200101 ---- - tests/integration-test.py | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/tests/integration-test.py b/tests/integration-test.py -index d8dea16..e3dd996 100755 ---- a/tests/integration-test.py -+++ b/tests/integration-test.py -@@ -37,7 +37,7 @@ except ImportError as e: - try: - gi.require_version('UMockdev', '1.0') - from gi.repository import UMockdev --except ImportError: -+except (ImportError, ValueError): - sys.stderr.write('Skipping tests, umockdev not available (https://github.com/martinpitt/umockdev)\n') - sys.exit(0) - --- -2.46.0 - diff --git a/anda/apps/switcheroo-control/0004-tests-add-tests-for-discrete-detection-with-mock-lib.patch b/anda/apps/switcheroo-control/0004-tests-add-tests-for-discrete-detection-with-mock-lib.patch deleted file mode 100644 index 24915eba46..0000000000 --- a/anda/apps/switcheroo-control/0004-tests-add-tests-for-discrete-detection-with-mock-lib.patch +++ /dev/null @@ -1,278 +0,0 @@ -From d933e96bdb15679ae7653f929461982aa66973ba Mon Sep 17 00:00:00 2001 -From: Jan200101 -Date: Tue, 12 Sep 2023 15:58:16 +0200 -Subject: [PATCH 04/10] tests: add tests for discrete detection with mock libs - Both tests have 4 different ways of testing: - Invalid Device - Unexpected - Device - Non Discrete GPU (iGPU/APU) - Discrete GPU - -Signed-off-by: Jan200101 ---- - src/discrete-detection/meson.build | 4 +- - tests/discrete-detection/libdrm_amdgpu_mock.c | 57 +++++++++++++ - .../discrete-detection/libdrm_nouveau_mock.c | 68 ++++++++++++++++ - tests/discrete-detection/meson.build | 80 +++++++++++++++++++ - tests/meson.build | 2 + - 5 files changed, 209 insertions(+), 2 deletions(-) - create mode 100644 tests/discrete-detection/libdrm_amdgpu_mock.c - create mode 100644 tests/discrete-detection/libdrm_nouveau_mock.c - create mode 100644 tests/discrete-detection/meson.build - -diff --git a/src/discrete-detection/meson.build b/src/discrete-detection/meson.build -index 3a6c03f..8eb8437 100644 ---- a/src/discrete-detection/meson.build -+++ b/src/discrete-detection/meson.build -@@ -1,6 +1,6 @@ - - if libdrm_amdgpu.found() -- executable('check-discrete-amdgpu', -+ amdgpu_discrete = executable('check-discrete-amdgpu', - files('amdgpu.c'), - dependencies: deps, - install: true, -@@ -9,7 +9,7 @@ if libdrm_amdgpu.found() - endif - - if libdrm.found() and libdrm_nouveau.found() -- executable('check-discrete-nouveau', -+ nouveau_discrete = executable('check-discrete-nouveau', - files('nouveau.c'), - dependencies: deps, - install: true, -diff --git a/tests/discrete-detection/libdrm_amdgpu_mock.c b/tests/discrete-detection/libdrm_amdgpu_mock.c -new file mode 100644 -index 0000000..20fa4aa ---- /dev/null -+++ b/tests/discrete-detection/libdrm_amdgpu_mock.c -@@ -0,0 +1,57 @@ -+#include -+#include -+#include -+ -+#include -+ -+enum { -+ OTHER_GPU, -+ AMD_APU, -+ AMD_GPU, -+}; -+ -+/* Mock open(2) so we can test multiple devices configurations */ -+int open(const char *pathname, int flags) -+{ -+ if (!strcmp(pathname, "OTHER_GPU")) -+ return OTHER_GPU; -+ if (!strcmp (pathname, "AMD_APU")) -+ return AMD_APU; -+ if (!strcmp (pathname, "AMD_GPU")) -+ return AMD_GPU; -+ -+ return -1; -+} -+ -+/* open64 may be used for large file support */ -+int open64(const char *pathname, int flags) -+{ -+ return open (pathname, flags); -+} -+ -+int amdgpu_device_initialize(int fd, uint32_t *major_version, uint32_t *minor_version, int *device_handle) -+{ -+ // Store the fd in the device handle for access in query_info -+ *device_handle = fd; -+ -+ if (fd != AMD_GPU && fd != AMD_APU) -+ return 1; -+ -+ return 0; -+} -+ -+int amdgpu_query_info(int device_handle, unsigned info_id, unsigned size, void *value) -+{ -+ struct drm_amdgpu_info_device* device_info = value; -+ -+ if (device_handle == AMD_GPU) { -+ device_info->ids_flags = 0; -+ return 0; -+ } -+ if (device_handle == AMD_APU) { -+ device_info->ids_flags = AMDGPU_IDS_FLAGS_FUSION; -+ return 0; -+ } -+ -+ return 1; -+} -diff --git a/tests/discrete-detection/libdrm_nouveau_mock.c b/tests/discrete-detection/libdrm_nouveau_mock.c -new file mode 100644 -index 0000000..cdbfda8 ---- /dev/null -+++ b/tests/discrete-detection/libdrm_nouveau_mock.c -@@ -0,0 +1,68 @@ -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+ -+enum { -+ OTHER_GPU, -+ NVIDIA_IGPU, -+ NVIDIA_GPU, -+}; -+ -+/* Mock open(2) so we can test multiple devices configurations */ -+int open(const char *pathname, int flags) -+{ -+ if (!strcmp(pathname, "OTHER_GPU")) -+ return OTHER_GPU; -+ if (!strcmp (pathname, "NVIDIA_IGPU")) -+ return NVIDIA_IGPU; -+ if (!strcmp (pathname, "NVIDIA_GPU")) -+ return NVIDIA_GPU; -+ -+ return -1; -+} -+ -+/* open64 may be used for large file support */ -+int open64(const char *pathname, int flags) -+{ -+ return open (pathname, flags); -+} -+ -+int drmCommandWrite(int fd, unsigned long drmCommandIndex, void *data, unsigned long size) -+{ -+ if (drmCommandIndex != DRM_NOUVEAU_NVIF) -+ return 1; -+ -+ if (fd != NVIDIA_GPU && fd != NVIDIA_IGPU) -+ return 1; -+ -+ return 0; -+} -+ -+int drmCommandWriteRead(int fd, unsigned long drmCommandIndex, void *data, unsigned long size) -+{ -+ if (drmCommandIndex != DRM_NOUVEAU_NVIF) -+ return 1; -+ -+ struct { -+ struct nvif_ioctl_v0 ioctl; -+ struct nvif_ioctl_mthd_v0 mthd; -+ struct nv_device_info_v0 info; -+ } *args = data; -+ -+ if (fd == NVIDIA_GPU) { -+ args->info.platform = NV_DEVICE_INFO_V0_PCIE; -+ return 0; -+ } -+ if (fd == NVIDIA_IGPU) { -+ args->info.platform = NV_DEVICE_INFO_V0_IGP; -+ return 0; -+ } -+ -+ return 1; -+} -\ No newline at end of file -diff --git a/tests/discrete-detection/meson.build b/tests/discrete-detection/meson.build -new file mode 100644 -index 0000000..f01a014 ---- /dev/null -+++ b/tests/discrete-detection/meson.build -@@ -0,0 +1,80 @@ -+ -+if libdrm_amdgpu.found() -+ amdgpu_mock_lib = shared_library( -+ 'drm_amdgpu_mock', -+ files('libdrm_amdgpu_mock.c'), -+ dependencies: libdrm_amdgpu -+ ) -+ -+ test( -+ 'test amdgpu detection with invalid device', -+ amdgpu_discrete, -+ args: ['NO_GPU'], -+ env: environment({'LD_PRELOAD': amdgpu_mock_lib.full_path()}), -+ should_fail: true -+ ) -+ -+ test( -+ 'test amdgpu detection with non-AMD GPU', -+ amdgpu_discrete, -+ args: ['OTHER_GPU'], -+ env: environment({'LD_PRELOAD': amdgpu_mock_lib.full_path()}), -+ should_fail: true -+ ) -+ -+ test( -+ 'test amdgpu detection with AMD APU', -+ amdgpu_discrete, -+ args: ['AMD_APU'], -+ env: environment({'LD_PRELOAD': amdgpu_mock_lib.full_path()}), -+ should_fail: true -+ ) -+ -+ test( -+ 'test amdgpu detection with AMD GPU', -+ amdgpu_discrete, -+ args: ['AMD_GPU'], -+ env: environment({'LD_PRELOAD': amdgpu_mock_lib.full_path()}), -+ should_fail: false -+ ) -+endif -+ -+if libdrm.found() and libdrm_nouveau.found() -+ nouveau_mock_lib = shared_library( -+ 'drm_nouveau_mock', -+ files('libdrm_nouveau_mock.c'), -+ dependencies: [libdrm, libdrm_nouveau] -+ ) -+ -+ test( -+ 'test nouveau detection with invalid device', -+ nouveau_discrete, -+ args: ['NO_GPU'], -+ env: environment({'LD_PRELOAD': nouveau_mock_lib.full_path()}), -+ should_fail: true -+ ) -+ -+ test( -+ 'test nouveau detection with non-Nvidia GPU', -+ nouveau_discrete, -+ args: ['OTHER_GPU'], -+ env: environment({'LD_PRELOAD': nouveau_mock_lib.full_path()}), -+ should_fail: true -+ ) -+ -+ test( -+ 'test nouveau detection with Nvidia iGPU', -+ nouveau_discrete, -+ args: ['NVIDIA_IGPU'], -+ env: environment({'LD_PRELOAD': nouveau_mock_lib.full_path()}), -+ should_fail: true -+ ) -+ -+ test( -+ 'test nouveau detection with Nvidia GPU', -+ nouveau_discrete, -+ args: ['NVIDIA_GPU'], -+ env: environment({'LD_PRELOAD': nouveau_mock_lib.full_path()}), -+ should_fail: false -+ ) -+endif -\ No newline at end of file -diff --git a/tests/meson.build b/tests/meson.build -index b0b7476..61ef00c 100644 ---- a/tests/meson.build -+++ b/tests/meson.build -@@ -15,3 +15,5 @@ foreach ut: unit_tests - env: envs, - ) - endforeach -+ -+subdir('discrete-detection') --- -2.46.0 - diff --git a/anda/apps/switcheroo-control/0005-main-remove-leftover-and-fix-typo.patch b/anda/apps/switcheroo-control/0005-main-remove-leftover-and-fix-typo.patch deleted file mode 100644 index b10984fc67..0000000000 --- a/anda/apps/switcheroo-control/0005-main-remove-leftover-and-fix-typo.patch +++ /dev/null @@ -1,39 +0,0 @@ -From c102b643945dc076d881497dd2ca5865938f7053 Mon Sep 17 00:00:00 2001 -From: Jan200101 -Date: Tue, 12 Sep 2023 15:57:47 +0200 -Subject: [PATCH 05/10] main: remove leftover and fix typo - -Signed-off-by: Jan200101 ---- - meson_options.txt | 2 +- - src/switcheroo-control.c | 1 - - 2 files changed, 1 insertion(+), 2 deletions(-) - -diff --git a/meson_options.txt b/meson_options.txt -index b8d671a..c77fea8 100644 ---- a/meson_options.txt -+++ b/meson_options.txt -@@ -13,7 +13,7 @@ option('hwdbdir', - option('rulesdir', - type: 'string', - value: '', -- description: 'Directory for ruke files', -+ description: 'Directory for rule files', - ) - - option('gtk_doc', -diff --git a/src/switcheroo-control.c b/src/switcheroo-control.c -index e407bfb..0f6a548 100644 ---- a/src/switcheroo-control.c -+++ b/src/switcheroo-control.c -@@ -319,7 +319,6 @@ static gboolean - get_card_is_discrete (GUdevDevice *d) - { - const char * const * tags; -- g_autoptr (GUdevDevice) platform_device = NULL; - - tags = g_udev_device_get_tags (d); - if (tags && g_strv_contains (tags, "switcheroo-discrete-gpu")) --- -2.46.0 - diff --git a/anda/apps/switcheroo-control/0006-main-move-discrete-dependencies-out-of-main-deps.patch b/anda/apps/switcheroo-control/0006-main-move-discrete-dependencies-out-of-main-deps.patch deleted file mode 100644 index 18a0e42158..0000000000 --- a/anda/apps/switcheroo-control/0006-main-move-discrete-dependencies-out-of-main-deps.patch +++ /dev/null @@ -1,48 +0,0 @@ -From f764db4eb565c19ba14155791fbfced3fb5d34c8 Mon Sep 17 00:00:00 2001 -From: Jan200101 -Date: Tue, 12 Sep 2023 15:58:27 +0200 -Subject: [PATCH 06/10] main: move discrete dependencies out of main deps - -Signed-off-by: Jan200101 ---- - src/discrete-detection/meson.build | 5 +++-- - src/meson.build | 2 +- - 2 files changed, 4 insertions(+), 3 deletions(-) - -diff --git a/src/discrete-detection/meson.build b/src/discrete-detection/meson.build -index 8eb8437..353316f 100644 ---- a/src/discrete-detection/meson.build -+++ b/src/discrete-detection/meson.build -@@ -1,8 +1,9 @@ -+discrete_deps = deps + [libdrm, libdrm_nouveau, libdrm_amdgpu] - - if libdrm_amdgpu.found() - amdgpu_discrete = executable('check-discrete-amdgpu', - files('amdgpu.c'), -- dependencies: deps, -+ dependencies: discrete_deps, - install: true, - install_dir: libexecdir, - ) -@@ -11,7 +12,7 @@ endif - if libdrm.found() and libdrm_nouveau.found() - nouveau_discrete = executable('check-discrete-nouveau', - files('nouveau.c'), -- dependencies: deps, -+ dependencies: discrete_deps, - install: true, - install_dir: libexecdir, - ) -diff --git a/src/meson.build b/src/meson.build -index da4267f..22d69e7 100644 ---- a/src/meson.build -+++ b/src/meson.build -@@ -1,4 +1,4 @@ --deps = [glib, gio, gudev, libdrm, libdrm_nouveau, libdrm_amdgpu] -+deps = [glib, gio, gudev] - - sources = [ - 'info-cleanup.c', --- -2.46.0 - diff --git a/anda/apps/switcheroo-control/0007-main-use-glib-for-discrete-command-line-arguments.patch b/anda/apps/switcheroo-control/0007-main-use-glib-for-discrete-command-line-arguments.patch deleted file mode 100644 index 524930709f..0000000000 --- a/anda/apps/switcheroo-control/0007-main-use-glib-for-discrete-command-line-arguments.patch +++ /dev/null @@ -1,137 +0,0 @@ -From d2ecc29469d5572fd171926c9d1dbb1b851c7b09 Mon Sep 17 00:00:00 2001 -From: Jan200101 -Date: Tue, 12 Sep 2023 17:12:00 +0200 -Subject: [PATCH 07/10] main: use glib for discrete command-line arguments - -Signed-off-by: Jan200101 ---- - src/discrete-detection/amdgpu.c | 29 ++++++++++++++++++++--------- - src/discrete-detection/nouveau.c | 29 ++++++++++++++++++++--------- - 2 files changed, 40 insertions(+), 18 deletions(-) - -diff --git a/src/discrete-detection/amdgpu.c b/src/discrete-detection/amdgpu.c -index 2d9804f..5a9a4ab 100644 ---- a/src/discrete-detection/amdgpu.c -+++ b/src/discrete-detection/amdgpu.c -@@ -1,9 +1,10 @@ -- - #include - #include - #include -+#include - #include - #include -+#include - - #include - #include -@@ -12,22 +13,32 @@ typedef int handle; - G_DEFINE_AUTO_CLEANUP_FREE_FUNC(handle, close, -1) - G_DEFINE_AUTOPTR_CLEANUP_FUNC(amdgpu_device_handle, free); - --int main(int argc, char** argv) -+int main (int argc, char** argv) - { -- if (argc < 2) -- { -- puts ("check-discrete-amdgpu [DEVNAME]"); -- return EXIT_FAILURE; -- } -- - const char *devname; - g_auto(handle) fd = -1; -- g_autoptr(GUdevDevice) parent = NULL; - struct drm_amdgpu_info_device device_info = {0}; - amdgpu_device_handle device = NULL; - uint32_t drm_major, drm_minor; -+ g_autoptr(GOptionContext) option_context = NULL; -+ g_autoptr(GError) error = NULL; - -+ setlocale (LC_ALL, ""); -+ option_context = g_option_context_new (""); -+ -+ if (!g_option_context_parse (option_context, &argc, &argv, &error)) { -+ g_print ("Failed to parse arguments: %s\n", error->message); -+ return EXIT_FAILURE; -+ } -+ -+ if (argc < 2) -+ { -+ g_print ("%s\n", g_option_context_get_help (option_context, TRUE, NULL)); -+ return EXIT_FAILURE; -+ } - devname = argv[1]; -+; -+ - fd = open (devname, O_RDWR); - if (fd < 0) - return EXIT_FAILURE; -diff --git a/src/discrete-detection/nouveau.c b/src/discrete-detection/nouveau.c -index 0a1f220..1d61cbb 100644 ---- a/src/discrete-detection/nouveau.c -+++ b/src/discrete-detection/nouveau.c -@@ -1,9 +1,10 @@ -- - #include - #include - #include -+#include - #include - #include -+#include - - #include - #include -@@ -14,23 +15,34 @@ - typedef int handle; - G_DEFINE_AUTO_CLEANUP_FREE_FUNC(handle, close, -1) - --int main(int argc, char** argv) -+int main (int argc, char** argv) - { -+ const gchar *devname = NULL; -+ g_auto(handle) fd = -1; -+ g_autofree void *device = NULL; -+ g_autoptr(GOptionContext) option_context = NULL; -+ g_autoptr(GError) error = NULL; -+ -+ setlocale (LC_ALL, ""); -+ option_context = g_option_context_new (""); -+ -+ if (!g_option_context_parse (option_context, &argc, &argv, &error)) { -+ g_print ("Failed to parse arguments: %s\n", error->message); -+ return EXIT_FAILURE; -+ } -+ - if (argc < 2) - { -- puts ("check-discrete-nouveau [DEVNAME]"); -+ g_print ("%s\n", g_option_context_get_help (option_context, TRUE, NULL)); - return EXIT_FAILURE; - } -- -- const char *devname; -- g_auto(handle) fd = -1; -- - devname = argv[1]; -+ - fd = open (devname, O_RDWR); - if (fd < 0) - return EXIT_FAILURE; - -- g_autofree void *device = malloc(352); -+ device = malloc(352); - - /* Init device */ - { -@@ -88,7 +100,6 @@ int main(int argc, char** argv) - if (drmCommandWriteRead (fd, DRM_NOUVEAU_NVIF, &args, sizeof(args))) - return EXIT_FAILURE; - -- - switch (args.info.platform) - { - case NV_DEVICE_INFO_V0_IGP: --- -2.46.0 - diff --git a/anda/apps/switcheroo-control/0008-main-add-udev-rule-for-i915.patch b/anda/apps/switcheroo-control/0008-main-add-udev-rule-for-i915.patch deleted file mode 100644 index b7a684a1c6..0000000000 --- a/anda/apps/switcheroo-control/0008-main-add-udev-rule-for-i915.patch +++ /dev/null @@ -1,25 +0,0 @@ -From 462b09f02de37dfd2965d23cc7c4137bcf45a4ae Mon Sep 17 00:00:00 2001 -From: Jan200101 -Date: Wed, 14 Feb 2024 20:25:42 +0100 -Subject: [PATCH 08/10] main: add udev rule for i915 checking a lot of systems - has shown that the intel iGPU will always be available at `0000:00:02.0`. - Using ID_PATH would have been cleaner, but I couldn't get it to work. - -Signed-off-by: Jan200101 ---- - data/30-discrete-gpu.rules.in | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/data/30-discrete-gpu.rules.in b/data/30-discrete-gpu.rules.in -index a803ed4..f30f315 100644 ---- a/data/30-discrete-gpu.rules.in -+++ b/data/30-discrete-gpu.rules.in -@@ -1,3 +1,4 @@ - DRIVERS=="amdgpu", SUBSYSTEM=="drm", PROGRAM="@libexecdir@/check-discrete-amdgpu $env{DEVNAME}", TAG+="switcheroo-discrete-gpu" - DRIVERS=="nouveau", SUBSYSTEM=="drm", PROGRAM="@libexecdir@/check-discrete-nouveau $env{DEVNAME}", TAG+="switcheroo-discrete-gpu" --DRIVERS=="nvidia", TAG+="switcheroo-discrete-gpu" -+DRIVERS=="nvidia", SUBSYSTEM=="drm", TAG+="switcheroo-discrete-gpu" -+DRIVERS=="i915", SUBSYSTEM=="drm", DEVPATH!="/devices/pci0000:00/0000:00:02.0/drm/*", TAG+="switcheroo-discrete-gpu" --- -2.46.0 - diff --git a/anda/apps/switcheroo-control/0009-main-use-Discrete-key-in-switcherooctl.patch b/anda/apps/switcheroo-control/0009-main-use-Discrete-key-in-switcherooctl.patch deleted file mode 100644 index abb8fdf525..0000000000 --- a/anda/apps/switcheroo-control/0009-main-use-Discrete-key-in-switcherooctl.patch +++ /dev/null @@ -1,34 +0,0 @@ -From 55db3aeaeb962952881f73e94432f750cfb64fc8 Mon Sep 17 00:00:00 2001 -From: Jan200101 -Date: Thu, 15 Feb 2024 16:24:00 +0100 -Subject: [PATCH 09/10] main: use Discrete key in switcherooctl - -Signed-off-by: Jan200101 ---- - src/switcherooctl.in | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/src/switcherooctl.in b/src/switcherooctl.in -index 96c21cc..c0e3f07 100755 ---- a/src/switcherooctl.in -+++ b/src/switcherooctl.in -@@ -77,6 +77,7 @@ def print_gpu(gpu, index): - print('Device:', index) - print(' Name: ', gpu['Name']) - print(' Default: ', "yes" if gpu['Default'] else "no") -+ print(' Discrete: ', "yes" if gpu['Discrete'] else "no") - print(' Environment:', env_to_str(gpu['Environment'])) - - def _list(): -@@ -126,7 +127,7 @@ def get_discrete_gpu(): - return None - - try: -- gpu = next(gpu for gpu in gpus if not gpu['Default']) -+ gpu = next(gpu for gpu in gpus if gpu['Discrete']) - except StopIteration: - return None - else: --- -2.46.0 - diff --git a/anda/apps/switcheroo-control/0010-main-use-new-GPU-list-on-uevent.patch b/anda/apps/switcheroo-control/0010-main-use-new-GPU-list-on-uevent.patch deleted file mode 100644 index 9f55d398e7..0000000000 --- a/anda/apps/switcheroo-control/0010-main-use-new-GPU-list-on-uevent.patch +++ /dev/null @@ -1,45 +0,0 @@ -From 4232c75fe41158bb5063d630d36b3ffd6a8a57ec Mon Sep 17 00:00:00 2001 -From: Jan200101 -Date: Fri, 6 Sep 2024 22:31:56 +0200 -Subject: [PATCH 10/10] main: use new GPU list on uevent the amount of GPUs may - still be the same but underlying attributes may have changed On the ASUS TUF - Dash F15 running Fedora 40 6.10.7-200.fc40.x86_64 the udev tags are not - applied at the time switcheroo-control starts but at a later uevent they are - correct. Memory gets allocated anyways to check if the GPU count has changed, - so this shouldn't affect memory usage. - -Signed-off-by: Jan200101 ---- - src/switcheroo-control.c | 16 ++++++---------- - 1 file changed, 6 insertions(+), 10 deletions(-) - -diff --git a/src/switcheroo-control.c b/src/switcheroo-control.c -index 0f6a548..01954c7 100644 ---- a/src/switcheroo-control.c -+++ b/src/switcheroo-control.c -@@ -438,16 +438,12 @@ uevent_cb (GUdevClient *client, - - cards = get_drm_cards (data); - num_gpus = cards->len; -- if (num_gpus != data->num_gpus) { -- g_debug ("GPUs added or removed (old: %d new: %d)", -- data->num_gpus, num_gpus); -- g_ptr_array_free (data->cards, TRUE); -- data->cards = cards; -- data->num_gpus = cards->len; -- send_dbus_event (data); -- } else { -- g_ptr_array_free (cards, TRUE); -- } -+ g_debug ("GPUs updated (old: %d new: %d)", -+ data->num_gpus, num_gpus); -+ g_ptr_array_free (data->cards, TRUE); -+ data->cards = cards; -+ data->num_gpus = cards->len; -+ send_dbus_event (data); - } - - static void --- -2.46.0 - diff --git a/anda/apps/switcheroo-control/discrete.patch b/anda/apps/switcheroo-control/discrete.patch new file mode 100644 index 0000000000..bc3e696e90 --- /dev/null +++ b/anda/apps/switcheroo-control/discrete.patch @@ -0,0 +1,1335 @@ +From 44046bfbcb30a19c45416113a2a82a4d17a1a998 Mon Sep 17 00:00:00 2001 +From: Jan200101 +Date: Mon, 14 Aug 2023 14:06:45 +0200 +Subject: [PATCH 01/13] main: update GPUs comment for dbus property + +Signed-off-by: Jan200101 +--- + src/net.hadess.SwitcherooControl.xml | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/net.hadess.SwitcherooControl.xml b/src/net.hadess.SwitcherooControl.xml +index e52bc1a..59a8896 100644 +--- a/src/net.hadess.SwitcherooControl.xml ++++ b/src/net.hadess.SwitcherooControl.xml +@@ -38,7 +38,8 @@ + will contain a user-facing name for the GPU, the "Environment" (as) key will + contain an array of even number of strings, each being an environment + variable to set to use the GPU, followed by its value, the "Default" (b) key +- will tag the default (usually integrated) GPU. ++ will tag the default GPU, the "Discrete" (b) key tags if the GPU is a ++ dedicated component. + --> + + +-- +2.49.0 + + +From 4f31415cb61a50c2bcba1510a7511518417d0970 Mon Sep 17 00:00:00 2001 +From: Jan200101 +Date: Mon, 11 Sep 2023 15:21:46 +0200 +Subject: [PATCH 02/13] main: add Discrete key + +Signed-off-by: Jan200101 +--- + .gitlab-ci.yml | 1 + + data/30-discrete-gpu.rules.in | 3 + + data/meson.build | 7 ++ + meson.build | 9 +++ + meson_options.txt | 24 +++++++ + src/discrete-detection/amdgpu.c | 46 +++++++++++++ + src/discrete-detection/meson.build | 18 +++++ + src/discrete-detection/nouveau.c | 105 +++++++++++++++++++++++++++++ + src/meson.build | 4 +- + src/switcheroo-control.c | 16 +++++ + 10 files changed, 232 insertions(+), 1 deletion(-) + create mode 100644 data/30-discrete-gpu.rules.in + create mode 100644 src/discrete-detection/amdgpu.c + create mode 100644 src/discrete-detection/meson.build + create mode 100644 src/discrete-detection/nouveau.c + +diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml +index a6aa3c7..a09fe20 100644 +--- a/.gitlab-ci.yml ++++ b/.gitlab-ci.yml +@@ -3,6 +3,7 @@ image: fedora:rawhide + variables: + DEPENDENCIES: glib2-devel + libgudev-devel ++ libdrm-devel + gtk-doc + gcc + gcc-c++ +diff --git a/data/30-discrete-gpu.rules.in b/data/30-discrete-gpu.rules.in +new file mode 100644 +index 0000000..a803ed4 +--- /dev/null ++++ b/data/30-discrete-gpu.rules.in +@@ -0,0 +1,3 @@ ++DRIVERS=="amdgpu", SUBSYSTEM=="drm", PROGRAM="@libexecdir@/check-discrete-amdgpu $env{DEVNAME}", TAG+="switcheroo-discrete-gpu" ++DRIVERS=="nouveau", SUBSYSTEM=="drm", PROGRAM="@libexecdir@/check-discrete-nouveau $env{DEVNAME}", TAG+="switcheroo-discrete-gpu" ++DRIVERS=="nvidia", TAG+="switcheroo-discrete-gpu" +diff --git a/data/meson.build b/data/meson.build +index 85e5c93..38cf96c 100644 +--- a/data/meson.build ++++ b/data/meson.build +@@ -17,3 +17,10 @@ install_data( + '30-pci-intel-gpu.hwdb', + install_dir: hwdb_dir, + ) ++ ++configure_file( ++ input: '30-discrete-gpu.rules.in', ++ output: '30-discrete-gpu.rules', ++ configuration: data_conf, ++ install_dir: rules_dir, ++) +diff --git a/meson.build b/meson.build +index b8f4bff..b3aaf0c 100644 +--- a/meson.build ++++ b/meson.build +@@ -20,6 +20,9 @@ gnome = import('gnome') + glib = dependency('glib-2.0', version: '>= 2.56.0') + gio = dependency('gio-2.0', version: '>= 2.56.0') + gudev = dependency('gudev-1.0', version: '>= 232') ++libdrm = dependency('libdrm', version: '>= 2.4.97', required: get_option('libdrm')) ++libdrm_nouveau = dependency('libdrm_nouveau', version: '>= 2.4.97', required: get_option('libdrm_nouveau')) ++libdrm_amdgpu = dependency('libdrm_amdgpu', version: '>= 2.4.97', required: get_option('libdrm_amdgpu')) + + systemd_systemunitdir = get_option('systemdsystemunitdir') + if systemd_systemunitdir == '' +@@ -32,6 +35,12 @@ if hwdb_dir == '' + hwdb_dir = udevdir / 'hwdb.d' + endif + ++rules_dir = get_option('rulesdir') ++if rules_dir == '' ++ udevdir = dependency('udev').get_pkgconfig_variable('udevdir') ++ rules_dir = udevdir / 'rules.d' ++endif ++ + # Make like license available in the build root for docs + configure_file( + input: 'COPYING', +diff --git a/meson_options.txt b/meson_options.txt +index c8d9619..b8d671a 100644 +--- a/meson_options.txt ++++ b/meson_options.txt +@@ -10,6 +10,12 @@ option('hwdbdir', + description: 'Directory for hwdb files', + ) + ++option('rulesdir', ++ type: 'string', ++ value: '', ++ description: 'Directory for ruke files', ++) ++ + option('gtk_doc', + type: 'boolean', + value: false, +@@ -21,3 +27,21 @@ option('tests', + type: 'boolean', + value: false + ) ++ ++option('libdrm', ++ description: 'Whether libdrm should be used to probe GPUs', ++ type: 'feature', ++ value: 'auto' ++) ++ ++option('libdrm_nouveau', ++ description: 'Whether libdrm_nouveau should be used to probe Nvidia GPUs', ++ type: 'feature', ++ value: 'auto' ++) ++ ++option('libdrm_amdgpu', ++ description: 'Whether libdrm_amdgpu should be used to probe AMD GPUs', ++ type: 'feature', ++ value: 'auto' ++) +diff --git a/src/discrete-detection/amdgpu.c b/src/discrete-detection/amdgpu.c +new file mode 100644 +index 0000000..2d9804f +--- /dev/null ++++ b/src/discrete-detection/amdgpu.c +@@ -0,0 +1,46 @@ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++typedef int handle; ++G_DEFINE_AUTO_CLEANUP_FREE_FUNC(handle, close, -1) ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(amdgpu_device_handle, free); ++ ++int main(int argc, char** argv) ++{ ++ if (argc < 2) ++ { ++ puts ("check-discrete-amdgpu [DEVNAME]"); ++ return EXIT_FAILURE; ++ } ++ ++ const char *devname; ++ g_auto(handle) fd = -1; ++ g_autoptr(GUdevDevice) parent = NULL; ++ struct drm_amdgpu_info_device device_info = {0}; ++ amdgpu_device_handle device = NULL; ++ uint32_t drm_major, drm_minor; ++ ++ devname = argv[1]; ++ fd = open (devname, O_RDWR); ++ if (fd < 0) ++ return EXIT_FAILURE; ++ ++ if (amdgpu_device_initialize (fd, &drm_major, &drm_minor, &device)) ++ return EXIT_FAILURE; ++ ++ if (amdgpu_query_info (device, AMDGPU_INFO_DEV_INFO, sizeof(device_info), &device_info)) ++ return EXIT_FAILURE; ++ ++ /* AMDGPU_IDS_FLAGS_FUSION is set for all APUs */ ++ if (device_info.ids_flags & AMDGPU_IDS_FLAGS_FUSION) ++ return EXIT_FAILURE; ++ ++ return EXIT_SUCCESS; ++} +diff --git a/src/discrete-detection/meson.build b/src/discrete-detection/meson.build +new file mode 100644 +index 0000000..3a6c03f +--- /dev/null ++++ b/src/discrete-detection/meson.build +@@ -0,0 +1,18 @@ ++ ++if libdrm_amdgpu.found() ++ executable('check-discrete-amdgpu', ++ files('amdgpu.c'), ++ dependencies: deps, ++ install: true, ++ install_dir: libexecdir, ++ ) ++endif ++ ++if libdrm.found() and libdrm_nouveau.found() ++ executable('check-discrete-nouveau', ++ files('nouveau.c'), ++ dependencies: deps, ++ install: true, ++ install_dir: libexecdir, ++ ) ++endif +diff --git a/src/discrete-detection/nouveau.c b/src/discrete-detection/nouveau.c +new file mode 100644 +index 0000000..0a1f220 +--- /dev/null ++++ b/src/discrete-detection/nouveau.c +@@ -0,0 +1,105 @@ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++typedef int handle; ++G_DEFINE_AUTO_CLEANUP_FREE_FUNC(handle, close, -1) ++ ++int main(int argc, char** argv) ++{ ++ if (argc < 2) ++ { ++ puts ("check-discrete-nouveau [DEVNAME]"); ++ return EXIT_FAILURE; ++ } ++ ++ const char *devname; ++ g_auto(handle) fd = -1; ++ ++ devname = argv[1]; ++ fd = open (devname, O_RDWR); ++ if (fd < 0) ++ return EXIT_FAILURE; ++ ++ g_autofree void *device = malloc(352); ++ ++ /* Init device */ ++ { ++ struct { ++ struct nvif_ioctl_v0 ioctl; ++ struct nvif_ioctl_new_v0 new; ++ struct nv_device_v0 dev; ++ } init_args = { ++ .ioctl = { ++ .object = 0, ++ .owner = NVIF_IOCTL_V0_OWNER_ANY, ++ .route = 0x00, ++ .type = NVIF_IOCTL_V0_NEW, ++ .version = 0, ++ }, ++ .new = { ++ .handle = 0, ++ .object = (uintptr_t)device, ++ .oclass = NV_DEVICE, ++ .route = NVIF_IOCTL_V0_ROUTE_NVIF, ++ .token = (uintptr_t)device, ++ .version = 0, ++ }, ++ .dev = { ++ .device = ~0ULL, ++ }, ++ }; ++ ++ if (drmCommandWrite (fd, DRM_NOUVEAU_NVIF, &init_args, sizeof(init_args))) ++ return EXIT_FAILURE; ++ } ++ ++ /* Query device info */ ++ struct { ++ struct nvif_ioctl_v0 ioctl; ++ struct nvif_ioctl_mthd_v0 mthd; ++ struct nv_device_info_v0 info; ++ } args = { ++ .ioctl = { ++ .object = (uintptr_t)device, ++ .owner = NVIF_IOCTL_V0_OWNER_ANY, ++ .route = 0x00, ++ .type = NVIF_IOCTL_V0_MTHD, ++ .version = 0, ++ }, ++ .mthd = { ++ .method = NV_DEVICE_V0_INFO, ++ .version = 0, ++ }, ++ .info = { ++ .version = 0, ++ }, ++ }; ++ ++ if (drmCommandWriteRead (fd, DRM_NOUVEAU_NVIF, &args, sizeof(args))) ++ return EXIT_FAILURE; ++ ++ ++ switch (args.info.platform) ++ { ++ case NV_DEVICE_INFO_V0_IGP: ++ case NV_DEVICE_INFO_V0_SOC: ++ return EXIT_FAILURE; ++ ++ case NV_DEVICE_INFO_V0_PCI: ++ case NV_DEVICE_INFO_V0_AGP: ++ case NV_DEVICE_INFO_V0_PCIE: ++ default: ++ return EXIT_SUCCESS; ++ } ++ return EXIT_FAILURE; ++} +diff --git a/src/meson.build b/src/meson.build +index ab3a77d..da4267f 100644 +--- a/src/meson.build ++++ b/src/meson.build +@@ -1,4 +1,4 @@ +-deps = [glib, gio, gudev] ++deps = [glib, gio, gudev, libdrm, libdrm_nouveau, libdrm_amdgpu] + + sources = [ + 'info-cleanup.c', +@@ -34,3 +34,5 @@ configure_file( + configuration: switcherooctl_conf, + install_dir: get_option('bindir') + ) ++ ++subdir('discrete-detection') +diff --git a/src/switcheroo-control.c b/src/switcheroo-control.c +index abd8154..e407bfb 100644 +--- a/src/switcheroo-control.c ++++ b/src/switcheroo-control.c +@@ -31,6 +31,7 @@ typedef struct { + char *name; + GPtrArray *env; + gboolean is_default; ++ gboolean is_discrete; + } CardData; + + typedef struct { +@@ -94,6 +95,8 @@ build_gpus_variant (ControlData *data) + g_variant_new_strv ((const gchar * const *) card->env->pdata, card->env->len)); + g_variant_builder_add (&asv_builder, "{sv}", "Default", + g_variant_new_boolean (card->is_default)); ++ g_variant_builder_add (&asv_builder, "{sv}", "Discrete", ++ g_variant_new_boolean (card->is_discrete)); + + g_variant_builder_add (&builder, "a{sv}", &asv_builder); + } +@@ -312,6 +315,18 @@ get_card_is_default (GUdevDevice *d) + return g_udev_device_get_sysfs_attr_as_boolean (parent, "boot_vga"); + } + ++static gboolean ++get_card_is_discrete (GUdevDevice *d) ++{ ++ const char * const * tags; ++ g_autoptr (GUdevDevice) platform_device = NULL; ++ ++ tags = g_udev_device_get_tags (d); ++ if (tags && g_strv_contains (tags, "switcheroo-discrete-gpu")) ++ return TRUE; ++ return FALSE; ++} ++ + static CardData * + get_card_data (GUdevClient *client, + GUdevDevice *d) +@@ -328,6 +343,7 @@ get_card_data (GUdevClient *client, + data->name = get_card_name (d); + data->env = env; + data->is_default = get_card_is_default (d); ++ data->is_discrete = get_card_is_discrete (d); + + return data; + } +-- +2.49.0 + + +From 1b115ed72e03ff1169cbfddd79ef10890baca133 Mon Sep 17 00:00:00 2001 +From: Jan200101 +Date: Tue, 12 Sep 2023 15:53:40 +0200 +Subject: [PATCH 03/13] tests: fix integration tests without UMockdev + `gi.require_version` throws ValueError if the dependency cannot be found + +Signed-off-by: Jan200101 +--- + tests/integration-test.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tests/integration-test.py b/tests/integration-test.py +index d8dea16..e3dd996 100755 +--- a/tests/integration-test.py ++++ b/tests/integration-test.py +@@ -37,7 +37,7 @@ except ImportError as e: + try: + gi.require_version('UMockdev', '1.0') + from gi.repository import UMockdev +-except ImportError: ++except (ImportError, ValueError): + sys.stderr.write('Skipping tests, umockdev not available (https://github.com/martinpitt/umockdev)\n') + sys.exit(0) + +-- +2.49.0 + + +From d933e96bdb15679ae7653f929461982aa66973ba Mon Sep 17 00:00:00 2001 +From: Jan200101 +Date: Tue, 12 Sep 2023 15:58:16 +0200 +Subject: [PATCH 04/13] tests: add tests for discrete detection with mock libs + Both tests have 4 different ways of testing: - Invalid Device - Unexpected + Device - Non Discrete GPU (iGPU/APU) - Discrete GPU + +Signed-off-by: Jan200101 +--- + src/discrete-detection/meson.build | 4 +- + tests/discrete-detection/libdrm_amdgpu_mock.c | 57 +++++++++++++ + .../discrete-detection/libdrm_nouveau_mock.c | 68 ++++++++++++++++ + tests/discrete-detection/meson.build | 80 +++++++++++++++++++ + tests/meson.build | 2 + + 5 files changed, 209 insertions(+), 2 deletions(-) + create mode 100644 tests/discrete-detection/libdrm_amdgpu_mock.c + create mode 100644 tests/discrete-detection/libdrm_nouveau_mock.c + create mode 100644 tests/discrete-detection/meson.build + +diff --git a/src/discrete-detection/meson.build b/src/discrete-detection/meson.build +index 3a6c03f..8eb8437 100644 +--- a/src/discrete-detection/meson.build ++++ b/src/discrete-detection/meson.build +@@ -1,6 +1,6 @@ + + if libdrm_amdgpu.found() +- executable('check-discrete-amdgpu', ++ amdgpu_discrete = executable('check-discrete-amdgpu', + files('amdgpu.c'), + dependencies: deps, + install: true, +@@ -9,7 +9,7 @@ if libdrm_amdgpu.found() + endif + + if libdrm.found() and libdrm_nouveau.found() +- executable('check-discrete-nouveau', ++ nouveau_discrete = executable('check-discrete-nouveau', + files('nouveau.c'), + dependencies: deps, + install: true, +diff --git a/tests/discrete-detection/libdrm_amdgpu_mock.c b/tests/discrete-detection/libdrm_amdgpu_mock.c +new file mode 100644 +index 0000000..20fa4aa +--- /dev/null ++++ b/tests/discrete-detection/libdrm_amdgpu_mock.c +@@ -0,0 +1,57 @@ ++#include ++#include ++#include ++ ++#include ++ ++enum { ++ OTHER_GPU, ++ AMD_APU, ++ AMD_GPU, ++}; ++ ++/* Mock open(2) so we can test multiple devices configurations */ ++int open(const char *pathname, int flags) ++{ ++ if (!strcmp(pathname, "OTHER_GPU")) ++ return OTHER_GPU; ++ if (!strcmp (pathname, "AMD_APU")) ++ return AMD_APU; ++ if (!strcmp (pathname, "AMD_GPU")) ++ return AMD_GPU; ++ ++ return -1; ++} ++ ++/* open64 may be used for large file support */ ++int open64(const char *pathname, int flags) ++{ ++ return open (pathname, flags); ++} ++ ++int amdgpu_device_initialize(int fd, uint32_t *major_version, uint32_t *minor_version, int *device_handle) ++{ ++ // Store the fd in the device handle for access in query_info ++ *device_handle = fd; ++ ++ if (fd != AMD_GPU && fd != AMD_APU) ++ return 1; ++ ++ return 0; ++} ++ ++int amdgpu_query_info(int device_handle, unsigned info_id, unsigned size, void *value) ++{ ++ struct drm_amdgpu_info_device* device_info = value; ++ ++ if (device_handle == AMD_GPU) { ++ device_info->ids_flags = 0; ++ return 0; ++ } ++ if (device_handle == AMD_APU) { ++ device_info->ids_flags = AMDGPU_IDS_FLAGS_FUSION; ++ return 0; ++ } ++ ++ return 1; ++} +diff --git a/tests/discrete-detection/libdrm_nouveau_mock.c b/tests/discrete-detection/libdrm_nouveau_mock.c +new file mode 100644 +index 0000000..cdbfda8 +--- /dev/null ++++ b/tests/discrete-detection/libdrm_nouveau_mock.c +@@ -0,0 +1,68 @@ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++enum { ++ OTHER_GPU, ++ NVIDIA_IGPU, ++ NVIDIA_GPU, ++}; ++ ++/* Mock open(2) so we can test multiple devices configurations */ ++int open(const char *pathname, int flags) ++{ ++ if (!strcmp(pathname, "OTHER_GPU")) ++ return OTHER_GPU; ++ if (!strcmp (pathname, "NVIDIA_IGPU")) ++ return NVIDIA_IGPU; ++ if (!strcmp (pathname, "NVIDIA_GPU")) ++ return NVIDIA_GPU; ++ ++ return -1; ++} ++ ++/* open64 may be used for large file support */ ++int open64(const char *pathname, int flags) ++{ ++ return open (pathname, flags); ++} ++ ++int drmCommandWrite(int fd, unsigned long drmCommandIndex, void *data, unsigned long size) ++{ ++ if (drmCommandIndex != DRM_NOUVEAU_NVIF) ++ return 1; ++ ++ if (fd != NVIDIA_GPU && fd != NVIDIA_IGPU) ++ return 1; ++ ++ return 0; ++} ++ ++int drmCommandWriteRead(int fd, unsigned long drmCommandIndex, void *data, unsigned long size) ++{ ++ if (drmCommandIndex != DRM_NOUVEAU_NVIF) ++ return 1; ++ ++ struct { ++ struct nvif_ioctl_v0 ioctl; ++ struct nvif_ioctl_mthd_v0 mthd; ++ struct nv_device_info_v0 info; ++ } *args = data; ++ ++ if (fd == NVIDIA_GPU) { ++ args->info.platform = NV_DEVICE_INFO_V0_PCIE; ++ return 0; ++ } ++ if (fd == NVIDIA_IGPU) { ++ args->info.platform = NV_DEVICE_INFO_V0_IGP; ++ return 0; ++ } ++ ++ return 1; ++} +\ No newline at end of file +diff --git a/tests/discrete-detection/meson.build b/tests/discrete-detection/meson.build +new file mode 100644 +index 0000000..f01a014 +--- /dev/null ++++ b/tests/discrete-detection/meson.build +@@ -0,0 +1,80 @@ ++ ++if libdrm_amdgpu.found() ++ amdgpu_mock_lib = shared_library( ++ 'drm_amdgpu_mock', ++ files('libdrm_amdgpu_mock.c'), ++ dependencies: libdrm_amdgpu ++ ) ++ ++ test( ++ 'test amdgpu detection with invalid device', ++ amdgpu_discrete, ++ args: ['NO_GPU'], ++ env: environment({'LD_PRELOAD': amdgpu_mock_lib.full_path()}), ++ should_fail: true ++ ) ++ ++ test( ++ 'test amdgpu detection with non-AMD GPU', ++ amdgpu_discrete, ++ args: ['OTHER_GPU'], ++ env: environment({'LD_PRELOAD': amdgpu_mock_lib.full_path()}), ++ should_fail: true ++ ) ++ ++ test( ++ 'test amdgpu detection with AMD APU', ++ amdgpu_discrete, ++ args: ['AMD_APU'], ++ env: environment({'LD_PRELOAD': amdgpu_mock_lib.full_path()}), ++ should_fail: true ++ ) ++ ++ test( ++ 'test amdgpu detection with AMD GPU', ++ amdgpu_discrete, ++ args: ['AMD_GPU'], ++ env: environment({'LD_PRELOAD': amdgpu_mock_lib.full_path()}), ++ should_fail: false ++ ) ++endif ++ ++if libdrm.found() and libdrm_nouveau.found() ++ nouveau_mock_lib = shared_library( ++ 'drm_nouveau_mock', ++ files('libdrm_nouveau_mock.c'), ++ dependencies: [libdrm, libdrm_nouveau] ++ ) ++ ++ test( ++ 'test nouveau detection with invalid device', ++ nouveau_discrete, ++ args: ['NO_GPU'], ++ env: environment({'LD_PRELOAD': nouveau_mock_lib.full_path()}), ++ should_fail: true ++ ) ++ ++ test( ++ 'test nouveau detection with non-Nvidia GPU', ++ nouveau_discrete, ++ args: ['OTHER_GPU'], ++ env: environment({'LD_PRELOAD': nouveau_mock_lib.full_path()}), ++ should_fail: true ++ ) ++ ++ test( ++ 'test nouveau detection with Nvidia iGPU', ++ nouveau_discrete, ++ args: ['NVIDIA_IGPU'], ++ env: environment({'LD_PRELOAD': nouveau_mock_lib.full_path()}), ++ should_fail: true ++ ) ++ ++ test( ++ 'test nouveau detection with Nvidia GPU', ++ nouveau_discrete, ++ args: ['NVIDIA_GPU'], ++ env: environment({'LD_PRELOAD': nouveau_mock_lib.full_path()}), ++ should_fail: false ++ ) ++endif +\ No newline at end of file +diff --git a/tests/meson.build b/tests/meson.build +index b0b7476..61ef00c 100644 +--- a/tests/meson.build ++++ b/tests/meson.build +@@ -15,3 +15,5 @@ foreach ut: unit_tests + env: envs, + ) + endforeach ++ ++subdir('discrete-detection') +-- +2.49.0 + + +From c102b643945dc076d881497dd2ca5865938f7053 Mon Sep 17 00:00:00 2001 +From: Jan200101 +Date: Tue, 12 Sep 2023 15:57:47 +0200 +Subject: [PATCH 05/13] main: remove leftover and fix typo + +Signed-off-by: Jan200101 +--- + meson_options.txt | 2 +- + src/switcheroo-control.c | 1 - + 2 files changed, 1 insertion(+), 2 deletions(-) + +diff --git a/meson_options.txt b/meson_options.txt +index b8d671a..c77fea8 100644 +--- a/meson_options.txt ++++ b/meson_options.txt +@@ -13,7 +13,7 @@ option('hwdbdir', + option('rulesdir', + type: 'string', + value: '', +- description: 'Directory for ruke files', ++ description: 'Directory for rule files', + ) + + option('gtk_doc', +diff --git a/src/switcheroo-control.c b/src/switcheroo-control.c +index e407bfb..0f6a548 100644 +--- a/src/switcheroo-control.c ++++ b/src/switcheroo-control.c +@@ -319,7 +319,6 @@ static gboolean + get_card_is_discrete (GUdevDevice *d) + { + const char * const * tags; +- g_autoptr (GUdevDevice) platform_device = NULL; + + tags = g_udev_device_get_tags (d); + if (tags && g_strv_contains (tags, "switcheroo-discrete-gpu")) +-- +2.49.0 + + +From f764db4eb565c19ba14155791fbfced3fb5d34c8 Mon Sep 17 00:00:00 2001 +From: Jan200101 +Date: Tue, 12 Sep 2023 15:58:27 +0200 +Subject: [PATCH 06/13] main: move discrete dependencies out of main deps + +Signed-off-by: Jan200101 +--- + src/discrete-detection/meson.build | 5 +++-- + src/meson.build | 2 +- + 2 files changed, 4 insertions(+), 3 deletions(-) + +diff --git a/src/discrete-detection/meson.build b/src/discrete-detection/meson.build +index 8eb8437..353316f 100644 +--- a/src/discrete-detection/meson.build ++++ b/src/discrete-detection/meson.build +@@ -1,8 +1,9 @@ ++discrete_deps = deps + [libdrm, libdrm_nouveau, libdrm_amdgpu] + + if libdrm_amdgpu.found() + amdgpu_discrete = executable('check-discrete-amdgpu', + files('amdgpu.c'), +- dependencies: deps, ++ dependencies: discrete_deps, + install: true, + install_dir: libexecdir, + ) +@@ -11,7 +12,7 @@ endif + if libdrm.found() and libdrm_nouveau.found() + nouveau_discrete = executable('check-discrete-nouveau', + files('nouveau.c'), +- dependencies: deps, ++ dependencies: discrete_deps, + install: true, + install_dir: libexecdir, + ) +diff --git a/src/meson.build b/src/meson.build +index da4267f..22d69e7 100644 +--- a/src/meson.build ++++ b/src/meson.build +@@ -1,4 +1,4 @@ +-deps = [glib, gio, gudev, libdrm, libdrm_nouveau, libdrm_amdgpu] ++deps = [glib, gio, gudev] + + sources = [ + 'info-cleanup.c', +-- +2.49.0 + + +From d2ecc29469d5572fd171926c9d1dbb1b851c7b09 Mon Sep 17 00:00:00 2001 +From: Jan200101 +Date: Tue, 12 Sep 2023 17:12:00 +0200 +Subject: [PATCH 07/13] main: use glib for discrete command-line arguments + +Signed-off-by: Jan200101 +--- + src/discrete-detection/amdgpu.c | 29 ++++++++++++++++++++--------- + src/discrete-detection/nouveau.c | 29 ++++++++++++++++++++--------- + 2 files changed, 40 insertions(+), 18 deletions(-) + +diff --git a/src/discrete-detection/amdgpu.c b/src/discrete-detection/amdgpu.c +index 2d9804f..5a9a4ab 100644 +--- a/src/discrete-detection/amdgpu.c ++++ b/src/discrete-detection/amdgpu.c +@@ -1,9 +1,10 @@ +- + #include + #include + #include ++#include + #include + #include ++#include + + #include + #include +@@ -12,22 +13,32 @@ typedef int handle; + G_DEFINE_AUTO_CLEANUP_FREE_FUNC(handle, close, -1) + G_DEFINE_AUTOPTR_CLEANUP_FUNC(amdgpu_device_handle, free); + +-int main(int argc, char** argv) ++int main (int argc, char** argv) + { +- if (argc < 2) +- { +- puts ("check-discrete-amdgpu [DEVNAME]"); +- return EXIT_FAILURE; +- } +- + const char *devname; + g_auto(handle) fd = -1; +- g_autoptr(GUdevDevice) parent = NULL; + struct drm_amdgpu_info_device device_info = {0}; + amdgpu_device_handle device = NULL; + uint32_t drm_major, drm_minor; ++ g_autoptr(GOptionContext) option_context = NULL; ++ g_autoptr(GError) error = NULL; + ++ setlocale (LC_ALL, ""); ++ option_context = g_option_context_new (""); ++ ++ if (!g_option_context_parse (option_context, &argc, &argv, &error)) { ++ g_print ("Failed to parse arguments: %s\n", error->message); ++ return EXIT_FAILURE; ++ } ++ ++ if (argc < 2) ++ { ++ g_print ("%s\n", g_option_context_get_help (option_context, TRUE, NULL)); ++ return EXIT_FAILURE; ++ } + devname = argv[1]; ++; ++ + fd = open (devname, O_RDWR); + if (fd < 0) + return EXIT_FAILURE; +diff --git a/src/discrete-detection/nouveau.c b/src/discrete-detection/nouveau.c +index 0a1f220..1d61cbb 100644 +--- a/src/discrete-detection/nouveau.c ++++ b/src/discrete-detection/nouveau.c +@@ -1,9 +1,10 @@ +- + #include + #include + #include ++#include + #include + #include ++#include + + #include + #include +@@ -14,23 +15,34 @@ + typedef int handle; + G_DEFINE_AUTO_CLEANUP_FREE_FUNC(handle, close, -1) + +-int main(int argc, char** argv) ++int main (int argc, char** argv) + { ++ const gchar *devname = NULL; ++ g_auto(handle) fd = -1; ++ g_autofree void *device = NULL; ++ g_autoptr(GOptionContext) option_context = NULL; ++ g_autoptr(GError) error = NULL; ++ ++ setlocale (LC_ALL, ""); ++ option_context = g_option_context_new (""); ++ ++ if (!g_option_context_parse (option_context, &argc, &argv, &error)) { ++ g_print ("Failed to parse arguments: %s\n", error->message); ++ return EXIT_FAILURE; ++ } ++ + if (argc < 2) + { +- puts ("check-discrete-nouveau [DEVNAME]"); ++ g_print ("%s\n", g_option_context_get_help (option_context, TRUE, NULL)); + return EXIT_FAILURE; + } +- +- const char *devname; +- g_auto(handle) fd = -1; +- + devname = argv[1]; ++ + fd = open (devname, O_RDWR); + if (fd < 0) + return EXIT_FAILURE; + +- g_autofree void *device = malloc(352); ++ device = malloc(352); + + /* Init device */ + { +@@ -88,7 +100,6 @@ int main(int argc, char** argv) + if (drmCommandWriteRead (fd, DRM_NOUVEAU_NVIF, &args, sizeof(args))) + return EXIT_FAILURE; + +- + switch (args.info.platform) + { + case NV_DEVICE_INFO_V0_IGP: +-- +2.49.0 + + +From 462b09f02de37dfd2965d23cc7c4137bcf45a4ae Mon Sep 17 00:00:00 2001 +From: Jan200101 +Date: Wed, 14 Feb 2024 20:25:42 +0100 +Subject: [PATCH 08/13] main: add udev rule for i915 checking a lot of systems + has shown that the intel iGPU will always be available at `0000:00:02.0`. + Using ID_PATH would have been cleaner, but I couldn't get it to work. + +Signed-off-by: Jan200101 +--- + data/30-discrete-gpu.rules.in | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/data/30-discrete-gpu.rules.in b/data/30-discrete-gpu.rules.in +index a803ed4..f30f315 100644 +--- a/data/30-discrete-gpu.rules.in ++++ b/data/30-discrete-gpu.rules.in +@@ -1,3 +1,4 @@ + DRIVERS=="amdgpu", SUBSYSTEM=="drm", PROGRAM="@libexecdir@/check-discrete-amdgpu $env{DEVNAME}", TAG+="switcheroo-discrete-gpu" + DRIVERS=="nouveau", SUBSYSTEM=="drm", PROGRAM="@libexecdir@/check-discrete-nouveau $env{DEVNAME}", TAG+="switcheroo-discrete-gpu" +-DRIVERS=="nvidia", TAG+="switcheroo-discrete-gpu" ++DRIVERS=="nvidia", SUBSYSTEM=="drm", TAG+="switcheroo-discrete-gpu" ++DRIVERS=="i915", SUBSYSTEM=="drm", DEVPATH!="/devices/pci0000:00/0000:00:02.0/drm/*", TAG+="switcheroo-discrete-gpu" +-- +2.49.0 + + +From 55db3aeaeb962952881f73e94432f750cfb64fc8 Mon Sep 17 00:00:00 2001 +From: Jan200101 +Date: Thu, 15 Feb 2024 16:24:00 +0100 +Subject: [PATCH 09/13] main: use Discrete key in switcherooctl + +Signed-off-by: Jan200101 +--- + src/switcherooctl.in | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/switcherooctl.in b/src/switcherooctl.in +index 96c21cc..c0e3f07 100755 +--- a/src/switcherooctl.in ++++ b/src/switcherooctl.in +@@ -77,6 +77,7 @@ def print_gpu(gpu, index): + print('Device:', index) + print(' Name: ', gpu['Name']) + print(' Default: ', "yes" if gpu['Default'] else "no") ++ print(' Discrete: ', "yes" if gpu['Discrete'] else "no") + print(' Environment:', env_to_str(gpu['Environment'])) + + def _list(): +@@ -126,7 +127,7 @@ def get_discrete_gpu(): + return None + + try: +- gpu = next(gpu for gpu in gpus if not gpu['Default']) ++ gpu = next(gpu for gpu in gpus if gpu['Discrete']) + except StopIteration: + return None + else: +-- +2.49.0 + + +From 4232c75fe41158bb5063d630d36b3ffd6a8a57ec Mon Sep 17 00:00:00 2001 +From: Jan200101 +Date: Fri, 6 Sep 2024 22:31:56 +0200 +Subject: [PATCH 10/13] main: use new GPU list on uevent the amount of GPUs may + still be the same but underlying attributes may have changed On the ASUS TUF + Dash F15 running Fedora 40 6.10.7-200.fc40.x86_64 the udev tags are not + applied at the time switcheroo-control starts but at a later uevent they are + correct. Memory gets allocated anyways to check if the GPU count has changed, + so this shouldn't affect memory usage. + +Signed-off-by: Jan200101 +--- + src/switcheroo-control.c | 16 ++++++---------- + 1 file changed, 6 insertions(+), 10 deletions(-) + +diff --git a/src/switcheroo-control.c b/src/switcheroo-control.c +index 0f6a548..01954c7 100644 +--- a/src/switcheroo-control.c ++++ b/src/switcheroo-control.c +@@ -438,16 +438,12 @@ uevent_cb (GUdevClient *client, + + cards = get_drm_cards (data); + num_gpus = cards->len; +- if (num_gpus != data->num_gpus) { +- g_debug ("GPUs added or removed (old: %d new: %d)", +- data->num_gpus, num_gpus); +- g_ptr_array_free (data->cards, TRUE); +- data->cards = cards; +- data->num_gpus = cards->len; +- send_dbus_event (data); +- } else { +- g_ptr_array_free (cards, TRUE); +- } ++ g_debug ("GPUs updated (old: %d new: %d)", ++ data->num_gpus, num_gpus); ++ g_ptr_array_free (data->cards, TRUE); ++ data->cards = cards; ++ data->num_gpus = cards->len; ++ send_dbus_event (data); + } + + static void +-- +2.49.0 + + +From ad09f908cc9e56cccfc946b285f2e7ddd014c587 Mon Sep 17 00:00:00 2001 +From: Jan200101 +Date: Mon, 28 Apr 2025 21:41:47 +0200 +Subject: [PATCH 11/13] main: add xe discrete detection + +Signed-off-by: Jan200101 +--- + data/30-discrete-gpu.rules.in | 1 + + meson.build | 1 + + meson_options.txt | 6 +++ + src/discrete-detection/meson.build | 9 +++++ + src/discrete-detection/xe.c | 63 ++++++++++++++++++++++++++++++ + 5 files changed, 80 insertions(+) + create mode 100644 src/discrete-detection/xe.c + +diff --git a/data/30-discrete-gpu.rules.in b/data/30-discrete-gpu.rules.in +index f30f315..ab60bf2 100644 +--- a/data/30-discrete-gpu.rules.in ++++ b/data/30-discrete-gpu.rules.in +@@ -2,3 +2,4 @@ DRIVERS=="amdgpu", SUBSYSTEM=="drm", PROGRAM="@libexecdir@/check-discrete-amdgpu + DRIVERS=="nouveau", SUBSYSTEM=="drm", PROGRAM="@libexecdir@/check-discrete-nouveau $env{DEVNAME}", TAG+="switcheroo-discrete-gpu" + DRIVERS=="nvidia", SUBSYSTEM=="drm", TAG+="switcheroo-discrete-gpu" + DRIVERS=="i915", SUBSYSTEM=="drm", DEVPATH!="/devices/pci0000:00/0000:00:02.0/drm/*", TAG+="switcheroo-discrete-gpu" ++DRIVERS=="xe", SUBSYSTEM=="drm", PROGRAM="@libexecdir@/check-discrete-xe $env{DEVNAME}", TAG+="switcheroo-discrete-gpu" +diff --git a/meson.build b/meson.build +index b3aaf0c..31dfa95 100644 +--- a/meson.build ++++ b/meson.build +@@ -23,6 +23,7 @@ gudev = dependency('gudev-1.0', version: '>= 232') + libdrm = dependency('libdrm', version: '>= 2.4.97', required: get_option('libdrm')) + libdrm_nouveau = dependency('libdrm_nouveau', version: '>= 2.4.97', required: get_option('libdrm_nouveau')) + libdrm_amdgpu = dependency('libdrm_amdgpu', version: '>= 2.4.97', required: get_option('libdrm_amdgpu')) ++drm_xe = cc.has_header('drm/xe_drm.h', required: get_option('drm_xe')) + + systemd_systemunitdir = get_option('systemdsystemunitdir') + if systemd_systemunitdir == '' +diff --git a/meson_options.txt b/meson_options.txt +index c77fea8..058dbdd 100644 +--- a/meson_options.txt ++++ b/meson_options.txt +@@ -45,3 +45,9 @@ option('libdrm_amdgpu', + type: 'feature', + value: 'auto' + ) ++ ++option('drm_xe', ++ description: 'Whether the kernel provided xe drm header should be used to probe Intel GPUs', ++ type: 'feature', ++ value: 'auto' ++) +diff --git a/src/discrete-detection/meson.build b/src/discrete-detection/meson.build +index 353316f..7e0b324 100644 +--- a/src/discrete-detection/meson.build ++++ b/src/discrete-detection/meson.build +@@ -17,3 +17,12 @@ if libdrm.found() and libdrm_nouveau.found() + install_dir: libexecdir, + ) + endif ++ ++if drm_xe ++ xe_discrete = executable('check-discrete-xe', ++ files('xe.c'), ++ dependencies: deps, ++ install: true, ++ install_dir: libexecdir, ++ ) ++endif +diff --git a/src/discrete-detection/xe.c b/src/discrete-detection/xe.c +new file mode 100644 +index 0000000..5ee6ede +--- /dev/null ++++ b/src/discrete-detection/xe.c +@@ -0,0 +1,63 @@ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++typedef int handle; ++G_DEFINE_AUTO_CLEANUP_FREE_FUNC(handle, close, -1) ++ ++int main (int argc, char** argv) ++{ ++ const char *devname; ++ g_auto(handle) fd = -1; ++ g_autofree struct drm_xe_query_config *config = NULL; ++ g_autoptr(GOptionContext) option_context = NULL; ++ g_autoptr(GError) error = NULL; ++ ++ setlocale (LC_ALL, ""); ++ option_context = g_option_context_new (""); ++ ++ if (!g_option_context_parse (option_context, &argc, &argv, &error)) { ++ g_print ("Failed to parse arguments: %s\n", error->message); ++ return EXIT_FAILURE; ++ } ++ ++ if (argc < 2) ++ { ++ g_print ("%s\n", g_option_context_get_help (option_context, TRUE, NULL)); ++ return EXIT_FAILURE; ++ } ++ devname = argv[1]; ++ ++ fd = open (devname, O_RDWR); ++ if (fd < 0) ++ return EXIT_FAILURE; ++ ++ struct drm_xe_device_query query = { ++ .extensions = 0, ++ .query = DRM_XE_DEVICE_QUERY_CONFIG, ++ .size = 0, ++ .data = 0, ++ }; ++ ++ if (ioctl(fd, DRM_IOCTL_XE_DEVICE_QUERY, &query)) ++ return EXIT_FAILURE; ++ ++ config = malloc(query.size); ++ if (!config) ++ return EXIT_FAILURE; ++ ++ query.data = (uintptr_t)config; ++ if (ioctl(fd, DRM_IOCTL_XE_DEVICE_QUERY, &query)) ++ return EXIT_FAILURE; ++ ++ // if the GPU has dedicated vram then its discrrete ++ if (config->info[DRM_XE_QUERY_CONFIG_FLAGS] & DRM_XE_QUERY_CONFIG_FLAG_HAS_VRAM) ++ return EXIT_SUCCESS; ++ ++ return EXIT_FAILURE; ++ ++} +-- +2.49.0 + + +From cefec04bfc28a32bbec1e5a25999a768a6a7bf9c Mon Sep 17 00:00:00 2001 +From: Jan200101 +Date: Mon, 28 Apr 2025 21:39:48 +0200 +Subject: [PATCH 12/13] tests: fix switcherooctl output test + +Signed-off-by: Jan200101 +--- + tests/integration-test.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tests/integration-test.py b/tests/integration-test.py +index e3dd996..5b4b4bb 100755 +--- a/tests/integration-test.py ++++ b/tests/integration-test.py +@@ -512,7 +512,7 @@ class Tests(dbusmock.DBusTestCase): + + out = subprocess.run([tool_path], capture_output=True) + self.assertEqual(out.returncode, 0, "'switcherooctl' call failed") +- self.assertEqual(out.stdout, b'Device: 0\n Name: Intel\xc2\xae UHD Graphics 620 (Kabylake GT2)\n Default: yes\n Environment: DRI_PRIME=pci-0000_00_02_0\n\nDevice: 1\n Name: GM108M [GeForce 930MX]\n Default: no\n Environment: DRI_PRIME=pci-0000_01_00_0\n') ++ self.assertEqual(out.stdout, b'Device: 0\n Name: Intel\xc2\xae UHD Graphics 620 (Kabylake GT2)\n Default: yes\n Discrete: no\n Environment: DRI_PRIME=pci-0000_00_02_0\n\nDevice: 1\n Name: GM108M [GeForce 930MX]\n Default: no\n Discrete: no\n Environment: DRI_PRIME=pci-0000_01_00_0\n') + + out = subprocess.run([tool_path, 'launch', '--gpu', '0', 'env'], capture_output=True) + self.assertEqual(out.returncode, 0, "'switcherooctl launch --gpu 0' failed") +-- +2.49.0 + + +From a446971f7aef77218625eb8627ec34e86e403737 Mon Sep 17 00:00:00 2001 +From: Jan200101 +Date: Mon, 28 Apr 2025 21:53:21 +0200 +Subject: [PATCH 13/13] tests: add tests for xe discrete detection + +Signed-off-by: Jan200101 +--- + tests/discrete-detection/drm_xe_mock.c | 47 ++++++++++++++++++++++++++ + tests/discrete-detection/meson.build | 42 ++++++++++++++++++++++- + 2 files changed, 88 insertions(+), 1 deletion(-) + create mode 100644 tests/discrete-detection/drm_xe_mock.c + +diff --git a/tests/discrete-detection/drm_xe_mock.c b/tests/discrete-detection/drm_xe_mock.c +new file mode 100644 +index 0000000..a9da460 +--- /dev/null ++++ b/tests/discrete-detection/drm_xe_mock.c +@@ -0,0 +1,47 @@ ++#include ++#include ++#include ++#include ++ ++#include ++enum { ++ OTHER_GPU, ++ INTEL_IGPU, ++ INTEL_GPU, ++}; ++ ++/* Mock open(2) so we can test multiple devices configurations */ ++int open(const char *pathname, int flags) ++{ ++ if (!strcmp(pathname, "OTHER_GPU")) ++ return OTHER_GPU; ++ if (!strcmp (pathname, "INTEL_IGPU")) ++ return INTEL_IGPU; ++ if (!strcmp (pathname, "INTEL_GPU")) ++ return INTEL_GPU; ++ ++ return -1; ++} ++ ++/* open64 may be used for large file support */ ++int open64(const char *pathname, int flags) ++{ ++ return open (pathname, flags); ++} ++ ++int ioctl(int fd, unsigned long op, struct drm_xe_device_query* query) ++{ ++ if (op != DRM_IOCTL_XE_DEVICE_QUERY) ++ return EINVAL; ++ if (fd == OTHER_GPU) ++ return ENODEV; ++ ++ if (query->size == 0) { ++ query->size = sizeof(struct drm_xe_query_config); ++ } else { ++ struct drm_xe_query_config *config = (struct drm_xe_query_config*)query->data; ++ config->info[DRM_XE_QUERY_CONFIG_FLAGS] = fd == INTEL_GPU ? DRM_XE_QUERY_CONFIG_FLAG_HAS_VRAM : 0; ++ } ++ ++ return 0; ++} +diff --git a/tests/discrete-detection/meson.build b/tests/discrete-detection/meson.build +index f01a014..998229f 100644 +--- a/tests/discrete-detection/meson.build ++++ b/tests/discrete-detection/meson.build +@@ -77,4 +77,44 @@ if libdrm.found() and libdrm_nouveau.found() + env: environment({'LD_PRELOAD': nouveau_mock_lib.full_path()}), + should_fail: false + ) +-endif +\ No newline at end of file ++endif ++ ++if drm_xe ++ xe_mock_lib = shared_library( ++ 'drm_xe_mock', ++ files('drm_xe_mock.c') ++ ) ++ ++ test( ++ 'test xe detection with invalid device', ++ xe_discrete, ++ args: ['NO_GPU'], ++ env: environment({'LD_PRELOAD': xe_mock_lib.full_path()}), ++ should_fail: true ++ ) ++ ++ test( ++ 'test xe detection with non-Intel GPU', ++ xe_discrete, ++ args: ['OTHER_GPU'], ++ env: environment({'LD_PRELOAD': xe_mock_lib.full_path()}), ++ should_fail: true ++ ) ++ ++ test( ++ 'test xe detection with Intel Integrated GPU', ++ xe_discrete, ++ args: ['INTEL_IGPU'], ++ env: environment({'LD_PRELOAD': xe_mock_lib.full_path()}), ++ should_fail: true ++ ) ++ ++ test( ++ 'test xe detection with Intel Discrete GPU', ++ xe_discrete, ++ args: ['INTEL_GPU'], ++ env: environment({'LD_PRELOAD': xe_mock_lib.full_path()}), ++ should_fail: false ++ ) ++ ++endif +-- +2.49.0 + diff --git a/anda/apps/switcheroo-control/switcheroo-control.spec b/anda/apps/switcheroo-control/switcheroo-control.spec index dde67c0bb2..11f83f1462 100644 --- a/anda/apps/switcheroo-control/switcheroo-control.spec +++ b/anda/apps/switcheroo-control/switcheroo-control.spec @@ -1,22 +1,16 @@ Name: switcheroo-control Version: 2.6 -Release: 8%{?dist} +Release: 9%{?dist} Summary: D-Bus service to check the availability of dual-GPU License: GPLv3 URL: https://gitlab.freedesktop.org/hadess/switcheroo-control/ # URL from https://gitlab.freedesktop.org/hadess/switcheroo-control/-/releases Source0: https://gitlab.freedesktop.org/hadess/switcheroo-control/uploads/86ea54ac7ddb901b6bf6e915209151f8/switcheroo-control-2.6.tar.xz -Patch: 0001-main-update-GPUs-comment-for-dbus-property.patch -Patch: 0002-main-add-Discrete-key.patch -Patch: 0003-tests-fix-integration-tests-without-UMockdev.patch -Patch: 0004-tests-add-tests-for-discrete-detection-with-mock-lib.patch -Patch: 0005-main-remove-leftover-and-fix-typo.patch -Patch: 0006-main-move-discrete-dependencies-out-of-main-deps.patch -Patch: 0007-main-use-glib-for-discrete-command-line-arguments.patch -Patch: 0008-main-add-udev-rule-for-i915.patch -Patch: 0009-main-use-Discrete-key-in-switcherooctl.patch -Patch: 0010-main-use-new-GPU-list-on-uevent.patch +# Adds proper discrete GPU detection to switcheroo-control +# https://gitlab.freedesktop.org/hadess/switcheroo-control/-/merge_requests/69 +Patch: discrete.patch + BuildRequires: gcc BuildRequires: pkgconfig(gudev-1.0) @@ -25,6 +19,7 @@ BuildRequires: gtk-doc BuildRequires: meson BuildRequires: systemd BuildRequires: libdrm-devel +BuildRequires: kernel-headers BuildRequires: python3-dbusmock BuildRequires: umockdev @@ -78,6 +73,7 @@ fi %{_mandir}/man1/switcherooctl.1* %{_libexecdir}/check-discrete-amdgpu %{_libexecdir}/check-discrete-nouveau +%{_libexecdir}/check-discrete-xe %{_udevrulesdir}/30-discrete-gpu.rules %files docs @@ -86,14 +82,17 @@ fi %{_datadir}/gtk-doc/html/%{name}/ %changelog -* Fri Sep 06 2024 Jan200101 - 2.6-8 +* Tue Apr 29 2025 Jan200101 - 2.6-9 - Update discrete patch -* Thu Feb 15 2024 Jan Drögehoff - 2.6-7 -- Update discrete patch +* Sun Jan 19 2025 Fedora Release Engineering - 2.6-8 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_42_Mass_Rebuild -* Wed Feb 14 2024 Jan Drögehoff - 2.6-6 -- Add discrete patch +* Mon Jul 29 2024 Miroslav Suchý - 2.6-7 +- convert license to SPDX + +* Sat Jul 20 2024 Fedora Release Engineering - 2.6-6 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_41_Mass_Rebuild * Sat Jan 27 2024 Fedora Release Engineering - 2.6-5 - Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild