summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--doc/guix.texi443
-rw-r--r--gnu/services/desktop.scm376
2 files changed, 814 insertions, 5 deletions
diff --git a/doc/guix.texi b/doc/guix.texi
index b83871bf0e..f479fe05ff 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -21378,6 +21378,448 @@ bluetooth keyboard or mouse.
 Users need to be in the @code{lp} group to access the D-Bus service.
 @end deffn
 
+@deffn {Scheme Variable} bluetooth-service-type
+This is the type for the @uref{https://bluez.org/, Linux Bluetooth Protocol
+Stack} (BlueZ) system, which generates the @file{/etc/bluetooth/main.conf}
+configuration file.  The value for this type is a @command{bluetooth-configuration}
+record as in this example:
+
+@lisp
+(service bluetooth-service-type)
+@end lisp
+
+See below for details about @code{bluetooth-configuration}.
+@end deffn
+
+@deftp {Data Type} bluetooth-configuration
+Data type representing the configuration for @code{bluetooth-service}.
+
+@table @asis
+@item @code{bluez} (default: @code{bluez})
+@code{bluez} package to use.
+
+@item @code{name} (default: @code{"BlueZ"})
+Default adapter name.
+
+@item @code{class} (default: @code{#x000000})
+Default device class. Only the major and minor device class bits are considered.
+
+@item @code{discoverable-timeout} (default: @code{180})
+How long to stay in discoverable mode before going back to non-discoverable. The
+value is in seconds.
+
+@item @code{always-pairable?} (default: @code{#f})
+Always allow pairing even if there are no agents registered.
+
+@item @code{pairable-timeout} (default: @code{0})
+How long to stay in pairable mode before going back to non-discoverable. The
+value is in seconds.
+
+@item @code{device-id} (default: @code{#f})
+Use vendor id source (assigner), vendor, product and version information for
+DID profile support. The values are separated by ":" and @var{assigner}, @var{VID},
+@var{PID} and @var{version}.
+
+Possible values are:
+
+@itemize @bullet
+@item
+@code{#f} to disable it,
+
+@item
+@code{"assigner:1234:5678:abcd"}, where @var{assigner} is either @code{usb} (default)
+or @code{bluetooth}.
+
+@end itemize
+
+@item @code{reverse-service-discovery?} (default: @code{#t})
+Do reverse service discovery for previously unknown devices that connect to
+us. For BR/EDR this option is really only needed for qualification since the
+BITE tester doesn't like us doing reverse SDP for some test cases, for LE
+this disables the GATT client functionally so it can be used in system which
+can only operate as peripheral.
+
+@item @code{name-resolving?} (default: @code{#t})
+Enable name resolving after inquiry. Set it to @code{#f} if you don't need
+remote devices name and want shorter discovery cycle.
+
+@item @code{debug-keys?} (default: @code{#f})
+Enable runtime persistency of debug link keys. Default is false which makes
+debug link keys valid only for the duration of the connection that they were
+created for.
+
+@item @code{controller-mode} (default: @code{'dual})
+Restricts all controllers to the specified transport. @code{'dual} means both
+BR/EDR and LE are enabled (if supported by the hardware).
+
+Possible values are: 
+
+@itemize @bullet
+@item
+@code{'dual}
+
+@item
+@code{'bredr}
+
+@item
+@code{'le}
+
+@end itemize
+
+@item @code{multi-profile} (default: @code{'off})
+Enables Multi Profile Specification support. This allows to specify if system
+supports only Multiple Profiles Single Device (MPSD) configuration or both
+Multiple Profiles Single Device (MPSD) and Multiple Profiles Multiple Devices
+(MPMD) configurations.
+
+Possible values are:
+
+@itemize @bullet
+@item
+@code{'off}
+
+@item
+@code{'single}
+
+@item
+@code{'multiple}
+
+@end itemize
+
+@item @code{fast-connectable?} (default: @code{#f})
+Permanently enables the Fast Connectable setting for adapters that support
+it. When enabled other devices can connect faster to us, however the
+tradeoff is increased power consumptions. This feature will fully work only
+on kernel version 4.1 and newer.
+
+@item @code{privacy} (default: @code{'off})
+Default privacy settings.
+
+@itemize @bullet
+@item
+@code{'off}: Disable local privacy
+
+@item
+@code{'network/on}: A device will only accept advertising packets from peer
+devices that contain private addresses. It may not be compatible with some
+legacy devices since it requires the use of RPA(s) all the time
+
+@item
+@code{'device}: A device in device privacy mode is only concerned about the
+privacy of the device and will accept advertising packets from peer devices
+that contain their Identity Address as well as ones that contain a private
+address, even if the peer device has distributed its IRK in the past
+
+@end itemize
+
+and additionally, if @var{controller-mode} is set to @code{'dual}:
+
+@itemize @bullet
+@item
+@code{'limited-network}: Apply Limited Discoverable Mode to advertising, which
+follows the same policy as to BR/EDR that publishes the identity address when
+discoverable, and Network Privacy Mode for scanning
+
+@item
+@code{'limited-device}: Apply Limited Discoverable Mode to advertising, which
+follows the same policy as to BR/EDR that publishes the identity address when
+discoverable, and Device Privacy Mode for scanning.
+
+@end itemize
+
+@item @code{just-works-repairing} (default: @code{'never})
+Specify the policy to the JUST-WORKS repairing initiated by peer.
+
+Possible values:
+@itemize @bullet
+@item
+@code{'never}
+
+@item
+@code{'confirm}
+
+@item
+@code{'always}
+
+@end itemize
+
+@item @code{temporary-timeout} (default: @code{30})
+How long to keep temporary devices around. The value is in seconds. @code{0}
+disables the timer completely.
+
+@item @code{refresh-discovery?} (default: @code{#t})
+Enables the device to issue an SDP request to update known services when
+profile is connected.
+
+@item @code{experimental} (default: @code{#f})
+Enables experimental features and interfaces, alternatively a list of UUIDs
+can be given.
+
+Possible values:
+
+@itemize @bullet
+@item
+@code{#t}
+
+@item
+@code{#f}
+
+@item
+@code{(list (uuid <uuid-1>) (uuid <uuid-2>) ...)}.
+@end itemize
+
+List of possible UUIDs:
+@itemize @bullet
+@item
+@code{d4992530-b9ec-469f-ab01-6c481c47da1c}: BlueZ Experimental Debug,
+
+@item
+@code{671b10b5-42c0-4696-9227-eb28d1b049d6}: BlueZ Experimental Simultaneous Central and Peripheral,
+
+@item
+@code{"15c0a148-c273-11ea-b3de-0242ac130004}: BlueZ Experimental LL privacy,
+
+@item
+@code{330859bc-7506-492d-9370-9a6f0614037f}: BlueZ Experimental Bluetooth Quality Report,
+
+@item
+@code{a6695ace-ee7f-4fb9-881a-5fac66c629af}: BlueZ Experimental Offload Codecs.
+@end itemize
+
+@item @code{remote-name-request-retry-delay} (default: @code{300})
+The duration to avoid retrying to resolve a peer's name, if the previous
+try failed.
+
+@item @code{page-scan-type} (default: @code{#f})
+BR/EDR Page scan activity type.
+
+@item @code{page-scan-interval} (default: @code{#f})
+BR/EDR Page scan activity interval.
+
+@item @code{page-scan-window} (default: @code{#f})
+BR/EDR Page scan activity window.
+
+@item @code{inquiry-scan-type} (default: @code{#f})
+BR/EDR Inquiry scan activity type.
+
+@item @code{inquiry-scan-interval} (default: @code{#f})
+BR/EDR Inquiry scan activity interval.
+
+@item @code{inquiry-scan-window} (default: @code{#f})
+BR/EDR Inquiry scan activity window.
+
+@item @code{link-supervision-timeout} (default: @code{#f})
+BR/EDR Link supervision timeout.
+
+@item @code{page-timeout} (default: @code{#f})
+BR/EDR Page timeout.
+
+@item @code{min-sniff-interval} (default: @code{#f})
+BR/EDR minimum sniff interval.
+
+@item @code{max-sniff-interval} (default: @code{#f})
+BR/EDR maximum sniff interval.
+
+@item @code{min-advertisement-interval} (default: @code{#f})
+LE minimum advertisement interval (used for legacy advertisement only).
+
+@item @code{max-advertisement-interval} (default: @code{#f})
+LE maximum advertisement interval (used for legacy advertisement only).
+
+@item @code{multi-advertisement-rotation-interval} (default: @code{#f})
+LE multiple advertisement rotation interval.
+
+@item @code{scan-interval-auto-connect} (default: @code{#f})
+LE scanning interval used for passive scanning supporting auto connect.
+
+@item @code{scan-window-auto-connect} (default: @code{#f})
+LE scanning window used for passive scanning supporting auto connect.
+
+@item @code{scan-interval-suspend} (default: @code{#f})
+LE scanning interval used for active scanning supporting wake from suspend.
+
+@item @code{scan-window-suspend} (default: @code{#f})
+LE scanning window used for active scanning supporting wake from suspend.
+
+@item @code{scan-interval-discovery} (default: @code{#f})
+LE scanning interval used for active scanning supporting discovery.
+
+@item @code{scan-window-discovery} (default: @code{#f})
+LE scanning window used for active scanning supporting discovery.
+
+@item @code{scan-interval-adv-monitor} (default: @code{#f})
+LE scanning interval used for passive scanning supporting the advertisement monitor APIs.
+
+@item @code{scan-window-adv-monitor} (default: @code{#f})
+LE scanning window used for passive scanning supporting the advertisement monitor APIs.
+
+@item @code{scan-interval-connect} (default: @code{#f})
+LE scanning interval used for connection establishment.
+
+@item @code{scan-window-connect} (default: @code{#f})
+LE scanning window used for connection establishment.
+
+@item @code{min-connection-interval} (default: @code{#f})
+LE default minimum connection interval. This value is superceeded by any specific
+value provided via the Load Connection Parameters interface.
+
+@item @code{max-connection-interval} (default: @code{#f})
+LE default maximum connection interval. This value is superceeded by any specific
+value provided via the Load Connection Parameters interface.
+
+@item @code{connection-latency} (default: @code{#f})
+LE default connection latency. This value is superceeded by any specific
+value provided via the Load Connection Parameters interface.
+
+@item @code{connection-supervision-timeout} (default: @code{#f})
+LE default connection supervision timeout. This value is superceeded by any specific
+value provided via the Load Connection Parameters interface.
+
+@item @code{autoconnect-timeout} (default: @code{#f})
+LE default autoconnect timeout. This value is superceeded by any specific
+value provided via the Load Connection Parameters interface.
+
+@item @code{adv-mon-allowlist-scan-duration} (default: @code{300})
+Allowlist scan duration during interleaving scan. Only used when scanning for ADV
+monitors. The units are msec.
+
+@item @code{adv-mon-no-filter-scan-duration} (default: @code{500})
+No filter scan duration during interleaving scan. Only used when scanning for ADV
+monitors. The units are msec.
+
+@item @code{enable-adv-mon-interleave-scan?} (default: @code{#t})
+Enable/Disable Advertisement Monitor interleave scan for power saving.
+
+@item @code{cache} (default: @code{'always})
+GATT attribute cache.
+
+Possible values are:
+@itemize @bullet
+@item
+@code{'always}: Always cache attributes even for devices not paired, this is
+recommended as it is best for interoperability, with more consistent
+reconnection times and enables proper tracking of notifications for all
+devices
+
+@item
+@code{'yes}: Only cache attributes of paired devices
+
+@item
+@code{'no}: Never cache attributes.
+@end itemize
+
+@item @code{key-size} (default: @code{0})
+Minimum required Encryption Key Size for accessing secured characteristics.
+
+Possible values are:
+@itemize @bullet
+@item
+@code{0}: Don't care
+
+@item
+@code{7 <= N <= 16}
+@end itemize
+
+@item @code{exchange-mtu} (default: @code{517})
+Exchange MTU size. Possible values are:
+
+@itemize @bullet
+@item
+@code{23 <= N <= 517}
+@end itemize
+
+@item @code{att-channels} (default: @code{3})
+Number of ATT channels. Possible values are:
+
+@itemize @bullet
+@item
+@code{1}: Disables EATT
+
+@item
+@code{2 <= N <= 5}
+@end itemize
+
+@item @code{session-mode} (default: @code{'basic})
+AVDTP L2CAP signalling channel mode.
+
+Possible values are:
+
+@itemize @bullet
+@item
+@code{'basic}: Use L2CAP basic mode
+
+@item
+@code{'ertm}: Use L2CAP enhanced retransmission mode.
+@end itemize
+
+@item @code{stream-mode} (default: @code{'basic})
+AVDTP L2CAP transport channel mode.
+
+Possible values are:
+
+@itemize @bullet
+@item
+@code{'basic}: Use L2CAP basic mode
+
+@item
+@code{'streaming}: Use L2CAP streaming mode.
+@end itemize
+
+@item @code{reconnect-uuids} (default: @code{'()})
+The ReconnectUUIDs defines the set of remote services that should try
+to be reconnected to in case of a link loss (link supervision
+timeout). The policy plugin should contain a sane set of values by
+default, but this list can be overridden here. By setting the list to
+empty the reconnection feature gets disabled.
+
+Possible values:
+
+@itemize @bullet
+@item
+@code{'()}
+
+@item
+@code{(list (uuid <uuid-1>) (uuid <uuid-2>) ...)}.
+@end itemize
+
+@item @code{reconnect-attempts} (default: @code{7})
+Defines the number of attempts to reconnect after a link lost. Setting
+the value to 0 disables reconnecting feature.
+
+@item @code{reconnect-intervals} (default: @code{'(1 2 4 8 16 32 64)})
+Defines a list of intervals in seconds to use in between attempts. If
+the number of attempts defined in @var{reconnect-attempts} is bigger than
+the list of intervals the last interval is repeated until the last attempt.
+
+@item @code{auto-enable?} (default: @code{#f})
+Defines option to enable all controllers when they are found. This includes
+adapters present on start as well as adapters that are plugged in later on.
+
+@item @code{resume-delay} (default: @code{2})
+Audio devices that were disconnected due to suspend will be reconnected on
+resume. @var{resume-delay} determines the delay between when the controller
+resumes from suspend and a connection attempt is made. A longer delay is
+better for better co-existence with Wi-Fi. The value is in seconds.
+
+@item @code{rssi-sampling-period} (default: @code{#xFF})
+Default RSSI Sampling Period. This is used when a client registers an
+advertisement monitor and leaves the RSSISamplingPeriod unset.
+
+Possible values are:
+@itemize @bullet
+@item
+@code{#x0}: Report all advertisements
+
+@item
+@code{N = #xXX}: Report advertisements every N x 100 msec (range: #x01 to #xFE)
+
+@item
+@code{#xFF}: Report only one advertisement per device during monitoring period.
+@end itemize
+
+@end table
+@end deftp
+
 @defvr {Scheme Variable} gnome-keyring-service-type
 This is the type of the service that adds the
 @uref{https://wiki.gnome.org/Projects/GnomeKeyring, GNOME Keyring}.  Its
@@ -21412,7 +21854,6 @@ and ``passwd'' is with the value @code{passwd}.
 @end table
 @end deftp
 
-
 @node Sound Services
 @subsection Sound Services
 
diff --git a/gnu/services/desktop.scm b/gnu/services/desktop.scm
index c2ee3a3d80..ecadb16b2f 100644
--- a/gnu/services/desktop.scm
+++ b/gnu/services/desktop.scm
@@ -44,6 +44,7 @@
   #:use-module (gnu system)
   #:use-module (gnu system setuid)
   #:use-module (gnu system shadow)
+  #:use-module (gnu system uuid)
   #:use-module (gnu system pam)
   #:use-module (gnu packages glib)
   #:use-module (gnu packages admin)
@@ -68,6 +69,7 @@
   #:use-module (guix utils)
   #:use-module (guix gexp)
   #:use-module (srfi srfi-1)
+  #:use-module (ice-9 format)
   #:use-module (ice-9 match)
   #:export (<upower-configuration>
             upower-configuration
@@ -403,14 +405,380 @@ site} for more information."
   bluetooth-configuration make-bluetooth-configuration
   bluetooth-configuration?
   (bluez bluetooth-configuration-bluez (default bluez))
-  (auto-enable? bluetooth-configuration-auto-enable? (default #f)))
+
+  ;;; [General]
+  (name bluetooth-configuration-name (default "BlueZ"))
+  (class bluetooth-configuration-class (default #x000000))
+  (discoverable-timeout
+   bluetooth-configuration-discoverable-timeout (default 180))
+  (always-pairable? bluetooth-configuration-always-pairable? (default #f))
+  (pairable-timeout bluetooth-configuration-pairable-timeout (default 0))
+
+  ;;; MAYBE: Exclude into separate <device-id> record-type?
+  (device-id bluetooth-configuration-device-id (default #f))
+  (reverse-service-discovery?
+   bluetooth-configuration-reverse-service-discovery (default #t))
+  (name-resolving? bluetooth-configuration-name-resolving? (default #t))
+  (debug-keys? bluetooth-configuration-debug-keys? (default #f))
+
+  ;;; Possible values:
+  ;;; 'dual, 'bredr, 'le
+  (controller-mode bluetooth-configuration-controller-mode (default 'dual))
+
+  ;;; Possible values:
+  ;;; 'off, 'single, 'multiple
+  (multi-profile bluetooth-configuration-multi-profile (default 'off))
+  (fast-connectable? bluetooth-configuration-fast-connectable? (default #f))
+
+  ;;; Possible values:
+  ;;; for LE mode: 'off, 'network/on, 'device
+  ;;; for Dual mode: 'off, 'network/on', 'device, 'limited-network, 'limited-device
+  ;;; Source: https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/src/main.conf#n68
+  (privacy bluetooth-configuration-privacy (default 'off))
+
+  ;;; Possible values:
+  ;;; 'never, 'confirm, 'always
+  (just-works-repairing
+   bluetooth-configuration-just-works-repairing (default 'never))
+  (temporary-timeout bluetooth-configuration-temporary-timeout (default 30))
+  (refresh-discovery? bluetooth-configuration-refresh-discovery (default #t))
+
+  ;;; Possible values: #t, #f, (uuid <uuid>)
+  ;;; Possible UUIDs:
+  ;;; d4992530-b9ec-469f-ab01-6c481c47da1c (BlueZ Experimental Debug)
+  ;;; 671b10b5-42c0-4696-9227-eb28d1b049d6 (BlueZ Experimental Simultaneous Central and Peripheral)
+  ;;; 15c0a148-c273-11ea-b3de-0242ac130004 (BlueZ Experimental LL privacy)
+  ;;; 330859bc-7506-492d-9370-9a6f0614037f (BlueZ Experimental Bluetooth Quality Report)
+  ;;; a6695ace-ee7f-4fb9-881a-5fac66c629af (BlueZ Experimental Offload Codecs)
+  ;;; Source: https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/src/main.conf#n110
+  (experimental bluetooth-configuration-experimental (default #f))
+  (remote-name-request-retry-delay
+   bluetooth-configuration-remote-name-request-retry-delay (default 300))
+
+  ;;; [BR]
+  (page-scan-type bluetooth-configuration-page-scan-type (default #f))
+  (page-scan-interval bluetooth-configuration-page-scan-interval (default #f))
+  (page-scan-window bluetooth-configuration-page-scan-window (default #f))
+  (inquiry-scan-type bluetooth-configuration-inquiry-scan-type (default #f))
+  (inquiry-scan-interval bluetooth-configuration-inquiry-scan-interval (default #f))
+  (inquiry-scan-window bluetooth-configuration-inquiry-scan-window (default #f))
+  (link-supervision-timeout bluetooth-configuration-link-supervision-timeout (default #f))
+  (page-timeout bluetooth-configuration-page-timeout (default #f))
+  (min-sniff-interval bluetooth-configuration-min-sniff-interval (default #f))
+  (max-sniff-interval bluetooth-configuration-max-sniff-interval (default #f))
+
+  ;;; [LE]
+  (min-advertisement-interval
+   bluetooth-configuration-min-advertisement-interval (default #f))
+  (max-advertisement-interval
+   bluetooth-configuration-max-advertisement-interval (default #f))
+  (multi-advertisement-rotation-interval
+   bluetooth-configuration-multi-advertisement-rotation-interval (default #f))
+  (scan-interval-auto-connect
+   bluetooth-configuration-scan-interval-auto-connect (default #f))
+  (scan-window-auto-connect
+   bluetooth-configuration-scan-window-auto-connect (default #f))
+  (scan-interval-suspend
+   bluetooth-configuration-scan-interval-suspend (default #f))
+  (scan-window-suspend
+   bluetooth-configuration-scan-window-suspend (default #f))
+  (scan-interval-discovery
+   bluetooth-configuration-scan-interval-discovery (default #f))
+  (scan-window-discovery
+   bluetooth-configuration-scan-window-discovery (default #f))
+  (scan-interval-adv-monitor
+   bluetooth-configuration-scan-interval-adv-monitor (default #f))
+  (scan-window-adv-monitor
+   bluetooth-configuration-scan-window-adv-monitor (default #f))
+  (scan-interval-connect
+   bluetooth-configuration-scan-interval-connect (default #f))
+  (scan-window-connect
+   bluetooth-configuration-scan-window-connect (default #f))
+  (min-connection-interval
+   bluetooth-configuration-min-connection-interval (default #f))
+  (max-connection-interval
+   bluetooth-configuration-max-connection-interval (default #f))
+  (connection-latency
+   bluetooth-configuration-connection-latency (default #f))
+  (connection-supervision-timeout
+   bluetooth-configuration-connection-supervision-timeout (default #f))
+  (autoconnect-timeout
+   bluetooth-configuration-autoconnect-timeout (default #f))
+  (adv-mon-allowlist-scan-duration
+   bluetooth-configuration-adv-mon-allowlist-scan-duration (default 300))
+  (adv-mon-no-filter-scan-duration
+   bluetooth-configuration-adv-mon-no-filter-scan-duration (default 500))
+  (enable-adv-mon-interleave-scan?
+   bluetooth-configuration-enable-adv-mon-interleave-scan (default #t))
+
+  ;;; [GATT]
+  ;;; Possible values: 'yes, 'no, 'always
+  (cache bluetooth-configuration-cache (default 'always))
+
+  ;;; Possible values: 7 ... 16, 0 (don't care)
+  (key-size bluetooth-configuration-key-size (default 0))
+
+  ;;; Possible values: 23 ... 517
+  (exchange-mtu bluetooth-configuration-exchange-mtu (default 517))
+
+  ;;; Possible values: 1 ... 5
+  (att-channels bluetooth-configuration-att-channels (default 3))
+
+  ;;; [AVDTP]
+  ;;; Possible values: 'basic, 'ertm
+  (session-mode bluetooth-configuration-session-mode (default 'basic))
+
+  ;;; Possible values: 'basic, 'streaming
+  (stream-mode bluetooth-configuration-stream-mode (default 'basic))
+
+  ;;; [Policy]
+  (reconnect-uuids bluetooth-configuration-reconnect-uuids (default '()))
+  (reconnect-attempts bluetooth-configuration-reconnect-attempts (default 7))
+  (reconnect-intervals bluetooth-configuration-reconnect-intervals
+                       (default (list 1 2 4 8 16 32 64)))
+  (auto-enable? bluetooth-configuration-auto-enable? (default #f))
+  (resume-delay bluetooth-configuration-resume-delay (default 2))
+
+  ;;; [AdvMon]
+  ;;; Possible values:
+  ;;; "0x00", "0xFF",
+  ;;; "N = 0x00" ... "N = 0xFF"
+  ;;; Source: https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/src/main.conf#n286
+  (rssi-sampling-period bluetooth-configuration-rssi-sampling-period
+                        (default #xFF)))
 
 (define (bluetooth-configuration-file config)
   "Return a configuration file for the systemd bluetooth service, as a string."
   (string-append
-   "[Policy]\n"
-   "AutoEnable=" (bool (bluetooth-configuration-auto-enable?
-                        config))))
+   "[General]"
+   "\nName = " (bluetooth-configuration-name config)
+   "\nClass = " (string-append
+                 "0x"
+                 (format #f "~6,'0x" (bluetooth-configuration-class config)))
+   "\nDiscoverableTimeout = " (number->string
+                               (bluetooth-configuration-discoverable-timeout
+                                config))
+   "\nAlwaysPairable = " (bool (bluetooth-configuration-always-pairable?
+                                config))
+   "\nPairableTimeout = " (number->string
+                           (bluetooth-configuration-pairable-timeout
+                            config))
+   (if (bluetooth-configuration-device-id config)
+       (string-append "\nDeviceID = " (bluetooth-configuration-device-id config))
+       "")
+   "\nReverseServiceDiscovery = " (bool
+                                   (bluetooth-configuration-reverse-service-discovery
+                                    config))
+   "\nNameResolving = " (bool (bluetooth-configuration-name-resolving? config))
+   "\nDebugKeys = " (bool (bluetooth-configuration-debug-keys? config))
+   "\nControllerMode = " (symbol->string
+                          (bluetooth-configuration-controller-mode config))
+   "\nMultiProfile = " (symbol->string (bluetooth-configuration-multi-profile
+                                        config))
+   "\nFastConnectable = " (bool (bluetooth-configuration-fast-connectable? config))
+   "\nPrivacy = " (symbol->string (bluetooth-configuration-privacy config))
+   "\nJustWorksRepairing = " (symbol->string
+                              (bluetooth-configuration-just-works-repairing config))
+   "\nTemporaryTimeout = " (number->string
+                            (bluetooth-configuration-temporary-timeout config))
+   "\nRefreshDiscovery = " (bool (bluetooth-configuration-refresh-discovery config))
+   "\nExperimental = " (let ((experimental (bluetooth-configuration-experimental config)))
+                         (cond ((or (eq? experimental #t)
+                                    (eq? experimental #f)) (bool experimental))
+                               ((list? experimental)
+                                (string-join (map uuid->string experimental) ","))))
+   "\nRemoteNameRequestRetryDelay = " (number->string
+                                       (bluetooth-configuration-remote-name-request-retry-delay
+                                        config))
+   "\n[BR]"
+   (if (bluetooth-configuration-page-scan-type config)
+       (string-append
+        "\nPageScanType = "
+        (number->string (bluetooth-configuration-page-scan-type config)))
+       "")
+   (if (bluetooth-configuration-page-scan-interval config)
+       (string-append
+        "\nPageScanInterval = "
+        (number->string (bluetooth-configuration-page-scan-interval config)))
+       "")
+   (if (bluetooth-configuration-page-scan-window config)
+       (string-append
+        "\nPageScanWindow = "
+        (number->string (bluetooth-configuration-page-scan-window config)))
+       "")
+   (if (bluetooth-configuration-inquiry-scan-type config)
+       (string-append
+        "\nInquiryScanType = "
+        (number->string (bluetooth-configuration-inquiry-scan-type config)))
+       "")
+   (if (bluetooth-configuration-inquiry-scan-interval config)
+       (string-append
+        "\nInquiryScanInterval = "
+        (number->string (bluetooth-configuration-inquiry-scan-interval config)))
+       "")
+   (if (bluetooth-configuration-inquiry-scan-window config)
+       (string-append
+        "\nInquiryScanWindow = "
+        (number->string (bluetooth-configuration-inquiry-scan-window config)))
+       "")
+   (if (bluetooth-configuration-link-supervision-timeout config)
+       (string-append
+        "\nLinkSupervisionTimeout = "
+        (number->string (bluetooth-configuration-link-supervision-timeout config)))
+       "")
+   (if (bluetooth-configuration-page-timeout config)
+       (string-append
+        "\nPageTimeout = "
+        (number->string (bluetooth-configuration-page-timeout config)))
+       "")
+   (if (bluetooth-configuration-min-sniff-interval config)
+       (string-append
+        "\nMinSniffInterval = "
+        (number->string (bluetooth-configuration-min-sniff-interval config)))
+       "")
+   (if (bluetooth-configuration-max-sniff-interval config)
+       (string-append
+        "\nMaxSniffInterval = "
+        (number->string (bluetooth-configuration-max-sniff-interval config)))
+       "")
+
+   "\n[LE]"
+   (if (bluetooth-configuration-min-advertisement-interval config)
+       (string-append
+        "\nMinAdvertisementInterval = "
+        (number->string (bluetooth-configuration-min-advertisement-interval config)))
+       "")
+   (if (bluetooth-configuration-max-advertisement-interval config)
+       (string-append
+        "\nMaxAdvertisementInterval = "
+        (number->string (bluetooth-configuration-max-advertisement-interval config)))
+       "")
+   (if (bluetooth-configuration-multi-advertisement-rotation-interval config)
+       (string-append
+        "\nMultiAdvertisementRotationInterval = "
+        (number->string
+         (bluetooth-configuration-multi-advertisement-rotation-interval config)))
+       "")
+   (if (bluetooth-configuration-scan-interval-auto-connect config)
+       (string-append
+        "\nScanIntervalAutoConnect = "
+        (number->string (bluetooth-configuration-scan-interval-auto-connect config)))
+       "")
+   (if (bluetooth-configuration-scan-window-auto-connect config)
+       (string-append
+        "\nScanWindowAutoConnect = "
+        (number->string (bluetooth-configuration-scan-window-auto-connect config)))
+       "")
+   (if (bluetooth-configuration-scan-interval-suspend config)
+       (string-append
+        "\nScanIntervalSuspend = "
+        (number->string (bluetooth-configuration-scan-interval-suspend config)))
+       "")
+   (if (bluetooth-configuration-scan-window-suspend config)
+       (string-append
+        "\nScanWindowSuspend = "
+        (number->string (bluetooth-configuration-scan-window-suspend config)))
+       "")
+   (if (bluetooth-configuration-scan-interval-discovery config)
+       (string-append
+        "\nScanIntervalDiscovery = "
+        (number->string (bluetooth-configuration-scan-interval-discovery config)))
+       "")
+   (if (bluetooth-configuration-scan-window-discovery config)
+       (string-append
+        "\nScanWindowDiscovery = "
+        (number->string (bluetooth-configuration-scan-window-discovery config)))
+       "")
+   (if (bluetooth-configuration-scan-interval-adv-monitor config)
+       (string-append
+        "\nScanIntervalAdvMonitor = "
+        (number->string (bluetooth-configuration-scan-interval-adv-monitor config)))
+       "")
+   (if (bluetooth-configuration-scan-window-adv-monitor config)
+       (string-append
+        "\nScanWindowAdvMonitor = "
+        (number->string (bluetooth-configuration-scan-window-adv-monitor config)))
+       "")
+   (if (bluetooth-configuration-scan-interval-connect config)
+       (string-append
+        "\nScanIntervalConnect = "
+        (number->string (bluetooth-configuration-scan-interval-connect config)))
+       "")
+   (if (bluetooth-configuration-scan-window-connect config)
+       (string-append
+        "\nScanWindowConnect = "
+        (number->string (bluetooth-configuration-scan-window-connect config)))
+       "")
+   (if (bluetooth-configuration-min-connection-interval config)
+       (string-append
+        "\nMinConnectionInterval = "
+        (number->string (bluetooth-configuration-min-connection-interval config)))
+       "")
+   (if (bluetooth-configuration-max-connection-interval config)
+       (string-append
+        "\nMaxConnectionInterval = "
+        (number->string (bluetooth-configuration-max-connection-interval config)))
+       "")
+   (if (bluetooth-configuration-connection-latency config)
+       (string-append
+        "\nConnectionLatency = "
+        (number->string (bluetooth-configuration-connection-latency config)))
+       "")
+   (if (bluetooth-configuration-connection-supervision-timeout config)
+       (string-append
+        "\nConnectionSupervisionTimeout = "
+        (number->string (bluetooth-configuration-connection-supervision-timeout config)))
+       "")
+   (if (bluetooth-configuration-autoconnect-timeout config)
+       (string-append
+        "\nAutoconnecttimeout = "
+        (number->string (bluetooth-configuration-autoconnect-timeout config)))
+       "")
+   "\nAdvMonAllowlistScanDuration = " (number->string
+                                       (bluetooth-configuration-adv-mon-allowlist-scan-duration
+                                        config))
+   "\nAdvMonNoFilterScanDuration = " (number->string
+                                      (bluetooth-configuration-adv-mon-no-filter-scan-duration
+                                       config))
+   "\nEnableAdvMonInterleaveScan = " (number->string
+                                      (if (eq? #t
+                                               (bluetooth-configuration-enable-adv-mon-interleave-scan
+                                                config))
+                                          1 0))
+   
+   "\n[GATT]"
+   "\nCache = " (symbol->string (bluetooth-configuration-cache config))
+   "\nKeySize = " (number->string (bluetooth-configuration-key-size config))
+   "\nExchangeMTU = " (number->string (bluetooth-configuration-exchange-mtu config))
+   "\nChannels = " (number->string (bluetooth-configuration-att-channels config))
+
+   "\n[AVDTP]"
+   "\nSessionMode = " (symbol->string (bluetooth-configuration-session-mode config))
+   "\nStreamMode = " (symbol->string (bluetooth-configuration-stream-mode config))
+
+   "\n[Policy]"
+   (let ((uuids (bluetooth-configuration-reconnect-uuids config)))
+     (if (not (eq? '() uuids))
+         (string-append
+          "\nReconnectUUIDs = "
+          (string-join (map uuid->string uuids) ","))
+         ""))
+   "\nReconnectAttempts = " (number->string
+                             (bluetooth-configuration-reconnect-attempts config))
+   "\nReconnectIntervals = " (string-join
+                              (map number->string
+                                   (bluetooth-configuration-reconnect-intervals
+                                    config))
+                              ",")
+   "\nAutoEnable = " (bool (bluetooth-configuration-auto-enable?
+                            config))
+   "\nResumeDelay = " (number->string (bluetooth-configuration-resume-delay config))
+
+   "\n[AdvMon]"
+   "\nRSSISamplingPeriod = " (string-append
+                              "0x"
+                              (format #f "~2,'0x"
+                                      (bluetooth-configuration-rssi-sampling-period config)))))
 
 (define (bluetooth-directory config)
   (computed-file "etc-bluetooth"