Skip to content

Commit 6b0832e

Browse files
committed
fix: unreliable udev permissions on Fedora Atomic + centralize sysfs attribute definitions
Fixed: 1. Boot-time platform_profile/no_turbo permissions on Bazzite/Fedora Atomic where intel_pstate is built-in and module add events never fire — added redundant asus-nb-wmi driver load triggers to udev rules 2. Phantom ENODEV on dual-backend machines — SysfsHelper now prefers firmware-attributes over legacy paths for aliased attributes (ppt_fppt→ppt_pl3_fppt, panel_od→panel_overdrive) 3. Redundant panel_overdrive fallback in Get/SetPanelOverdrive — now handled transparently by AttrDef alias system 4. Missing udev rules for ppt_apu_sppt, ppt_platform_sppt, egpu_enable, boot_sound Added: 1. AsusAttributes centralized registry — all 16 ASUS WMI sysfs attributes defined as AttrDef objects with legacy/firmware-attributes name mapping, replacing hardcoded strings across LinuxAsusWmi, ModeControl, FansWindow, ExtraWindow, MainWindow, and Diagnostics 2. Versioned udev rules — VERSION_PLACEHOLDER stamp in rules header, CI stamps real version during release, rules uploaded as release artifact 3. Udev rules version reporting and mismatch detection in diagnostics 4. In-memory 1000-line thread-safe log ring buffer — diagnostics report now includes recent log history so users can provide logs without running from terminal 5. Enhanced dual-backend diagnostics — SysfsHelper logs firmware-attributes preference over phantom legacy paths, diagnostics iterate all 16 attrs (was 10) with alias annotations 6. Hide GPU panel when no discrete GPU is present CI: - Add udev-rules artifact upload step to build job - Replace GitHub-login-required artifact links with nightly.link public URLs - Add installation instructions for AppImage, native binary, and udev rules - .csproj version stamp now uses 0.0.0-pr.{N} format instead of bare 0.0.0 - New step stamps VERSION_PLACEHOLDER in udev rules with same PR version - Comment job downloads all artifacts, bundles into single g-helper-linux-pr-{N} zip - Rewritten PR comment with single download link, contents table, and unzip instructions - include short SHA in versioning for PR builds and udev rules
1 parent 33ed511 commit 6b0832e

File tree

14 files changed

+516
-109
lines changed

14 files changed

+516
-109
lines changed

.github/workflows/pr-build.yml

Lines changed: 81 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,17 @@ jobs:
1818

1919
- name: Patch version in .csproj
2020
run: |
21-
VERSION="0.0.0"
21+
SHORT_SHA=$(echo "${{ github.event.pull_request.head.sha }}" | cut -c1-7)
22+
VERSION="0.0.0-pr.${{ github.event.pull_request.number }}.${SHORT_SHA}"
2223
sed -i "s|<Version>.*</Version>|<Version>${VERSION}</Version>|" src/GHelper.Linux.csproj
23-
echo "Set version to: ${VERSION} (PR #${{ github.event.pull_request.number }})"
24+
echo "Set version to: ${VERSION}"
25+
26+
- name: Stamp version in udev rules
27+
run: |
28+
SHORT_SHA=$(echo "${{ github.event.pull_request.head.sha }}" | cut -c1-7)
29+
VERSION="0.0.0-pr.${{ github.event.pull_request.number }}.${SHORT_SHA}"
30+
sed -i "s/VERSION_PLACEHOLDER/${VERSION}/" install/90-ghelper.rules
31+
echo "Updated udev rules version to: ${VERSION}"
2432
2533
- name: Setup .NET 8
2634
uses: actions/setup-dotnet@v4
@@ -52,6 +60,13 @@ jobs:
5260
src/bin/Release/net8.0/linux-x64/publish/libHarfBuzzSharp.so
5361
retention-days: 7
5462

