Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow using deckbd for password entry during boot #382

Open
wants to merge 5 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions modules/devices/steamdeck/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ in
imports = [
./controller.nix
./fan-control.nix
./fde.nix
./firmware.nix
./graphical.nix
./hw-support.nix
Expand Down
105 changes: 105 additions & 0 deletions modules/devices/steamdeck/fde.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
{ config, lib, pkgs, utils, ... }:

let
cfg = config.jovian.devices.steamdeck;
in
{
options = {
jovian.devices.steamdeck = {
fde = {
deckbd = {
Comment on lines +8 to +10
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm... not sure about the jovian.devices.steamdeck.fde.deckbd namespace...

I don't have a suggestion at the exact moment, but it's less directly about FDE, but about a device-specific tool and integration to make FDE work, so maybe it should be categorized as such. 🤔 Any thoughts?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My reasoning was that the launch is directly tied to FDE-related password prompting services. I too can't really think of a better name - I'm open to suggestions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(You don't need to do anything. I'll handle moving the option[s].)

So I've spent a few more minutes thinking about it. I think I know why I found the namespacing off. No options in NixOS uses the fde wording, even the word “encryption” isn't used by the NixOS docs around that. (And uuuuh... that's another issue to solve, just an observation.)

Now, couple that with this being a device-specific "service". We have a single one at the moment (jovian.devices.steamdeck.enableGyroDsuService), and it too I think is somewhat miscategorized. (Side-note, this other option was moved since its inception, and was "grandfathered-in" into the steamdeck namespace instead of being globally awkwardly available...)

So, what I want is thoughts about my thought:

Let's have jovian.devices.*.[some word].[tool name].[options]. I'm not sure if .services or .tools or what would be appropriate. Though I think it should encompass things like sdgyrodsu and other device-specific services.

My train of thoughts here is that being for FDE is an implementation detail; it's an input device "interceptor" service that is intended for usage in stage-1 for passphrase input. And it's not strictly limited to being a stage-1 tool. While there's no use-case yet, it could be used in the booted system, too, right?.

So I suppose the enable option might be better described as enableStage1 or /*...*/.deckbd.initrd.enable or something like that?

Sorry if that's a lot of useless stuff to consider / think of, and I know it's pedantry, but I don't want to just declare my pedantry as the way forward, I want to know if I'm missing something, or even if you think I'm wrong :).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(side-note: I'll try to get this tested and verified on my system soon, and make it so the last issues are those tiny little details so it can be merged)

enable = lib.mkOption {
default = cfg.enable;
defaultText = lib.literalExpression "config.jovian.devices.steamdeck.enable";
Comment on lines +12 to +13
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, good thing I'm testing on my system, I missed this.

This can't be enabled by default. It should be considered an opt-in option from the end-user. Especially since it changes the existing semantics of the gamepad during boot.

Suggested change
default = cfg.enable;
defaultText = lib.literalExpression "config.jovian.devices.steamdeck.enable";
default = false;

(And also because it will cause a sudden assertion failure when not already using systemd stage-1.)

type = lib.types.bool;
description = ''
Whether to enable deckbd for initrd password entry.

You must hold the menu button (≡) for a second before entering
your password.
'';
};
bindsTo = lib.mkOption {
inherit (utils.systemdUtils.unitOptions.commonUnitOptions.options.bindsTo) type;
description = ''
systemd units that require deckbd.

For example, if you want controller input in unl0kr, add `config.boot.initrd.systemd.services.unl0kr-ask-password.name`.

Plymouth and the console work by default.
'';
};
debug = lib.mkOption {
default = false;
type = lib.types.bool;
description = ''
Enable debug output for deckbd.

This will log keycodes to the journal.
'';
};
};
};
};
};

config = lib.mkMerge [
(lib.mkIf cfg.fde.deckbd.enable {
assertions = [
{
assertion = cfg.enableDefaultStage1Modules;
message = ''
deckbd cannot be used without the required kernel modules.

jovian.devices.steamdeck.enableDefaultStage1Modules must be enabled.
'';
}
{
assertion = config.boot.initrd.systemd.enable;
message = ''
deckbd requires systemd-in-initrd.

boot.initrd.systemd.enable must be set to true. Note that this may impact other NixOS features.
'';
}
];

jovian.devices.steamdeck.fde.deckbd.bindsTo = [
"systemd-ask-password-console.service"
(lib.mkIf config.boot.plymouth.enable config.systemd.services.systemd-ask-password-plymouth.name)
# Note: Agents that have optional keyboard input, such as unl0kr, should not be added here. The user may not want it.
];

boot.initrd = {
kernelModules = [ "uinput" ];

services.udev.packages = [
(pkgs.writeTextDir "lib/udev/rules.d/70-steam-deck-controller.rules" ''
ACTION=="add", SUBSYSTEM=="input", ENV{ID_VENDOR_ID}=="28de", ENV{ID_MODEL_ID}=="1205", ENV{PRODUCT}=="?*", ENV{ID_INPUT_JOYSTICK}=="1", TAG+="systemd", ENV{SYSTEMD_ALIAS}+="/sys/class/input/input_steam_deck_controller"
'')
];

systemd = {
storePaths = with pkgs; [ deckbd ]; # https://github.com/NixOS/nixpkgs/issues/309316
services.deckbd = {
description = "Steam Deck controller keymapper";
unitConfig.DefaultDependencies = false;
wantedBy = cfg.fde.deckbd.bindsTo;
before = cfg.fde.deckbd.bindsTo;
inherit (cfg.fde.deckbd) bindsTo;
requires = [ "sys-class-input-input_steam_deck_controller.device" ];
after = [ "sys-class-input-input_steam_deck_controller.device" ];
path = with pkgs; [ deckbd ];
environment.G_MESSAGES_DEBUG = lib.mkIf cfg.fde.deckbd.debug "all";
# Note: The lizard_mode parameter does not work as intended. The
# Steam Deck must also be manually put into gamepad mode by holding
# the menu button (≡) for a second.
preStart = "echo 0 > /sys/module/hid_steam/parameters/lizard_mode";
script = "deckbd";
postStop = "echo 1 > /sys/module/hid_steam/parameters/lizard_mode";
Comment on lines +94 to +99
Copy link
Member

@samueldr samueldr Oct 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've dived into trying to understand why it wouldn't work "as expected".

First, here's what the lizard_mode parameter is documented to do:

This driver will disable the lizard mode when the input device is opened
and re-enable it when the input device is closed, so as not to break user
mode behaviour. The lizard_mode parameter can be used to change that.

This comment has aged poorly... That's only true for the original Steam controller... But still has interesting hints as to what is happening.

So, here's one reason why it "doesn't work" for the Steam Deck controller:

... Except that this is only about automatically enabling/disabling lizard mode on open. And that's why things gets kinda confusing fast.

When the parameter is changed live, this happens:

See how there's no check for the hardware in use? So, as long as the device is not opened, the switch will happen... EXCEPT it's not that easy...

If the device has been “forced” into gamepad mode, attempting to set it to lizard mode won't work.

In turn this is what the (≡) button does; it flips that flag:

(Though also notice how if lizard_mode is disabled, it will not be put into lizard_mode...)


So, to recap, for a steam deck, when the device is registered to the driver, if it was not already opened, it will be out into the mode set by the kernel module parameter:

If the device is not opened, changing the kernel module parameter will change the mode of the controller...

... unless it's been “put” into gamepad mode (via the ≡ button)

And the last situation where the lizard_mode parameter takes effect on the Steam Deck is when the hid_ll_driver interface for it is closed (which is of no importance for us here):


Did you get all that? :) (I'm still somewhat confused.)

};
};
};
})
];
}
1 change: 1 addition & 0 deletions modules/devices/steamdeck/hw-support.nix
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ in

# Gamepad
"usbhid"
"hid-steam"
];
boot.initrd.availableKernelModules = [
"nvme"
Expand Down
2 changes: 2 additions & 0 deletions overlay.nix
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,6 @@ rec {

decky-loader = final.callPackage ./pkgs/decky-loader { };
decky-loader-prerelease = final.callPackage ./pkgs/decky-loader/prerelease.nix { };

deckbd = final.callPackage ./pkgs/deckbd { };
}
33 changes: 33 additions & 0 deletions pkgs/deckbd/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{ lib
, stdenv
, fetchFromGitHub
, pkg-config
, glib
, libevdev
}:

stdenv.mkDerivation {
pname = "deckbd";
version = "0-unstable-2024-07-01";

src = fetchFromGitHub {
owner = "Ninlives";
repo = "deckbd";
rev = "1d2c71f2c096fbfa42624dd820a9d11a35c64826";
hash = "sha256-Svp/5Mo/XkiptbTM3E4QhSRxC6rMeF0t3eTq9BUjLbs=";
};

nativeBuildInputs = [ pkg-config ];

buildInputs = [ glib libevdev ];

makeFlags = [ "PREFIX=$(out)" ];

meta = {
description = "Steam Deck controller keymapper";
homepage = "https://github.com/Ninlives/deckbd";
license = lib.licenses.gpl3Only;
mainProgram = "deckbd";
platforms = lib.platforms.linux;
};
}