diff --git a/Makefile.am b/Makefile.am
index b75125e..c70d654 100644
--- a/Makefile.am
+++ b/Makefile.am
src/adv_monitor.h src/adv_monitor.c \
src/battery.h src/battery.c \
src/settings.h src/settings.c \
- src/set.h src/set.c
+ src/set.h src/set.c \
+ src/bearer.h src/bearer.c
src_bluetoothd_LDADD = lib/libbluetooth-internal.la \
gdbus/libgdbus-internal.la \
src/libshared-glib.la \
diff --git a/src/bearer.c b/src/bearer.c
new file mode 100644
index 0000000..d4286b4
--- /dev/null
+++ b/src/bearer.c
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2025 Intel Corporation
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <stdint.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "lib/bluetooth.h"
+#include "lib/mgmt.h"
+
+#include "gdbus/gdbus.h"
+#include "src/shared/util.h"
+
+#include "log.h"
+#include "error.h"
+#include "adapter.h"
+#include "device.h"
+#include "dbus-common.h"
+#include "bearer.h"
+
+struct btd_bearer {
+ struct btd_device *device;
+ uint8_t type;
+ const char *path;
+};
+
+static void bearer_free(void *data)
+{
+ struct btd_bearer *bearer = data;
+
+ free(bearer);
+}
+
+static DBusMessage *bearer_connect(DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ /* TODO */
+ return NULL;
+}
+
+static DBusMessage *bearer_disconnect(DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ /* TODO */
+ return NULL;
+}
+
+static const GDBusMethodTable bearer_methods[] = {
+ { GDBUS_EXPERIMENTAL_ASYNC_METHOD("Connect", NULL, NULL,
+ bearer_connect) },
+ { GDBUS_EXPERIMENTAL_ASYNC_METHOD("Disconnect", NULL, NULL,
+ bearer_disconnect) },
+ {}
+};
+
+static gboolean bearer_get_adapter(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct btd_bearer *bearer = data;
+ struct btd_adapter *adapter = device_get_adapter(bearer->device);
+ const char *path = adapter_get_path(adapter);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
+
+ return TRUE;
+}
+
+static gboolean bearer_get_paired(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct btd_bearer *bearer = data;
+ dbus_bool_t paired = device_is_paired(bearer->device, bearer->type);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &paired);
+
+ return TRUE;
+}
+
+static gboolean bearer_get_bonded(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct btd_bearer *bearer = data;
+ dbus_bool_t bonded = device_is_bonded(bearer->device, bearer->type);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &bonded);
+
+ return TRUE;
+}
+
+static gboolean bearer_get_connected(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct btd_bearer *bearer = data;
+ dbus_bool_t connected = btd_device_bdaddr_type_connected(bearer->device,
+ bearer->type);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &connected);
+
+ return TRUE;
+}
+
+static const GDBusSignalTable bearer_signals[] = {
+ { GDBUS_SIGNAL("Disconnected",
+ GDBUS_ARGS({ "name", "s" }, { "message", "s" })) },
+ { }
+};
+
+static const GDBusPropertyTable bearer_properties[] = {
+ { "Adapter", "o", bearer_get_adapter, NULL, NULL,
+ G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+ { "Paired", "b", bearer_get_paired, NULL, NULL,
+ G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+ { "Bonded", "b", bearer_get_bonded, NULL, NULL,
+ G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+ { "Connected", "b", bearer_get_connected, NULL, NULL,
+ G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+ {}
+};
+
+static const char *bearer_interface(uint8_t type)
+{
+ if (type == BDADDR_BREDR)
+ return BTD_BEARER_BREDR_INTERFACE;
+ else
+ return BTD_BEARER_LE_INTERFACE;
+}
+
+struct btd_bearer *btd_bearer_new(struct btd_device *device, uint8_t type)
+{
+ struct btd_bearer *bearer;
+
+ bearer = new0(struct btd_bearer, 1);
+ bearer->device = device;
+ bearer->type = type;
+ bearer->path = device_get_path(device);
+
+ if (!g_dbus_register_interface(btd_get_dbus_connection(),
+ bearer->path, bearer_interface(type),
+ bearer_methods, bearer_signals,
+ bearer_properties,
+ bearer, bearer_free)) {
+ error("Unable to register BREDR interface");
+ bearer->path = NULL;
+ }
+
+ return bearer;
+}
+
+void btd_bearer_destroy(struct btd_bearer *bearer)
+{
+ if (!bearer)
+ return;
+
+ if (!bearer->path) {
+ bearer_free(bearer);
+ return;
+ }
+
+ g_dbus_unregister_interface(btd_get_dbus_connection(), bearer->path,
+ bearer_interface(bearer->type));
+}
+
+void btd_bearer_paired(struct btd_bearer *bearer)
+{
+ if (!bearer || !bearer->path)
+ return;
+
+ g_dbus_emit_property_changed(btd_get_dbus_connection(), bearer->path,
+ bearer_interface(bearer->type),
+ "Paired");
+}
+
+void btd_bearer_bonded(struct btd_bearer *bearer)
+{
+ if (!bearer || !bearer->path)
+ return;
+
+ g_dbus_emit_property_changed(btd_get_dbus_connection(), bearer->path,
+ bearer_interface(bearer->type),
+ "Bonded");
+}
+
+void btd_bearer_connected(struct btd_bearer *bearer)
+{
+ if (!bearer || !bearer->path)
+ return;
+
+ g_dbus_emit_property_changed(btd_get_dbus_connection(), bearer->path,
+ bearer_interface(bearer->type),
+ "Connected");
+}
+
+void btd_bearer_disconnected(struct btd_bearer *bearer, uint8_t reason)
+{
+ const char *name;
+ const char *message;
+
+ if (!bearer || !bearer->path)
+ return;
+
+ g_dbus_emit_property_changed(btd_get_dbus_connection(), bearer->path,
+ bearer_interface(bearer->type),
+ "Connected");
+
+ switch (reason) {
+ case MGMT_DEV_DISCONN_UNKNOWN:
+ name = "org.bluez.Reason.Unknown";
+ message = "Unspecified";
+ break;
+ case MGMT_DEV_DISCONN_TIMEOUT:
+ name = "org.bluez.Reason.Timeout";
+ message = "Connection timeout";
+ break;
+ case MGMT_DEV_DISCONN_LOCAL_HOST:
+ name = "org.bluez.Reason.Local";
+ message = "Connection terminated by local host";
+ break;
+ case MGMT_DEV_DISCONN_REMOTE:
+ name = "org.bluez.Reason.Remote";
+ message = "Connection terminated by remote user";
+ break;
+ case MGMT_DEV_DISCONN_AUTH_FAILURE:
+ name = "org.bluez.Reason.Authentication";
+ message = "Connection terminated due to authentication failure";
+ break;
+ case MGMT_DEV_DISCONN_LOCAL_HOST_SUSPEND:
+ name = "org.bluez.Reason.Suspend";
+ message = "Connection terminated by local host for suspend";
+ break;
+ default:
+ warn("Unknown disconnection value: %u", reason);
+ name = "org.bluez.Reason.Unknown";
+ message = "Unspecified";
+ }
+
+ g_dbus_emit_signal(btd_get_dbus_connection(), bearer->path,
+ bearer_interface(bearer->type),
+ "Disconnected",
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_STRING, &message,
+ DBUS_TYPE_INVALID);
+}
diff --git a/src/bearer.h b/src/bearer.h
new file mode 100644
index 0000000..4a39dcd
--- /dev/null
+++ b/src/bearer.h
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2025 Intel Corporation
+ *
+ *
+ */
+
+#define BTD_BEARER_BREDR_INTERFACE "org.bluez.Bearer.BREDR1"
+#define BTD_BEARER_LE_INTERFACE "org.bluez.Bearer.LE1"
+
+struct btd_bearer;
+
+struct btd_bearer *btd_bearer_new(struct btd_device *device, uint8_t type);
+void btd_bearer_destroy(struct btd_bearer *bearer);
+
+void btd_bearer_paired(struct btd_bearer *bearer);
+void btd_bearer_bonded(struct btd_bearer *bearer);
+void btd_bearer_connected(struct btd_bearer *bearer);
+void btd_bearer_disconnected(struct btd_bearer *bearer, uint8_t reason);
diff --git a/src/device.c b/src/device.c
index eed2edc..7e62112 100644
--- a/src/device.c
+++ b/src/device.c
#include "eir.h"
#include "settings.h"
#include "set.h"
+#include "bearer.h"
#define DISCONNECT_TIMER 2
#define DISCOVERY_TIMER 1
uint8_t bdaddr_type;
bool rpa;
char *path;
- bool bredr;
- bool le;
+ struct btd_bearer *bredr;
+ struct btd_bearer *le;
bool pending_paired; /* "Paired" waiting for SDP */
bool svc_refreshed;
bool refresh_discovery;
queue_destroy(device->sirks, free);
+ btd_bearer_destroy(device->bredr);
+ btd_bearer_destroy(device->le);
+
g_free(device->local_csrk);
g_free(device->remote_csrk);
free(device->ltk);
}
if (dev->pending_paired) {
+ if (bdaddr_type == BDADDR_BREDR)
+ btd_bearer_paired(dev->bredr);
+ else
+ btd_bearer_paired(dev->le);
+
g_dbus_emit_property_changed(dbus_conn, dev->path,
DEVICE_INTERFACE, "Paired");
dev->pending_paired = false;
dev->eir_uuids = NULL;
if (dev->pending_paired) {
+ if (bdaddr_type == BDADDR_BREDR)
+ btd_bearer_paired(dev->bredr);
+ else
+ btd_bearer_paired(dev->le);
+
g_dbus_emit_property_changed(dbus_conn, dev->path,
DEVICE_INTERFACE, "Paired");
dev->pending_paired = false;
dev->conn_bdaddr_type = dev->bdaddr_type;
/* If this is the first connection over this bearer */
- if (bdaddr_type == BDADDR_BREDR)
+ if (bdaddr_type == BDADDR_BREDR) {
device_set_bredr_support(dev);
- else
+ btd_bearer_connected(dev->bredr);
+ } else {
device_set_le_support(dev, bdaddr_type);
+ btd_bearer_connected(dev->le);
+ }
state->connected = true;
state->initiator = flags & BIT(3);
device->connect = NULL;
}
+ /* Update bearer interface */
+ if (bdaddr_type == BDADDR_BREDR)
+ btd_bearer_disconnected(device->bredr, reason);
+ else
+ btd_bearer_disconnected(device->le, reason);
+
/* Check paired status of both bearers since it's possible to be
* paired but not connected via link key to LTK conversion.
*/
BDADDR_BREDR);
device->bredr_state.paired = false;
paired_status_updated = true;
+ btd_bearer_paired(device->bredr);
}
if (!device->le_state.connected && device->le_state.paired &&
device->bdaddr_type);
device->le_state.paired = false;
paired_status_updated = true;
+ btd_bearer_paired(device->le);
}
/* report change only if both bearers are unpaired */
for (t = techno; *t; t++) {
if (g_str_equal(*t, "BR/EDR"))
- device->bredr = true;
+ device->bredr = btd_bearer_new(device, BDADDR_BREDR);
else if (g_str_equal(*t, "LE"))
- device->le = true;
+ device->le = btd_bearer_new(device, BDADDR_LE_PUBLIC);
else
error("Unknown device technology");
}
device->bdaddr_type = bdaddr_type;
if (bdaddr_type == BDADDR_BREDR)
- device->bredr = true;
+ device->bredr = btd_bearer_new(device, BDADDR_BREDR);
else
- device->le = true;
+ device->le = btd_bearer_new(device, BDADDR_LE_PUBLIC);
storage_dir = btd_adapter_get_storage_dir(adapter);
str = load_cached_name(device, storage_dir, dst);
/* Since this function is only used for LE SMP Identity
* Resolving purposes we can now assume LE is supported.
*/
- device->le = true;
+ if (!device->le)
+ device->le = btd_bearer_new(device, BDADDR_LE_PUBLIC);
/* Remove old address from accept/auto-connect list since its address
* will be changed.
if (btd_opts.mode == BT_MODE_LE || device->bredr)
return;
- device->bredr = true;
+ if (!device->bredr)
+ device->bredr = btd_bearer_new(device, BDADDR_BREDR);
if (device->le)
g_dbus_emit_property_changed(dbus_conn, device->path,
if (btd_opts.mode == BT_MODE_BREDR || device->le)
return;
- device->le = true;
+ if (!device->le)
+ device->le = btd_bearer_new(device, BDADDR_LE_PUBLIC);
+
device->bdaddr_type = bdaddr_type;
g_dbus_emit_property_changed(dbus_conn, device->path,
state->bonded = true;
+ if (bdaddr_type == BDADDR_BREDR)
+ btd_bearer_bonded(device->bredr);
+ else
+ btd_bearer_bonded(device->le);
+
btd_device_set_temporary(device, false);
/* If the other bearer state was already true we don't need to
state->paired = true;
+ if (bdaddr_type == BDADDR_BREDR)
+ btd_bearer_paired(dev->bredr);
+ else
+ btd_bearer_paired(dev->le);
+
/* If the other bearer state was already true we don't need to
* send any property signals.
*/
state->paired = false;
+ if (bdaddr_type == BDADDR_BREDR)
+ btd_bearer_paired(dev->bredr);
+ else
+ btd_bearer_paired(dev->le);
+
/*
* If the other bearer state is still true we don't need to
* send any property signals or remove device.