63+
- name: Upload udev rules artifact
64+
uses: actions/upload-artifact@v4
65+
with:
66+
name: udev-rules
67+
path: install/90-ghelper.rules
68+
retention-days: 7
69+
5570
appimage:
5671
needs: [build]
5772
runs-on: ubuntu-latest
@@ -111,37 +126,91 @@ jobs:
111126
needs: [build, appimage]
112127
runs-on: ubuntu-latest
113128
steps:
114-
- name: Post or update PR comment with artifact links
129+
- name: Download all artifacts
130+
uses: actions/download-artifact@v4
131+
with:
132+
path: artifacts
133+
134+
- name: Bundle artifacts
135+
run: |
136+
mkdir -p bundle
137+
cp artifacts/ghelper/ghelper bundle/
138+
cp artifacts/native-libs/libSkiaSharp.so bundle/
139+
cp artifacts/native-libs/libHarfBuzzSharp.so bundle/
140+
cp artifacts/udev-rules/90-ghelper.rules bundle/
141+
cp artifacts/appimage/GHelper-x86_64.AppImage bundle/
142+
chmod +x bundle/ghelper bundle/GHelper-x86_64.AppImage
143+
144+
- name: Upload combined artifact
145+
uses: actions/upload-artifact@v4
146+
with:
147+
name: g-helper-linux-pr-${{ github.event.pull_request.number }}
148+
path: bundle/
149+
retention-days: 7
150+
151+
- name: Post or update PR comment
115152
uses: actions/github-script@v7
116153
with:
117154
script: |
118155
const marker = '<!-- pr-build-artifacts -->';
119156
const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
120157
const sha = context.payload.pull_request.head.sha.substring(0, 7);
158+
const prNum = context.payload.pull_request.number;
159+
const version = `0.0.0-pr.${prNum}.${sha}`;
160+
161+
const nightlyBase = `https://nightly.link/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
162+
const zipName = `g-helper-linux-pr-${prNum}`;
121163
122164
const body = [
123165
marker,
124-
'### Build Artifacts',
166+
`### Build Artifacts`,
125167
'',
126-
`Built from commit \`${sha}\` — [Download from Actions run](${runUrl})`,
168+
`Built from commit \`${sha}\` — version \`${version}\``,
127169
'',
128-
'| Artifact | Description |',
129-
'|----------|-------------|',
130-
'| **ghelper** | Native AOT binary |',
131-
'| **native-libs** | libSkiaSharp.so + libHarfBuzzSharp.so |',
132-
'| **appimage** | GHelper-x86_64.AppImage (all-in-one) |',
170+
`**[Download ${zipName}.zip](${nightlyBase}/${zipName}.zip)**`,
171+
'',
172+
`> [View on GitHub Actions](${runUrl}) (requires login)`,
173+
'',
174+
'---',
175+
'',
176+
'**Contents:**',
177+
'',
178+
'| File | Description |',
179+
'|------|-------------|',
180+
'| `GHelper-x86_64.AppImage` | All-in-one AppImage (easiest to run) |',
181+
'| `ghelper` | Native AOT binary |',
182+
'| `libSkiaSharp.so` | SkiaSharp native library |',
183+
'| `libHarfBuzzSharp.so` | HarfBuzzSharp native library |',
184+
'| `90-ghelper.rules` | Udev rules for hardware access without sudo |',
185+
'',
186+
'---',
133187
'',
134188
'**Quick test:**',
135189
'```bash',
136-
'# Option 1: AppImage (easiest)',
190+
`unzip ${zipName}.zip -d ${zipName}`,
191+
`cd ${zipName}`,
192+
'```',
193+
'',
194+
'**Option A — AppImage (easiest):**',
195+
'```bash',
137196
'chmod +x GHelper-x86_64.AppImage',
138197
'./GHelper-x86_64.AppImage',
198+
'```',
139199
'',
140-
'# Option 2: Native binary',
200+
'**Option B — Native binary:**',
201+
'```bash',
141202
'chmod +x ghelper',
142203
'./ghelper',
143204
'```',
144205
'',
206+
'**Install udev rules (required for hardware access):**',
207+
'```bash',
208+
'sudo cp 90-ghelper.rules /etc/udev/rules.d/',
209+
'sudo udevadm control --reload-rules && sudo udevadm trigger',
210+
'```',
211+
'',
212+
'> **Note:** Without udev rules, ghelper needs `sudo` to access fan curves, performance modes, keyboard backlight, etc.',
213+
'',
145214
`> Artifacts expire in 7 days.`,
146215
].join('\n');
147216

