at 23.05-pre 4.4 kB view raw
1{ config, lib, pkgs, ... }: 2let 3 cfg = config.hardware.bluetooth; 4 package = cfg.package; 5 6 inherit (lib) 7 mkDefault mkEnableOption mkIf mkOption 8 mkRenamedOptionModule mkRemovedOptionModule 9 concatStringsSep escapeShellArgs literalExpression 10 optional optionals optionalAttrs recursiveUpdate types; 11 12 cfgFmt = pkgs.formats.ini { }; 13 14 defaults = { 15 General.ControllerMode = "dual"; 16 Policy.AutoEnable = cfg.powerOnBoot; 17 }; 18 19 hasDisabledPlugins = builtins.length cfg.disabledPlugins > 0; 20 21in 22{ 23 imports = [ 24 (mkRenamedOptionModule [ "hardware" "bluetooth" "config" ] [ "hardware" "bluetooth" "settings" ]) 25 (mkRemovedOptionModule [ "hardware" "bluetooth" "extraConfig" ] '' 26 Use hardware.bluetooth.settings instead. 27 28 This is part of the general move to use structured settings instead of raw 29 text for config as introduced by RFC0042: 30 https://github.com/NixOS/rfcs/blob/master/rfcs/0042-config-option.md 31 '') 32 ]; 33 34 ###### interface 35 36 options = { 37 38 hardware.bluetooth = { 39 enable = mkEnableOption (lib.mdDoc "support for Bluetooth"); 40 41 hsphfpd.enable = mkEnableOption (lib.mdDoc "support for hsphfpd[-prototype] implementation"); 42 43 powerOnBoot = mkOption { 44 type = types.bool; 45 default = true; 46 description = lib.mdDoc "Whether to power up the default Bluetooth controller on boot."; 47 }; 48 49 package = mkOption { 50 type = types.package; 51 default = pkgs.bluez; 52 defaultText = literalExpression "pkgs.bluez"; 53 example = literalExpression "pkgs.bluezFull"; 54 description = lib.mdDoc '' 55 Which BlueZ package to use. 56 57 ::: {.note} 58 Use the `pkgs.bluezFull` package to enable all 59 bluez plugins. 60 ::: 61 ''; 62 }; 63 64 disabledPlugins = mkOption { 65 type = types.listOf types.str; 66 default = [ ]; 67 description = lib.mdDoc "Built-in plugins to disable"; 68 }; 69 70 settings = mkOption { 71 type = cfgFmt.type; 72 default = { }; 73 example = { 74 General = { 75 ControllerMode = "bredr"; 76 }; 77 }; 78 description = lib.mdDoc "Set configuration for system-wide bluetooth (/etc/bluetooth/main.conf)."; 79 }; 80 }; 81 }; 82 83 ###### implementation 84 85 config = mkIf cfg.enable { 86 environment.systemPackages = [ package ] 87 ++ optional cfg.hsphfpd.enable pkgs.hsphfpd; 88 89 environment.etc."bluetooth/main.conf".source = 90 cfgFmt.generate "main.conf" (recursiveUpdate defaults cfg.settings); 91 services.udev.packages = [ package ]; 92 services.dbus.packages = [ package ] 93 ++ optional cfg.hsphfpd.enable pkgs.hsphfpd; 94 systemd.packages = [ package ]; 95 96 systemd.services = { 97 bluetooth = 98 let 99 # `man bluetoothd` will refer to main.conf in the nix store but bluez 100 # will in fact load the configuration file at /etc/bluetooth/main.conf 101 # so force it here to avoid any ambiguity and things suddenly breaking 102 # if/when the bluez derivation is changed. 103 args = [ "-f" "/etc/bluetooth/main.conf" ] 104 ++ optional hasDisabledPlugins 105 "--noplugin=${concatStringsSep "," cfg.disabledPlugins}"; 106 in 107 { 108 wantedBy = [ "bluetooth.target" ]; 109 aliases = [ "dbus-org.bluez.service" ]; 110 serviceConfig.ExecStart = [ 111 "" 112 "${package}/libexec/bluetooth/bluetoothd ${escapeShellArgs args}" 113 ]; 114 # restarting can leave people without a mouse/keyboard 115 unitConfig.X-RestartIfChanged = false; 116 }; 117 } 118 // (optionalAttrs cfg.hsphfpd.enable { 119 hsphfpd = { 120 after = [ "bluetooth.service" ]; 121 requires = [ "bluetooth.service" ]; 122 wantedBy = [ "bluetooth.target" ]; 123 124 description = "A prototype implementation used for connecting HSP/HFP Bluetooth devices"; 125 serviceConfig.ExecStart = "${pkgs.hsphfpd}/bin/hsphfpd.pl"; 126 }; 127 }); 128 129 systemd.user.services = { 130 obex.aliases = [ "dbus-org.bluez.obex.service" ]; 131 } 132 // optionalAttrs cfg.hsphfpd.enable { 133 telephony_client = { 134 wantedBy = [ "default.target" ]; 135 136 description = "telephony_client for hsphfpd"; 137 serviceConfig.ExecStart = "${pkgs.hsphfpd}/bin/telephony_client.pl"; 138 }; 139 }; 140 }; 141}