Reuse connected BlueZ GATT before requesting another bearer
This commit is contained in:
1 parent
eafa1297dd
commit
13094ae7d6
2 files changed
+50
-14
No files matched your search
+26
-9
@@ -57,7 +57,8 @@ struct ble_backend {
|
||||
};
|
||||
static void bus_update(struct ble_backend* b);
|
||||
static void bus_timer(void* arg);
|
||||
static void query_link(struct bluez_link* l);
|
||||
static int query_link(struct bluez_link* l);
|
||||
static int connected(sd_bus_message* m, void* arg, sd_bus_error* error);
|
||||
static void beacon_update(struct ble_backend* b);
|
||||
static void fail(struct ble_backend* b, const char* where, int error) {
|
||||
if (b->failed) return;
|
||||
@@ -135,7 +136,8 @@ static struct bluez_link* incoming(struct ble_backend* b, const char* device) {
|
||||
return l;
|
||||
}
|
||||
static void link_error(struct bluez_link* l, const char* reason) {
|
||||
DEBUG_WARN(CAT, "BLE BlueZ link=%llu failed: %s", (unsigned long long)l->id, reason);
|
||||
DEBUG_WARN(CAT, "BLE BlueZ link=%llu failed: %s device=%s outgoing=%d connected=%d resolved=%d opened=%d",
|
||||
(unsigned long long)l->id, reason, l->device, l->outgoing, l->connected, l->resolved, l->opened);
|
||||
ble_backend_closed(l->backend->owner, l->id, reason);
|
||||
}
|
||||
static void notify_read(int fd, void* arg) {
|
||||
@@ -408,7 +410,8 @@ static int properties(struct ble_backend* b, const char* path, const char* inter
|
||||
if (resolved >= 0) l->resolved = resolved;
|
||||
DEBUG_DEBUG(CAT, "BLE BlueZ readiness link=%llu connected=%d resolved=%d opened=%d",
|
||||
(unsigned long long)l->id, l->connected, l->resolved, l->opened);
|
||||
if (l->connected && l->resolved && !l->opened && !l->query) query_link(l);
|
||||
if (l->connected && l->resolved && !l->opened && !l->query && query_link(l) < 0)
|
||||
link_error(l, "discovery request failed");
|
||||
}
|
||||
if (live && have_data && strlen(path) < BLE_ENDPOINT_MAX) {
|
||||
struct ble_endpoint ep = {0}; strcpy(ep.id, path); ble_backend_broadcast(b->owner, &ep, data, data_size, rssi);
|
||||
@@ -456,6 +459,17 @@ static int queried(sd_bus_message* m, void* arg, sd_bus_error* error) {
|
||||
if (r < 0) { link_error(l, "service discovery failed"); return 0; }
|
||||
DEBUG_DEBUG(CAT, "BLE BlueZ GATT snapshot link=%llu connected=%d resolved=%d RX=%d TX=%d INVITE=%d ACK=%d mtu=%u known=%d",
|
||||
(unsigned long long)l->id, l->connected, l->resolved, !!l->rx[0], !!l->tx[0], !!l->invite[0], !!l->ack[0], l->mtu, l->mtu_known);
|
||||
/* Connect уже подключённого dual-mode Device1 может запустить BR/EDR вместо использования готового LE. */
|
||||
if (!l->connected && !l->connect) {
|
||||
DEBUG_INFO(CAT, "BLE BlueZ Connect requested link=%llu device=%s: no existing connection", (unsigned long long)l->id, l->device);
|
||||
r = sd_bus_call_method_async(l->backend->bus, &l->connect, "org.bluez", l->device, DEVICE_IF, "Connect", connected, l, "");
|
||||
if (r < 0) {
|
||||
DEBUG_ERROR(CAT, "BLE Connect request link=%llu: %s", (unsigned long long)l->id, strerror(-r));
|
||||
link_error(l, "connect request failed");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (l->connected) DEBUG_DEBUG(CAT, "BLE BlueZ reusing connection link=%llu resolved=%d", (unsigned long long)l->id, l->resolved);
|
||||
if (l->connected && l->resolved && l->rx[0] && l->tx[0] && l->invite[0] && l->ack[0] && !l->subscribe && !l->opened) {
|
||||
if (!l->mtu_known) DEBUG_WARN(CAT, "BLE BlueZ MTU unavailable link=%llu: using ATT minimum 23", (unsigned long long)l->id);
|
||||
r = sd_bus_call_method_async(l->backend->bus, &l->subscribe, "org.bluez", l->tx, CHAR_IF, "StartNotify", subscribed, l, "");
|
||||
@@ -463,16 +477,19 @@ static int queried(sd_bus_message* m, void* arg, sd_bus_error* error) {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
static void query_link(struct bluez_link* l) {
|
||||
if (l->query || l->subscribe || l->opened) return;
|
||||
/* Сначала snapshot: существующее LE соединение не требует повторного Device1.Connect. */
|
||||
static int query_link(struct bluez_link* l) {
|
||||
if (l->query || l->subscribe || l->opened) return 0;
|
||||
int r = sd_bus_call_method_async(l->backend->bus, &l->query, "org.bluez", "/", "org.freedesktop.DBus.ObjectManager",
|
||||
"GetManagedObjects", queried, l, "");
|
||||
if (r < 0) link_error(l, "discovery request failed");
|
||||
if (r < 0) DEBUG_ERROR(CAT, "BLE discovery request link=%llu: %s", (unsigned long long)l->id, strerror(-r));
|
||||
return r;
|
||||
}
|
||||
static int connected(sd_bus_message* m, void* arg, sd_bus_error* error) {
|
||||
(void)error; struct bluez_link* l = arg; l->connect = sd_bus_slot_unref(l->connect);
|
||||
if (response_error(m, "Connect")) { link_error(l, "connect failed"); return 0; }
|
||||
query_link(l); return 0;
|
||||
if (query_link(l) < 0) link_error(l, "discovery request failed");
|
||||
return 0;
|
||||
}
|
||||
static int signal_changed(sd_bus_message* m, void* arg, sd_bus_error* error) {
|
||||
(void)error; struct ble_backend* b = arg; const char* iface;
|
||||
@@ -685,8 +702,8 @@ int ble_backend_connect(struct ble_backend* b, ble_link_id id, const struct ble_
|
||||
}
|
||||
struct bluez_link* l = alloc_link(b, id, ep->id, 1);
|
||||
if (!l) { DEBUG_WARN(CAT, "BLE BlueZ duplicate/limit endpoint=%s", ep->id); return BLE_ERROR; }
|
||||
int r = sd_bus_call_method_async(b->bus, &l->connect, "org.bluez", l->device, DEVICE_IF, "Connect", connected, l, "");
|
||||
if (r < 0) { DEBUG_ERROR(CAT, "BLE Connect request: %s", strerror(-r)); ble_backend_close(b, id); return BLE_ERROR; }
|
||||
int r = query_link(l);
|
||||
if (r < 0) { ble_backend_close(b, id); return BLE_ERROR; }
|
||||
bus_update(b); return BLE_OK;
|
||||
}
|
||||
static int written(sd_bus_message* m, void* arg, sd_bus_error* error) {
|
||||
|
||||
@@ -23,7 +23,8 @@ static int ready, broadcasts, ready_links, advertisement_count, closed_links;
|
||||
static ble_link_id links[4];
|
||||
static unsigned commands, requests, ack_requests, ack_values;
|
||||
static int ack_fds[2] = {-1, -1};
|
||||
static int remote_resolved;
|
||||
static int remote_resolved, remote_connected = 1;
|
||||
static unsigned remote_connects;
|
||||
struct reply { sd_bus_slot* slot; int done, fd, failed; };
|
||||
static void poll_all(void) {
|
||||
for (int i = 0; i < 500; i++) {
|
||||
@@ -105,10 +106,17 @@ static int remote_state(sd_bus* b, const char* path, const char* i, const char*
|
||||
sd_bus_message* m, void* arg, sd_bus_error* e) {
|
||||
(void)b; (void)path; (void)i; (void)arg; (void)e;
|
||||
if (!strcmp(name, "ManufacturerData")) return sd_bus_message_append(m, "a{qv}", 1, 0xffff, "ay", 3, 'a', 'b', 'c');
|
||||
return sd_bus_message_append(m, "b", !strcmp(name, "Connected") || remote_resolved);
|
||||
return sd_bus_message_append(m, "b", !strcmp(name, "Connected") ? remote_connected : remote_resolved);
|
||||
}
|
||||
/* Повторный Connect существующего ACL недопустим: BlueZ может начать другой bearer (BR/EDR). */
|
||||
static int remote_connect(sd_bus_message* m, void* arg, sd_bus_error* e) {
|
||||
(void)arg; (void)e; CHECK(!remote_connected);
|
||||
remote_connects++; remote_connected = 1;
|
||||
CHECK(sd_bus_emit_properties_changed(bus, REMOTE, "org.bluez.Device1", "Connected", NULL) >= 0);
|
||||
return sd_bus_reply_method_return(m, "");
|
||||
}
|
||||
static const sd_bus_vtable device_vtable[] = {
|
||||
SD_BUS_VTABLE_START(0), SD_BUS_METHOD("Connect", "", "", simple, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_VTABLE_START(0), SD_BUS_METHOD("Connect", "", "", remote_connect, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_PROPERTY("Connected", "b", remote_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("ServicesResolved", "b", remote_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("ManufacturerData", "a{qv}", remote_state, 0, 0),
|
||||
@@ -272,18 +280,29 @@ int main(void) {
|
||||
struct ble_endpoint ep = {REMOTE};
|
||||
CHECK(ble_connect(transport, &ep, 3)); poll_all();
|
||||
/* Кеш UUID доступен сразу, но до ServicesResolved нельзя объявлять GATT готовым с прежним MTU23. */
|
||||
CHECK(ready_links == 3 && commands == 0);
|
||||
CHECK(ready_links == 3 && commands == 0 && remote_connects == 0);
|
||||
remote_resolved = 1;
|
||||
CHECK(sd_bus_emit_properties_changed(bus, REMOTE, "org.bluez.Device1", "ServicesResolved", NULL) >= 0);
|
||||
poll_all(); CHECK(ready_links == 4 && commands == 1);
|
||||
CHECK(!ble_tx_put(transport, links[3], frame(0xe5)));
|
||||
CHECK(!ble_channel_tx_put(transport, links[3], BLE_CHANNEL_INVITE, frame(0xe6)));
|
||||
poll_all(); CHECK(commands == 2 && requests == 1 && ack_requests >= 1); /* MTU517 и ACK Write Command. */
|
||||
/* Новый ACL требует ровно одного Connect; discovery ждёт ServicesResolved. */
|
||||
ble_disconnect(transport, links[3]); poll_all(); remote_connected = remote_resolved = 0;
|
||||
CHECK(ble_connect(transport, &ep, 3)); poll_all();
|
||||
CHECK(remote_connects == 1 && ready_links == 4 && commands == 2);
|
||||
remote_resolved = 1;
|
||||
CHECK(sd_bus_emit_properties_changed(bus, REMOTE, "org.bluez.Device1", "ServicesResolved", NULL) >= 0);
|
||||
poll_all(); CHECK(remote_connects == 1 && ready_links == 5 && commands == 3);
|
||||
/* Следующий GATT на уже готовом ACL сразу использует snapshot без Connect. */
|
||||
ble_disconnect(transport, links[3]); poll_all();
|
||||
CHECK(ble_connect(transport, &ep, 3)); poll_all();
|
||||
CHECK(remote_connects == 1 && ready_links == 6 && commands == 4);
|
||||
ble_stop(transport); power = ble_get_advertising_power(transport); CHECK(power.max_dbm == 127 && power.can_set == -1);
|
||||
ble_destroy(transport); uasync_destroy(ua, 0);
|
||||
for (int i = 0; i < 5; i++) sd_bus_slot_unref(remote[i]);
|
||||
sd_bus_slot_unref(manager); sd_bus_slot_unref(adapter); sd_bus_slot_unref(gatt); sd_bus_slot_unref(adv); sd_bus_close_unref(bus);
|
||||
CHECK(u_get_allocated_count() == before);
|
||||
puts("BlueZ private-bus tests PASS (DATA/ACK commands and notifications, INVITE indications, two clients, teardown)");
|
||||
puts("BlueZ private-bus tests PASS (existing/new ACL, DATA/ACK commands, INVITE indications, two clients, teardown)");
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user