.github/workflows/release.yml

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,14 @@ jobs:
5656
echo "Updated .csproj version to: ${VERSION}"
5757
grep "<Version>" src/GHelper.Linux.csproj
5858
59+
- name: Stamp version in udev rules
60+
run: |
61+
VERSION=${{ needs.version.outputs.new_version }}
62+
VERSION=${VERSION#v}
63+
sed -i "s/VERSION_PLACEHOLDER/${VERSION}/" install/90-ghelper.rules
64+
echo "Updated udev rules version to: ${VERSION}"
65+
grep "# Version:" install/90-ghelper.rules
66+
5967
- name: Setup .NET 8
6068
uses: actions/setup-dotnet@v4
6169
with:
@@ -84,6 +92,12 @@ jobs:
8492
src/bin/Release/net8.0/linux-x64/publish/libSkiaSharp.so
8593
src/bin/Release/net8.0/linux-x64/publish/libHarfBuzzSharp.so
8694
95+
- name: Upload udev rules artifact
96+
uses: actions/upload-artifact@v4
97+
with:
98+
name: udev-rules
99+
path: install/90-ghelper.rules
100+
87101
# Build AppImage from compiled artifacts
88102
appimage:
89103
needs: [build]
@@ -161,6 +175,7 @@ jobs:
161175
cp artifacts/native-libs/libSkiaSharp.so release/
162176
cp artifacts/native-libs/libHarfBuzzSharp.so release/
163177
cp artifacts/appimage/GHelper-x86_64.AppImage release/
178+
cp artifacts/udev-rules/90-ghelper.rules release/
164179
chmod +x release/ghelper
165180
chmod +x release/GHelper-x86_64.AppImage
166181
ls -la release/
@@ -211,7 +226,7 @@ jobs:
211226
> **Note:** udev rules are required for hardware access without root. The quick install script handles this automatically. For AppImage or manual installs:
212227
>
213228
> ```bash
214-
> sudo curl -sL https://raw.githubusercontent.com/${{ github.repository }}/master/install/90-ghelper.rules -o /etc/udev/rules.d/90-ghelper.rules
229+
> sudo curl -sL https://github.com/${{ github.repository }}/releases/download/${{ needs.version.outputs.new_version }}/90-ghelper.rules -o /etc/udev/rules.d/90-ghelper.rules
215230
> sudo udevadm control --reload-rules && sudo udevadm trigger
216231
> ```
217232
@@ -222,4 +237,5 @@ jobs:
222237
release/libSkiaSharp.so
223238
release/libHarfBuzzSharp.so
224239
release/GHelper-x86_64.AppImage
240+
release/90-ghelper.rules
225241
generate_release_notes: true

install/90-ghelper.rules

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# G-Helper Linux — udev rules for non-root access to ASUS hardware controls
22
# Install: sudo cp 90-ghelper.rules /etc/udev/rules.d/ && sudo udevadm control --reload-rules
3+
# Version: VERSION_PLACEHOLDER
34
#
45
# These rules grant read/write access to ASUS sysfs attributes for members of
56
# the "input" group (or any user with a login session via uaccess).
@@ -35,6 +36,33 @@ SUBSYSTEM=="platform", DRIVER=="asus-nb-wmi", \
3536
ATTR{nv_temp_target}!="", \
3637
RUN+="/bin/chmod 0666 /sys/devices/platform/asus-nb-wmi/nv_temp_target"
3738

39+
# Additional PPT power limits (less common, but present on some models)
40+
SUBSYSTEM=="platform", DRIVER=="asus-nb-wmi", \
41+
ATTR{ppt_apu_sppt}!="", \
42+
RUN+="/bin/chmod 0666 /sys/devices/platform/asus-nb-wmi/ppt_apu_sppt"
43+
44+
SUBSYSTEM=="platform", DRIVER=="asus-nb-wmi", \
45+
ATTR{ppt_platform_sppt}!="", \
46+
RUN+="/bin/chmod 0666 /sys/devices/platform/asus-nb-wmi/ppt_platform_sppt"
47+
48+
# eGPU and boot sound
49+
SUBSYSTEM=="platform", DRIVER=="asus-nb-wmi", \
50+
ATTR{egpu_enable}!="", \
51+
RUN+="/bin/chmod 0666 /sys/devices/platform/asus-nb-wmi/egpu_enable"
52+
53+
SUBSYSTEM=="platform", DRIVER=="asus-nb-wmi", \
54+
ATTR{boot_sound}!="", \
55+
RUN+="/bin/chmod 0666 /sys/devices/platform/asus-nb-wmi/boot_sound"
56+
57+
# platform_profile + CPU boost — also triggered via asus-nb-wmi since
58+
# dedicated module/cpu/acpi triggers are unreliable on some distros
59+
# (Bazzite/Fedora Atomic: intel_pstate built-in, CPU add fires too early).
60+
SUBSYSTEM=="platform", DRIVER=="asus-nb-wmi", \
61+
RUN+="/bin/sh -c '[ -f /sys/firmware/acpi/platform_profile ] && chmod 0666 /sys/firmware/acpi/platform_profile'"
62+
63+
SUBSYSTEM=="platform", DRIVER=="asus-nb-wmi", \
64+
RUN+="/bin/sh -c '[ -f /sys/devices/system/cpu/intel_pstate/no_turbo ] && chmod 0666 /sys/devices/system/cpu/intel_pstate/no_turbo; [ -f /sys/devices/system/cpu/cpufreq/boost ] && chmod 0666 /sys/devices/system/cpu/cpufreq/boost'"
65+
3866
# ── asus-nb-wmi bus device ──
3967
# GPU Eco mode, MUX switch, Mini LED
4068
SUBSYSTEM=="platform", DRIVER=="asus-nb-wmi", \
@@ -80,14 +108,19 @@ SUBSYSTEM=="hwmon", ATTR{name}=="asus_nb_wmi", \
80108
RUN+="/bin/sh -c 'for f in /sys/class/hwmon/%k/pwm*_enable; do [ -f \"$f\" ] && chmod 0666 \"$f\"; done'"
81109

82110
# ── CPU boost ──
83-
# Intel
111+
# Intel (module trigger — works when intel_pstate is a loadable module)
84112
ACTION=="add|change", SUBSYSTEM=="module", KERNEL=="intel_pstate", \
85113
RUN+="/bin/sh -c '[ -f /sys/devices/system/cpu/intel_pstate/no_turbo ] && chmod 0666 /sys/devices/system/cpu/intel_pstate/no_turbo'"
86114

87-
# AMD / generic cpufreq
115+
# AMD / generic cpufreq (module trigger)
88116
ACTION=="add|change", SUBSYSTEM=="module", KERNEL=="acpi_cpufreq", \
89117
RUN+="/bin/sh -c '[ -f /sys/devices/system/cpu/cpufreq/boost ] && chmod 0666 /sys/devices/system/cpu/cpufreq/boost'"
90118

119+
# CPU device trigger — catches built-in drivers (intel_pstate, amd_pstate) that don't
120+
# fire module events. Also sets platform_profile permissions.
121+
SUBSYSTEM=="cpu", ACTION=="add", \
122+
RUN+="/bin/sh -c '[ -f /sys/devices/system/cpu/intel_pstate/no_turbo ] && chmod 0666 /sys/devices/system/cpu/intel_pstate/no_turbo; [ -f /sys/devices/system/cpu/cpufreq/boost ] && chmod 0666 /sys/devices/system/cpu/cpufreq/boost; [ -f /sys/firmware/acpi/platform_profile ] && chmod 0666 /sys/firmware/acpi/platform_profile'"
123+
91124
# ── ACPI platform profile ──
92125
# Allow non-root to set platform profile (balanced/performance/low-power)
93126
ACTION=="add|change", SUBSYSTEM=="acpi", \

src/App.axaml.cs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -230,17 +230,17 @@ private void InitializePlatform()
230230

231231
private void LogFeatureDetection()
232232
{
233-
var features = new[]
233+
var features = new (AttrDef attr, string name)[]
234234
{
235-
("throttle_thermal_policy", "Performance modes"),
236-
("dgpu_disable", "GPU Eco mode"),
237-
("gpu_mux_mode", "MUX switch"),
238-
("panel_od", "Panel overdrive"),
239-
("mini_led_mode", "Mini LED"),
240-
("ppt_pl1_spl", "PL1 power limit"),
241-
("ppt_pl2_sppt", "PL2 power limit"),
242-
("nv_dynamic_boost", "NVIDIA dynamic boost"),
243-
("nv_temp_target", "NVIDIA temp target"),
235+
(AsusAttributes.ThrottleThermalPolicy, "Performance modes"),
236+
(AsusAttributes.DgpuDisable, "GPU Eco mode"),
237+
(AsusAttributes.GpuMuxMode, "MUX switch"),
238+
(AsusAttributes.PanelOd, "Panel overdrive"),
239+
(AsusAttributes.MiniLedMode, "Mini LED"),
240+
(AsusAttributes.PptPl1Spl, "PL1 power limit"),
241+
(AsusAttributes.PptPl2Sppt, "PL2 power limit"),
242+
(AsusAttributes.NvDynamicBoost, "NVIDIA dynamic boost"),
243+
(AsusAttributes.NvTempTarget, "NVIDIA temp target"),
244244
};
245245

246246
foreach (var (attr, name) in features)
@@ -597,7 +597,7 @@ private NativeMenu CreateTrayMenu(IClassicDesktopStyleApplicationLifetime deskto
597597
// GPU modes — only show if dGPU is present (dgpu_disable sysfs attribute exists).
598598
// All sysfs writes run in Task.Run via GpuModeController
599599
// (dgpu_disable writes can block in the kernel for 30-60 seconds)
600-
if (Wmi?.IsFeatureSupported("dgpu_disable") == true)
600+
if (Wmi?.IsFeatureSupported(AsusAttributes.DgpuDisable) == true)
601601
{
602602
var eco = new NativeMenuItem("GPU: Eco (iGPU only)");
603603
eco.Click += (_, _) => TrayGpuModeSwitch(GpuMode.Eco);
@@ -612,7 +612,7 @@ private NativeMenu CreateTrayMenu(IClassicDesktopStyleApplicationLifetime deskto
612612
menu.Add(optimized);
613613

614614
// Ultimate (MUX switch) — only on models with gpu_mux_mode support
615-
if (Wmi?.IsFeatureSupported("gpu_mux_mode") == true)
615+
if (Wmi?.IsFeatureSupported(AsusAttributes.GpuMuxMode) == true)
616616
{
617617
var ultimate = new NativeMenuItem("GPU: Ultimate (MUX)");
618618
ultimate.Click += (_, _) => TrayGpuModeSwitch(GpuMode.Ultimate);

0 commit comments

Comments
 (0)