anchor.diff:          };
           description = "Set configuration for system-wide bluetooth (/etc/bluetooth/main.conf).";
         };
  -
  -      extraConfig = mkOption {
  -        type = with types; nullOr lines;
  -        default = null;
  -        example = ''
  -          [General]
  -          ControllerMode = bredr
  -        '';
  -        description = ''
  -          Set additional configuration for system-wide bluetooth (/etc/bluetooth/main.conf).
  -        '';
  -      };
       };
  -
     };
   
     ###### implementation
   
     config = mkIf cfg.enable {
  -    warnings = optional (cfg.extraConfig != null) "hardware.bluetooth.`extraConfig` is deprecated, please use hardware.bluetooth.`config`.";
  +    environment.systemPackages = [ package ]
  +      ++ optional cfg.hsphfpd.enable pkgs.hsphfpd;
   
  -    hardware.bluetooth.config = {
  -      Policy = {
  -        AutoEnable = mkDefault cfg.powerOnBoot;
  -      };
  -    };
  -
  -    environment.systemPackages = [ bluez-bluetooth ]
  -      ++ optionals cfg.hsphfpd.enable [ pkgs.hsphfpd ];
  -
  -    environment.etc."bluetooth/main.conf"= {
  -      source = pkgs.writeText "main.conf"
  -        (generators.toINI { } cfg.config + optionalString (cfg.extraConfig != null) cfg.extraConfig);
  -    };
  -
  -    services.udev.packages = [ bluez-bluetooth ];
  -    services.dbus.packages = [ bluez-bluetooth ]
  -      ++ optionals cfg.hsphfpd.enable [ pkgs.hsphfpd ];
  -    systemd.packages       = [ bluez-bluetooth ];
  +    environment.etc."bluetooth/main.conf".source =
  +      cfgFmt.generate "main.conf" (recursiveUpdate defaults cfg.settings);
  +    services.udev.packages = [ package ];
  +    services.dbus.packages = [ package ]
  +      ++ optional cfg.hsphfpd.enable pkgs.hsphfpd;
  +    systemd.packages = [ package ];
   
       systemd.services = {
  -      bluetooth = {
  +      bluetooth =
  +        let
  +          # `man bluetoothd` will refer to main.conf in the nix store but bluez
  +          # will in fact load the configuration file at /etc/bluetooth/main.conf
  +          # so force it here to avoid any ambiguity and things suddenly breaking
  +          # if/when the bluez derivation is changed.
  +          args = [ "-f /etc/bluetooth/main.conf" ]
  +            ++ optional hasDisabledPlugins
  +            "--noplugin=${concatStringsSep "," cfg.disabledPlugins}";
  +        in
  +        {
  +          wantedBy = [ "bluetooth.target" ];
  +          aliases = [ "dbus-org.bluez.service" ];
  +          serviceConfig.ExecStart = [
  +            ""
  +            "${package}/libexec/bluetooth/bluetoothd ${escapeShellArgs " " args}"
  +          ];
  +          # restarting can leave people without a mouse/keyboard
  +          unitConfig.X-RestartIfChanged = false;
  +        };
  +    }
  +    // (optionalAttrs cfg.hsphfpd.enable {
  +      hsphfpd = {
  +        after = [ "bluetooth.service" ];
  +        requires = [ "bluetooth.service" ];
           wantedBy = [ "bluetooth.target" ];
  -        aliases  = [ "dbus-org.bluez.service" ];
  -        # restarting can leave people without a mouse/keyboard
  -        unitConfig.X-RestartIfChanged = false;
  +
  +        description = "A prototype implementation used for connecting HSP/HFP Bluetooth devices";
  +        serviceConfig.ExecStart = "${pkgs.hsphfpd}/bin/hsphfpd.pl";
         };
  -    }
  -      // (optionalAttrs cfg.hsphfpd.enable {
  -        hsphfpd = {
  -          after = [ "bluetooth.service" ];
  -          requires = [ "bluetooth.service" ];
  -          wantedBy = [ "multi-user.target" ];
  -
  -          description = "A prototype implementation used for connecting HSP/HFP Bluetooth devices";
  -          serviceConfig.ExecStart = "${pkgs.hsphfpd}/bin/hsphfpd.pl";
  -        };
  -      })
  -      ;
  +    });
   
       systemd.user.services = {
         obex.aliases = [ "dbus-org.bluez.obex.service" ];
       }
  -      // (optionalAttrs cfg.hsphfpd.enable {
  -        telephony_client = {
  -          wantedBy = [ "default.target"];
  +    // optionalAttrs cfg.hsphfpd.enable {
  +      telephony_client = {
  +        wantedBy = [ "default.target" ];
   
  -          description = "telephony_client for hsphfpd";
  -          serviceConfig.ExecStart = "${pkgs.hsphfpd}/bin/telephony_client.pl";
  -        };
  -      })
  -      ;
  -
  +        description = "telephony_client for hsphfpd";
  +        serviceConfig.ExecStart = "${pkgs.hsphfpd}/bin/telephony_client.pl";
  +      };
  +    };
     };
  -
   }
  
positive.code:         '';
        };
  
        config = mkOption {
          type = with types; attrsOf (attrsOf (oneOf [ bool int str ]));
          example = {
            General = {
              ControllerMode = "bredr";
  
negative.code: { config, lib, pkgs, ... }:
  let
    cfg = config.hardware.bluetooth;
    package = cfg.package;
  
    inherit (lib)
      mkDefault mkEnableOption mkIf mkOption
      mkRenamedOptionModule mkRemovedOptionModule
      concatStringsSep escapeShellArgs
      optional optionals optionalAttrs recursiveUpdate types;
  
    cfgFmt = pkgs.formats.ini { };
  
    # bluez will complain if some of the sections are not found, so just make them
    # empty (but present in the file) for now
    defaults = {
      General.ControllerMode = "dual";
      Controller = { };
      GATT = { };
      Policy.AutoEnable = cfg.powerOnBoot;
    };
  
    hasDisabledPlugins = builtins.length cfg.disabledPlugins > 0;
  
  in
  {
    imports = [
      (mkRenamedOptionModule [ "hardware" "bluetooth" "config" ] [ "hardware" "bluetooth" "settings" ])
      (mkRemovedOptionModule [ "hardware" "bluetooth" "extraConfig" ] ''
        Use hardware.bluetooth.settings instead.
  
        This is part of the general move to use structured settings instead of raw
        text for config as introduced by RFC0042:
        https://github.com/NixOS/rfcs/blob/master/rfcs/0042-config-option.md
      '')
    ];
  
    ###### interface
  
  