[PATCH BlueZ] plugins: Add new plugin to fix airpods pairing

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Apple Airpods are discoverable and pairable in BREDR mode, but also
advertise in unconnectable mode in LE with the same Public address, at the
same time. As the pairing process uses the latest seen address, sometimes
it uses the LE Public address to pair, which fails.

This commit adds a new adapter driver plugin which force the BREDR last
seen time on LE Public address device found event related to an Apple
device, allowing pairing process to always use the BREDR.

This commit is based on proposal
https://lore.kernel.org/all/20240103101328.1812899-1-clancy_shang@xxxxxxx/
---
 Makefile.plugins  |   3 ++
 plugins/airpods.c | 132 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 135 insertions(+)
 create mode 100644 plugins/airpods.c

diff --git a/Makefile.plugins b/Makefile.plugins
index 4aa2c9c92..2ebd8aaf6 100644
--- a/Makefile.plugins
+++ b/Makefile.plugins
@@ -11,6 +11,9 @@ builtin_sources += plugins/autopair.c
 builtin_modules += policy
 builtin_sources += plugins/policy.c
 
+builtin_modules += airpods
+builtin_sources += plugins/airpods.c
+
 if ADMIN
 builtin_modules += admin
 builtin_sources += plugins/admin.c
diff --git a/plugins/airpods.c b/plugins/airpods.c
new file mode 100644
index 000000000..5043f0cca
--- /dev/null
+++ b/plugins/airpods.c
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2024 Frédéric Danis <frederic.danis@xxxxxxxxxxxxx>
+ *
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+
+#include <glib.h>
+
+#include "bluetooth/bluetooth.h"
+
+#include "lib/mgmt.h"
+#include "src/plugin.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/eir.h"
+#include "src/log.h"
+#include "src/shared/mgmt.h"
+#include "src/shared/util.h"
+
+#define APPLE_INC_VENDOR_ID 0x004c
+
+static struct mgmt *mgmt;
+
+static bool eir_msd_is_apple_inc(GSList *msd_list)
+{
+	GSList *msd_l, *msd_next;
+
+	for (msd_l = msd_list; msd_l != NULL; msd_l = msd_next) {
+		const struct eir_msd *msd = msd_l->data;
+
+		msd_next = g_slist_next(msd_l);
+
+		if (msd->company == APPLE_INC_VENDOR_ID)
+			return true;
+	}
+
+	return false;
+}
+
+static void airpods_device_found_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct btd_device *dev;
+	const struct mgmt_ev_device_found *ev = param;
+	struct btd_adapter *adapter = user_data;
+	uint16_t eir_len;
+	uint32_t flags = le32_to_cpu(ev->flags);
+	struct eir_data eir_data;
+
+	dev = btd_adapter_find_device(adapter,  &ev->addr.bdaddr,
+					ev->addr.type);
+	if (!dev)
+		return;
+
+	if (length < sizeof(*ev)) {
+		warn("Too short device found event (%u bytes)", length);
+		return;
+	}
+
+	eir_len = btohs(ev->eir_len);
+	if (length != sizeof(*ev) + eir_len) {
+		warn("Device found event size mismatch (%u != %zu)",
+					length, sizeof(*ev) + eir_len);
+		return;
+	}
+
+	if (eir_len == 0)
+		return;
+
+	memset(&eir_data, 0, sizeof(eir_data));
+	eir_parse(&eir_data, ev->eir, eir_len);
+
+	if (eir_msd_is_apple_inc(eir_data.msd_list) &&
+				(flags & MGMT_DEV_FOUND_NOT_CONNECTABLE) &&
+				(ev->addr.type == BDADDR_LE_PUBLIC)) {
+		DBG("Force BREDR last seen");
+		device_set_bredr_support(dev);
+		device_update_last_seen(dev, BDADDR_BREDR, true);
+	}
+}
+
+static int airpods_probe(struct btd_adapter *adapter)
+{
+	if (!mgmt)
+		mgmt = mgmt_new_default();
+
+	if (!mgmt) {
+		fprintf(stderr, "Failed to open management socket\n");
+		return 0;
+	}
+
+	mgmt_register(mgmt, MGMT_EV_DEVICE_FOUND,
+					btd_adapter_get_index(adapter),
+					airpods_device_found_callback,
+					adapter, NULL);
+
+	return 0;
+}
+
+static void airpods_remove(struct btd_adapter *adapter)
+{
+	mgmt_unregister_index(mgmt, btd_adapter_get_index(adapter));
+}
+
+static struct btd_adapter_driver airpods_driver = {
+	.name	= "airpods",
+	.probe	= airpods_probe,
+	.remove	= airpods_remove,
+};
+
+static int airpods_init(void)
+{
+	return btd_register_adapter_driver(&airpods_driver);
+}
+
+static void airpods_exit(void)
+{
+	btd_unregister_adapter_driver(&airpods_driver);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(airpods, VERSION,
+		BLUETOOTH_PLUGIN_PRIORITY_LOW, airpods_init, airpods_exit)
-- 
2.34.1





[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux