diff --git a/lib/ble/ble_winrt.cpp b/lib/ble/ble_winrt.cpp index d805fab36..55d5d46b9 100644 --- a/lib/ble/ble_winrt.cpp +++ b/lib/ble/ble_winrt.cpp @@ -743,7 +743,7 @@ struct WinState : std::enable_shared_from_this { l->value_changed = s.watch_value(id, l->tx, BLE_WIRE_DATA); l->invite_changed = s.watch_value(id, l->invite, BLE_WIRE_INVITE); l->ack_changed = s.watch_value(id, l->ack, BLE_WIRE_ACK); - s.subscribe_channel(id, BLE_WIRE_DATA); + s.subscribe_channel(id, BLE_WIRE_DATA, true); }); } GattCharacteristic::ValueChanged_revoker watch_value(ble_link_id id, GattCharacteristic const& characteristic, enum ble_wire_channel channel) { @@ -752,7 +752,12 @@ struct WinState : std::enable_shared_from_this { DEBUG_DEBUG(CAT, "BLE WinRT register ValueChanged link=%llu channel=%d", (unsigned long long)id, channel); return characteristic.ValueChanged(auto_revoke, [weak, id, channel](auto const&, GattValueChangedEventArgs const& args) { if (auto s = weak.lock()) s->post("GattCharacteristic.ValueChanged", id, [id, channel, args](WinState& s) { - if (!s.link(id)) return; + auto* l = s.link(id); if (!l) return; + if (!l->opened) { + DEBUG_DEBUG(CAT, "BLE WinRT notification ignored during CCC preparation link=%llu channel=%d", + (unsigned long long)id, channel); + return; + } s.step("ValueChanged.CharacteristicValue/DataReader", id); auto data = bytes(args.CharacteristicValue()); DEBUG_TRACE(CAT, "BLE WinRT RX value link=%llu channel=%d bytes=%zu", (unsigned long long)id, channel, data.size()); @@ -760,24 +765,29 @@ struct WinState : std::enable_shared_from_this { }); }); } - void subscribe_channel(ble_link_id id, enum ble_wire_channel channel) { + // Сбросить весь набор CCC до включения: ACL и удалённые CCC могут пережить прежнюю сессию. + void subscribe_channel(ble_link_id id, enum ble_wire_channel channel, bool reset) { auto* l = link(id); auto characteristic = channel == BLE_WIRE_ACK ? l->ack : channel == BLE_WIRE_DATA ? l->tx : l->invite; - auto mode = channel == BLE_WIRE_INVITE ? GattClientCharacteristicConfigurationDescriptorValue::Indicate : - GattClientCharacteristicConfigurationDescriptorValue::Notify; + auto mode = reset ? GattClientCharacteristicConfigurationDescriptorValue::None : + channel == BLE_WIRE_INVITE ? GattClientCharacteristicConfigurationDescriptorValue::Indicate : + GattClientCharacteristicConfigurationDescriptorValue::Notify; step("GattCharacteristic.WriteCCCDWithResultAsync", id); - DEBUG_DEBUG(CAT, "BLE WinRT subscribe request link=%llu channel=%d mode=%d", (unsigned long long)id, channel, (int)mode); + DEBUG_DEBUG(CAT, "BLE WinRT CCC request link=%llu reset=%d channel=%d mode=%d", (unsigned long long)id, reset, channel, (int)mode); await("subscribe channel", characteristic.WriteClientCharacteristicConfigurationDescriptorWithResultAsync(mode), id, - [id, channel](WinState& s, GattWriteResult const& result) { + [id, channel, reset](WinState& s, GattWriteResult const& result) { auto status = result.Status(); auto protocol = result.ProtocolError(); - DEBUG_DEBUG(CAT, "BLE WinRT subscribe link=%llu channel=%d status=%d", (unsigned long long)id, channel, (int)status); + DEBUG_DEBUG(CAT, "BLE WinRT CCC complete link=%llu reset=%d channel=%d status=%d", + (unsigned long long)id, reset, channel, (int)status); if (status != GattCommunicationStatus::Success) { - DEBUG_WARN(CAT, "BLE WinRT subscription failed link=%llu channel=%d status=%d ATT_error=%d", - (unsigned long long)id, channel, (int)status, protocol ? int(protocol.Value()) : -1); + DEBUG_WARN(CAT, "BLE WinRT CCC failed link=%llu reset=%d channel=%d status=%d ATT_error=%d", + (unsigned long long)id, reset, channel, (int)status, protocol ? int(protocol.Value()) : -1); s.error(id, "GATT subscription failed"); return; } - if (channel == BLE_WIRE_DATA) { s.subscribe_channel(id, BLE_WIRE_INVITE); return; } - if (channel == BLE_WIRE_INVITE) { s.subscribe_channel(id, BLE_WIRE_ACK); return; } + if (channel == BLE_WIRE_DATA) { s.subscribe_channel(id, BLE_WIRE_INVITE, reset); return; } + if (channel == BLE_WIRE_INVITE) { s.subscribe_channel(id, BLE_WIRE_ACK, reset); return; } + if (reset) { s.subscribe_channel(id, BLE_WIRE_DATA, false); return; } + DEBUG_INFO(CAT, "BLE WinRT subscriptions ready link=%llu DATA=notify INVITE=indicate ACK=notify", (unsigned long long)id); auto* l = s.link(id); l->opened = true; ble_backend_opened(s.owner, id, l->session.MaxPduSize()); }); } diff --git a/lib/ble/tests/test_ble_bluez.c b/lib/ble/tests/test_ble_bluez.c index 91537e15d..6f03d7b8c 100644 --- a/lib/ble/tests/test_ble_bluez.c +++ b/lib/ble/tests/test_ble_bluez.c @@ -19,7 +19,7 @@ static struct UASYNC* ua; static sd_bus* bus; static struct ble_transport* transport; static char application[256]; -static int ready, broadcasts, ready_links, advertisement_count; +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}; @@ -127,6 +127,7 @@ static void status(void* arg, enum ble_state state, const char* reason) { static void connection(void* arg, ble_link_id id, uint64_t node, enum ble_state state, const char* reason) { (void)arg; (void)reason; if (state == BLE_READY) { CHECK(node < 4); links[node] = id; ready_links++; } + if (state == BLE_CLOSED) closed_links++; } static void broadcast(void* arg, const struct ble_endpoint* ep, const uint8_t* data, size_t len, int rssi) { (void)arg; CHECK(strstr(ep->id, "dev_A")); CHECK(len == 3 && !memcmp(data, "abc", 3)); CHECK(rssi == -42); broadcasts++; @@ -241,6 +242,28 @@ int main(void) { CHECK(send(invite2, "\1", 1, MSG_NOSIGNAL) == 1); poll_all(); } CHECK(ack_values >= 2); + /* Повторный AcquireNotify до HUP старого fd: новая сессия A, действующая сессия B не затронута. */ + ble_link_id old_a = links[1], old_b = links[2]; + int closed_before = closed_links; + int new_fd1 = acquire(ADAPTER "/dev_A", 0); + CHECK(closed_links == closed_before + 1 && ready_links == 2); + CHECK(recv(invite1, data, sizeof(data), MSG_DONTWAIT) == 0); + close(ack_fds[0]); ack_fds[0] = -1; + close(invite1); close(fd1); fd1 = new_fd1; + invite1 = acquire(ADAPTER "/dev_A", 1); + CHECK(ready_links == 2); + ack_fds[0] = acquire(ADAPTER "/dev_A", 2); + hello(ADAPTER "/dev_A", fd1, 1); + CHECK(ready_links == 3 && links[1] != old_a && links[2] == old_b); + CHECK(!ble_tx_put(transport, old_b, frame(0xf1))); poll_all(); + for (int offset = 0; offset < 31; offset += 11) { + CHECK(recv(fd2, data, sizeof(data), MSG_DONTWAIT) == (offset == 22 ? 18 : 20)); + CHECK(data[8] == offset && data[9] == 0xf1); + } + write_value(ADAPTER "/dev_B", data, 2, 2); + write_value(ADAPTER "/dev_A", incoming, sizeof(incoming), 0); + e = ble_rx_get(transport, links[1]); CHECK(e && e->len == 3 && !memcmp(e->dgram, incoming + 9, 3)); + queue_dgram_free(e); queue_entry_free(e); close(ack_fds[0]); close(ack_fds[1]); ack_fds[0] = ack_fds[1] = -1; close(invite1); close(invite2); close(fd1); poll_all(); close(fd2); poll_all(); @@ -248,10 +271,10 @@ int main(void) { struct ble_endpoint ep = {REMOTE}; CHECK(ble_connect(transport, &ep, 3)); poll_all(); /* Кеш UUID доступен сразу, но до ServicesResolved нельзя объявлять GATT готовым с прежним MTU23. */ - CHECK(ready_links == 2 && commands == 0); + CHECK(ready_links == 3 && commands == 0); remote_resolved = 1; CHECK(sd_bus_emit_properties_changed(bus, REMOTE, "org.bluez.Device1", "ServicesResolved", NULL) >= 0); - poll_all(); CHECK(ready_links == 3 && commands == 1); + 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. */