Compare commits

...
2 Commits
Author SHA1 Message Date
Frank Zechert 4ba1d1317f added teams-for-linux 2026-08-20 15:36:53 +02:00
Frank Zechert 67c4ae45a1 hardware bugs in xps13 2026-08-20 15:36:38 +02:00
2 changed files with 200 additions and 10 deletions
+60 -10
View File
@@ -49,20 +49,41 @@ uptime runtime-suspended, so the race window was being hit constantly.
Pin the audio controller out of runtime power management so the SoundWire links stay powered and the
window never opens.
`/etc/udev/rules.d/99-sof-audio-no-runtime-pm.rules`:
.box {Do not do this with udev} type:{warning}
A udev rule writing `ATTR{power/control}="on"` on `add|bind` looks like the obvious lever and
matches correctly — `udevadm test` confirms it fires for both actions — but it does not hold.
`snd_sof_pci` calls `pm_runtime_allow()` from `sof_pci_probe_complete()`, which runs *after* the
uevents udev hooks, so the driver reverts `power/control` to `auto` on every boot.
This cost five further panics between 2026-08-11 and 2026-08-20 while the rule sat in
`/etc/udev/rules.d/` looking like active protection.
Gate the driver instead. Bit 0 of `sof_pci_debug` (`SOF_PCI_DISABLE_PM_RUNTIME`) makes
`sof_pci_probe_complete()` return before it reaches the runtime PM setup, so there is no ordering to
lose:
```txt
ACTION=="add|bind", SUBSYSTEM=="pci", ATTR{vendor}=="0x8086", ATTR{device}=="0xa828", ATTR{power/control}="on"
sof_pci_probe_complete:
testb $0x1, sof_pci_debug ; skip the block entirely when bit 0 is set
je ...
pm_runtime_set_autosuspend_delay(dev, 0x7d0) ; 2000 ms
__pm_runtime_use_autosuspend(dev, 1)
pm_runtime_allow(dev) ; this is what reverts power/control to "auto"
```
Matching on the PCI ID `8086:a828` rather than the slot `0000:00:1f.3` keeps the rule valid if the
device is ever renumbered. `add|bind` covers both device registration and driver bind.
`/etc/modprobe.d/99-sof-no-runtime-pm.conf`:
Apply it without rebooting:
```txt
options snd_sof_pci sof_pci_debug=1
```
`snd_sof_pci` is not in the initramfs, so this needs no `mkinitcpio` run and no re-signing of the
unified kernel image. It takes effect on the next boot.
Close the window immediately on a running system, without waiting for a reboot:
```sh
sudo udevadm control --reload
sudo udevadm trigger --action=add /sys/bus/pci/devices/0000:00:1f.3
echo on | sudo tee /sys/bus/pci/devices/0000:00:1f.3/power/control
```
Verify. `control` must read `on` and `runtime_status` must read `active`:
@@ -79,14 +100,37 @@ cat /sys/bus/pci/devices/0000:00:1f.3/power/runtime_suspended_time
cat /sys/bus/pci/devices/0000:00:1f.3/power/runtime_active_time
```
Confirm the rule also applies on a fresh boot rather than only surviving the manual trigger:
Check the ratio after a fresh boot, not just the manual write. Unmitigated, the controller spends
around 90% of uptime suspended and cycles constantly rather than settling — roughly 30 seconds of
active time accrues in the first 8 minutes, so the race window is being re-entered continuously:
```sh
udevadm test --action=add /sys/bus/pci/devices/0000:00:1f.3 2>&1 | grep power/control
awk '{printf "suspended %d%%\n", 100*$1/($1+$2)}' \
<(paste /sys/bus/pci/devices/0000:00:1f.3/power/runtime_{suspended,active}_time)
```
### Recognising it after the fact
The panic leaves nothing behind, so identify it by shape rather than by trace. `CONFIG_EFI_VARS_PSTORE_DEFAULT_DISABLE`
is set and no pstore backend is registered, so `/sys/fs/pstore` and `/var/lib/systemd/pstore` are
both empty after a hang.
A crashed boot ends mid-heartbeat with no shutdown sequence. Compare the last line of each boot: a
clean shutdown ends in `Unmounted /efi` or `Stopping Flush Journal to Persistent Storage`, while a
panic ends on whatever was logging periodically — usually `kdeconnectd`, exactly on its 30-second
cadence, with the next tick missing:
```sh
for b in $(journalctl --list-boots -q | awk '{print $1}'); do
printf "%4s %s\n" "$b" "$(journalctl -b "$b" -o short-iso -q | tail -1)"
done
```
The machine is always idle when it dies, not under load, and the journal is silent on the kernel side
for the entire session — `journalctl -b -1 -k -p 4` shows only the usual boot-time noise.
.box {This is a mitigation, not a fix} type:{warning}
The driver defect is still present. The rule only stops it from being triggered.
The driver defect is still present. Disabling runtime PM only stops it from being triggered.
If the freezes return, bypass SoundWire entirely by forcing the legacy HDA driver in
`/etc/modprobe.d/`:
@@ -106,6 +150,12 @@ udevadm test --action=add /sys/bus/pci/devices/0000:00:1f.3 2>&1 | grep power/co
The hardware is fine too: no machine check exceptions, no ECC or EDAC errors, and `fwupdmgr`
reports all firmware current.
The battery charge-controller fault is a separate problem and not the cause of these hangs. It
presents the same way from the outside — the machine dies with no clean shutdown and nothing in
the journal — so check it off explicitly. In a SoundWire panic the battery is untouched
(`charge_now == charge_full` on the next boot) and `battery-charge-watchdog` logs only its
startup line, never a `FAULT`.
Capturing a fresh trace would need `efi_pstore.pstore_disable=0` on the kernel command line,
which means creating `/etc/kernel/cmdline`, regenerating the unified kernel image and re-signing
it. `CONFIG_EFI_VARS_PSTORE_DEFAULT_DISABLE` is set, so panics are not persisted by default.
+140
View File
@@ -0,0 +1,140 @@
.docname {Teams for Linux}
.include {docs}
Microsoft does not publish a native Teams client for Linux. [Teams for Linux][teams-for-linux] is an
unofficial [Electron][electron] wrapper around the Teams web app that provides a standalone desktop
client, system tray integration and native notifications.
Install it with `yay -S teams-for-linux-bin`.
## Configuration
The configuration is stored in `~/.config/teams-for-linux/config.json`.
```json
{
"notificationMethod": "web",
"electronCLIFlags": [
["enable-features", "WebRtcPipeWireCamera"]
]
}
```
- `notificationMethod: web` uses the browser notification API of the embedded web app instead of the
Electron notification API.
- `WebRtcPipeWireCamera` enables the PipeWire camera backend, which is required for the webcam to
work under Wayland.
## Opening meeting links in the app
Clicking a Teams meeting link in the [Thunderbird](002-thunderbird.qd) or Outlook web interface opens
the meeting in the browser instead of in Teams for Linux. Getting the handover to work requires a
userscript, because the desktop integration alone is not sufficient.
### Verify the protocol handler
The package registers the `msteams:` URI scheme through its desktop entry, so this part usually works
out of the box:
```sh
gio mime x-scheme-handler/msteams
```
The output must name `teams-for-linux.desktop` as the default application. If it does not, register it
with `xdg-mime default teams-for-linux.desktop x-scheme-handler/msteams`.
A deep link can be tested directly from a shell. This opens the meeting in Teams for Linux:
```sh
xdg-open 'msteams:/meet/<meeting-id>?p=<passcode>'
```
### Why the browser never asks
Meeting invitations do not contain `msteams:` links. They contain plain HTTPS links in the form
`https://teams.microsoft.com/meet/<meeting-id>?p=<passcode>`. An HTTPS URL belongs to the browser,
so nothing is ever handed to an external application.
That URL answers with a redirect to a launcher page:
```txt
HTTP/2 302
location: /dl/launcher/launcher.html?url=%2F_%23%2Fmeet%2F<meeting-id>%3Fp%3D<passcode>%26anon%3Dtrue&type=meet&directDl=true&msLaunch=true
```
Because the redirect happens on the server, no document is ever created for the `/meet/` URL and a
userscript cannot match on it. The launcher page is the first document that actually loads, and its
`url` query parameter carries the deep link path, prefixed with `/_#`. Stripping that prefix yields
exactly the path the `msteams:` scheme expects.
### Userscript
Install [Tampermonkey][tampermonkey] in [Librewolf](001-librewolf.qd) and add the following userscript.
```js
// ==UserScript==
// @name Teams links to teams-for-linux
// @match https://teams.microsoft.com/dl/launcher/launcher.html*
// @match https://teams.microsoft.com/meet/*
// @match https://teams.microsoft.com/l/*
// @run-at document-start
// @grant GM_info
// ==/UserScript==
(() => {
let path;
if (location.pathname.startsWith('/dl/launcher/')) {
const u = new URLSearchParams(location.search).get('url');
if (!u) return;
path = u.replace(/^\/_#/, '');
} else {
path = location.pathname + location.search;
}
if (!/^\/(meet|l)\//.test(path)) return;
window.stop();
location.href = 'msteams:' + path;
setTimeout(() => {
window.close();
location.replace('about:blank');
}, 1500);
})();
```
Three details in this script are not obvious and the script silently stops working without them:
- `@grant GM_info` forces Tampermonkey to run the script in its sandboxed content script scope. With
`@grant none` the script is injected into the page as an inline `<script>` element and is then
blocked by the launcher page, which sends a `script-src 'nonce-...'` content security policy.
The script still matches, but never executes.
- `window.stop()` aborts the launcher page before its own scripts are parsed. Otherwise the page
navigates on to `https://teams.microsoft.com/v2/`, which destroys the document and discards the
pending timeout, so the tab is never cleaned up. It must be called **before** the `msteams:`
assignment, because calling it afterwards cancels the handover itself.
- `@run-at document-start` is what makes the above possible in the first place.
On the first meeting link the browser asks which application should open the link. Select Teams for
Linux and enable `Always allow teams.microsoft.com to open links of this type`. The dialog is
tab-modal, so if it is still open when the tab closes it disappears together with the tab.
> Tip: The tab is closed by `window.close()` even though `dom.allow_scripts_to_close_windows` is
> `false` by default. Outlook opens meeting links through `window.open()`, and Firefox permits a
> script to close windows that were opened by a script. The `location.replace('about:blank')` call
> is the fallback for tabs that were opened another way.
### Maintenance
Two upstream changes can break this and are worth checking first if meeting links stop working:
- Microsoft changing the launcher URL layout. The `.replace(/^\/_#/, '')` call encodes the current
`/_#/meet/...` format of the `url` query parameter.
- Teams for Linux tightening its deep link validation. The accepted schemes are defined by these
regular expressions in the application:
```txt
^msteams:/(?:meet/|l/(?:app|call|channel|chat|entity|file|meet(?:ing|up-join)|message|task|team)/)
^msteams://teams\.(?:microsoft\.com|live\.com|cloud\.microsoft)/(?:meet/|l/(?:app|call|channel|chat|entity|file|meet(?:ing|up-join)|message|task|team)/)
```
[teams-for-linux]: https://github.com/IsmaelMartinez/teams-for-linux
[electron]: https://www.electronjs.org
[tampermonkey]: https://www.tampermonkey.net