Use a single connectable node service advertisement on Windows

This commit is contained in:
evgeny committed 2026-10-31 00:33:44 +01:00
1 parent 0b66ec27d2
commit 8a5a423da9
1 file changed
+72 -170
+72 -170
View File
@@ -94,6 +94,7 @@ struct WinState : std::enable_shared_from_this<WinState> {
struct UASYNC* ua = nullptr;
struct ble_transport* owner = nullptr;
uint16_t company = 0;
std::string service_uuid;
std::mutex mutex;
std::deque<Event> events;
const char* operation = "idle"; /* Только uasync; Windows callbacks передают контекст через Event. */
@@ -102,23 +103,18 @@ struct WinState : std::enable_shared_from_this<WinState> {
bool stopped = false, failed = false, overflow = false, apartment = false, scan_ready = false, service_ready = false;
int received_address_type_api = -1; /* -1: ещё не было объявления; 0/1: фактический интерфейс EventArgs2. */
/* Windows callbacks считают все события, включая отсеянные до mailbox. Остальные поля — только uasync. */
std::atomic<uint64_t> scan_events{0}, scan_no_manufacturer{0}, scan_other_company{0}, scan_matching{0};
std::atomic<uint64_t> scan_events{0}, scan_foreign{0}, scan_matching{0};
uint64_t scan_forwarded = 0, scan_oversized = 0;
void* diagnostics_timer = nullptr;
int publisher_status = -1; /* Последний StatusChanged; -1: publisher ещё не создан. */
uint64_t publisher_wait_since = 0;
bool publisher_wait_warned = false;
bool adapter_ready = false, server_created = false, ready_reported = false;
bool service_requested = false, service_stop_pending = false, publication_pending = false;
uint64_t publication_deadline = 0; /* Ограничивает запуск/замену publisher, включая освобождение GATT-рекламы. */
bool service_requested = false, service_stop_pending = false;
uint64_t publication_deadline = 0; /* Ограничивает запуск/смену discoverability единственного GATT advertising. */
IAsyncInfo startup{nullptr};
BluetoothLEAdvertisementWatcher watcher{nullptr};
BluetoothLEAdvertisementPublisher publisher{nullptr};
GattServiceProvider provider{nullptr};
GattLocalCharacteristic local_rx{nullptr}, local_tx{nullptr}, local_invite{nullptr}, local_ack{nullptr};
BluetoothLEAdvertisementWatcher::Received_revoker received;
BluetoothLEAdvertisementWatcher::Stopped_revoker scan_stopped;
BluetoothLEAdvertisementPublisher::StatusChanged_revoker publish_changed;
GattServiceProvider::AdvertisementStatusChanged_revoker service_changed;
GattLocalCharacteristic::WriteRequested_revoker write_requested, invite_write_requested, ack_write_requested;
GattLocalCharacteristic::SubscribedClientsChanged_revoker subscriptions_changed, invite_subscriptions_changed, ack_subscriptions_changed;
@@ -126,18 +122,15 @@ struct WinState : std::enable_shared_from_this<WinState> {
std::array<uint8_t, BLE_BROADCAST_MAX> broadcast{};
size_t broadcast_size = 0;
bool broadcast_enabled = false;
uint64_t publish_generation = 0;
/* Отмечает конкретный вызов до обращения к WinRT, включая синхронные исключения. */
void step(const char* name, ble_link_id id = 0) {
operation = name; operation_link = id;
DEBUG_TRACE(CAT, "BLE WinRT operation=%s link=%llu", name, (unsigned long long)id);
}
/* Читаем ОС, а не сохранённые callbacks. -1: объекта нет, -2: чтение завершилось ошибкой. */
struct RadioStatus { int publisher = -1, service = -1, scan = -1; };
struct RadioStatus { int service = -1, scan = -1; };
RadioStatus radio_status() {
RadioStatus result;
try { if (publisher) result.publisher = int(publisher.Status()); }
catch (...) { result.publisher = -2; DEBUG_WARN(CAT, "%s", exception_reason("diagnostics Publisher.Status").c_str()); }
try { if (provider) result.service = int(provider.AdvertisementStatus()); }
catch (...) { result.service = -2; DEBUG_WARN(CAT, "%s", exception_reason("diagnostics GATT.AdvertisementStatus").c_str()); }
try { if (watcher) result.scan = int(watcher.Status()); }
@@ -145,38 +138,19 @@ struct WinState : std::enable_shared_from_this<WinState> {
return result;
}
void discovery_stats(const char* phase) {
auto wait_ms = publisher_wait_since ? (get_time_us() - publisher_wait_since) / 1000 : 0;
DEBUG_DEBUG(CAT, "BLE WinRT scan totals phase=%s scan_ready=%d service_ready=%d failed=%d company=%04x "
"events=%llu no_manufacturer=%llu other_company=%llu matching_events=%llu forwarded_fields=%llu oversized_fields=%llu "
"publisher_state=%d waiting_ms=%llu",
phase, scan_ready, service_ready, failed, company, (unsigned long long)scan_events.load(),
(unsigned long long)scan_no_manufacturer.load(), (unsigned long long)scan_other_company.load(),
(unsigned long long)scan_matching.load(), (unsigned long long)scan_forwarded,
(unsigned long long)scan_oversized, publisher_status, (unsigned long long)wait_ms);
if (publisher_wait_since) {
auto actual = radio_status();
DEBUG_DEBUG(CAT, "BLE WinRT radio snapshot phase=%s publisher_actual=%d publisher_event=%d service_actual=%d scan_actual=%d "
"generation=%llu payload_bytes=%zu manufacturer_ad_bytes=%zu links=%zu "
"(publisher:1=Waiting,2=Started; service:1=Stopped,2=Started; scan:1=Started; -1=absent,-2=read_error)",
phase, actual.publisher, publisher_status, actual.service, actual.scan,
(unsigned long long)publish_generation, broadcast_size, broadcast_size + 4, links.size());
}
if (wait_ms >= 10000 && !publisher_wait_warned) {
publisher_wait_warned = true;
DEBUG_WARN(CAT, "BLE WinRT publisher still waiting for radio resources elapsed_ms=%llu generation=%llu "
"scan_ready=%d service_ready=%d; manufacturer advertisement has not started",
(unsigned long long)wait_ms, (unsigned long long)publish_generation, scan_ready, service_ready);
DEBUG_WARN(CAT, "BLE WinRT GATT and manufacturer publisher share Windows advertising resources; "
"close VibeChat and run ble_advertising_probe.exe to compare GATT on/off/restored; "
"Waiting is not proof of GATT contention or of transmission");
}
auto actual = radio_status();
DEBUG_DEBUG(CAT, "BLE WinRT scan totals phase=%s ready=%d/%d events=%llu foreign=%llu matching=%llu forwarded=%llu "
"service_actual=%d scan_actual=%d discoverable=%d UUID=%s links=%zu",
phase, scan_ready, service_ready, (unsigned long long)scan_events.load(), (unsigned long long)scan_foreign.load(),
(unsigned long long)scan_matching.load(), (unsigned long long)scan_forwarded, actual.service, actual.scan,
broadcast_enabled, service_uuid.c_str(), links.size());
}
static void diagnostics_tick(void* arg) {
auto& s = *static_cast<WinState*>(arg); s.diagnostics_timer = nullptr;
s.discovery_stats("periodic");
s.publication_timeout();
if (s.stopped || s.failed) return;
uint64_t delay = 100000; /* Сводка раз в 10s, но deadline publisher обслуживается без лишних 10s ожидания. TB=0.1ms. */
uint64_t delay = 100000; /* Сводка раз в10s; deadline advertising обслуживается без лишнего ожидания. TB=0.1ms. */
if (s.publication_deadline) {
auto now = get_time_us();
delay = std::min(delay, s.publication_deadline > now ? (s.publication_deadline - now + 99) / 100 : uint64_t(1));
@@ -220,8 +194,8 @@ struct WinState : std::enable_shared_from_this<WinState> {
WinLink* link(ble_link_id id) { auto it = links.find(id); return it == links.end() ? nullptr : it->second.get(); }
void fail(const char* reason) {
if (failed) return;
DEBUG_ERROR(CAT, "BLE WinRT radio failure scan_ready=%d service_ready=%d links=%zu publisher_generation=%llu",
scan_ready, service_ready, links.size(), (unsigned long long)publish_generation);
DEBUG_ERROR(CAT, "BLE WinRT radio failure scan_ready=%d service_ready=%d links=%zu UUID=%s",
scan_ready, service_ready, links.size(), service_uuid.c_str());
failed = true; DEBUG_ERROR(CAT, "BLE WinRT failed: %s", reason); ble_backend_status(owner, 0, reason);
}
void error(ble_link_id id, const char* reason) {
@@ -296,40 +270,39 @@ struct WinState : std::enable_shared_from_this<WinState> {
void publication_timeout() {
if (!publication_deadline || get_time_us() < publication_deadline || failed || stopped) return;
publication_deadline = 0;
DEBUG_ERROR(CAT, "BLE WinRT publication timeout publisher=%d service_requested=%d stop_pending=%d generation=%llu",
publisher_status, service_requested, service_stop_pending, (unsigned long long)publish_generation);
ble_backend_advertising(owner, BLE_CLOSED, "Windows BLE publisher did not start within 20 seconds");
fail("Windows BLE publication timed out; GATT startup/update incomplete");
DEBUG_ERROR(CAT, "BLE WinRT GATT advertisement timeout requested=%d stop_pending=%d UUID=%s",
service_requested, service_stop_pending, service_uuid.c_str());
ble_backend_advertising(owner, BLE_CLOSED, "GATT advertisement timed out"); fail("GATT advertisement timed out");
}
void start_service_advertising() {
GattServiceProviderAdvertisingParameters parameters; parameters.IsConnectable(true); parameters.IsDiscoverable(true);
DEBUG_INFO(CAT, "BLE WinRT start GATT advertisement service=%s publisher=%d generation=%llu connectable=1 discoverable=1",
BLE_SERVICE_UUID, publisher_status, (unsigned long long)publish_generation);
service_requested = true;
GattServiceProviderAdvertisingParameters parameters;
parameters.IsConnectable(true); parameters.IsDiscoverable(broadcast_enabled);
DEBUG_INFO(CAT, "BLE WinRT start single GATT advertisement UUID=%s connectable=1 discoverable=%d",
service_uuid.c_str(), broadcast_enabled);
service_requested = true; publication_deadline = get_time_us() + 20000000;
step("GattServiceProvider.StartAdvertising"); provider.StartAdvertising(parameters);
}
/* Первичная регистрация — только после Started publisher либо при выключенном объявлении. */
/* Один GATT provider обслуживает и обнаружение, и подключения; отдельного publisher нет. */
void ensure_service() {
if (!adapter_ready || failed || stopped || service_stop_pending) return;
if (broadcast_enabled && (publication_pending || publisher_status != int(BluetoothLEAdvertisementPublisherStatus::Started))) return;
if (!server_created) {
server_created = true;
DEBUG_INFO(CAT, "BLE WinRT create GATT after publication enabled=%d publisher=%d", broadcast_enabled, publisher_status);
make_server();
} else if (provider && !service_requested) start_service_advertising();
if (!server_created) { server_created = true; make_server(); }
else if (provider && !service_requested) start_service_advertising();
}
void service_status(GattServiceProviderAdvertisementStatus status, BluetoothError error) {
DEBUG_DEBUG(CAT, "BLE WinRT service advertising state=%d error=%d (%s) requested=%d stop_pending=%d",
int(status), int(error), bluetooth_error(error), service_requested, service_stop_pending);
DEBUG_DEBUG(CAT, "BLE WinRT GATT advertising state=%d error=%d (%s) requested=%d stop_pending=%d discoverable=%d",
int(status), int(error), bluetooth_error(error), service_requested, service_stop_pending, broadcast_enabled);
if (error != BluetoothError::Success) { radio_error("GATT advertising failed", error); return; }
if (service_stop_pending && !service_requested && (status == GattServiceProviderAdvertisementStatus::Stopped ||
status == GattServiceProviderAdvertisementStatus::Aborted)) {
service_stop_pending = false; service_ready = false;
DEBUG_INFO(CAT, "BLE WinRT requested GATT stop confirmed state=%d; updating manufacturer advertisement", int(status));
publish();
} else if (status == GattServiceProviderAdvertisementStatus::Started) { service_ready = true; ready(); }
else if (status == GattServiceProviderAdvertisementStatus::Aborted) radio_error("GATT advertising aborted", error);
else DEBUG_INFO(CAT, "BLE WinRT service advertising state=%d", int(status));
if (service_stop_pending) {
if (status == GattServiceProviderAdvertisementStatus::Stopped || status == GattServiceProviderAdvertisementStatus::Aborted) {
service_stop_pending = false; service_ready = false; ensure_service();
}
return;
}
if (status == GattServiceProviderAdvertisementStatus::Started) {
publication_deadline = 0; service_ready = true;
ble_backend_advertising(owner, broadcast_enabled ? BLE_READY : BLE_CLOSED, "GATT discovery publication applied"); ready();
} else if (status == GattServiceProviderAdvertisementStatus::Aborted) radio_error("GATT advertising aborted", error);
else if (service_requested) fail("GATT advertisement stopped unexpectedly");
}
/* Только доступные Win11 API: фактические параметры отделены от запрошенного профиля. */
void log_connection(ble_link_id id) {
@@ -473,7 +446,7 @@ struct WinState : std::enable_shared_from_this<WinState> {
for (auto id : gone) error(id, "notification subscription removed");
}
void make_server() {
await("create GATT service", GattServiceProvider::CreateAsync(guid(BLE_SERVICE_UUID)), 0, [](WinState& s, GattServiceProviderResult const& result) {
await("create GATT service", GattServiceProvider::CreateAsync(guid(service_uuid.c_str())), 0, [](WinState& s, GattServiceProviderResult const& result) {
if (result.Error() != BluetoothError::Success) { s.radio_error("create GATT service", result.Error()); return; }
s.provider = result.ServiceProvider();
GattLocalCharacteristicParameters parameters;
@@ -602,39 +575,27 @@ struct WinState : std::enable_shared_from_this<WinState> {
auto state = weak.lock(); if (!state) return;
++state->scan_events;
try {
auto fields = args.Advertisement().ManufacturerData();
if (!fields.Size()) { ++state->scan_no_manufacturer; return; }
bool matching = false;
for (auto const& value : fields) if (value.CompanyId() == state->company) matching = true;
if (!matching) { ++state->scan_other_company; return; }
auto type = args.AdvertisementType();
if (type != BluetoothLEAdvertisementType::ScannableUndirected && type != BluetoothLEAdvertisementType::NonConnectableUndirected)
for (auto const& uuid : args.Advertisement().ServiceUuids()) {
uint64_t peer; if (!ble_service_uuid_decode(to_string(uuid).c_str(), &peer)) { matching = true; break; }
}
if (!matching) { ++state->scan_foreign; return; }
++state->scan_matching;
} catch (...) {
auto reason = exception_reason("Received manufacturer filter");
state->post("Watcher.Received filter failure", 0, [reason](WinState& s) { s.fail(reason.c_str()); });
return;
auto reason = exception_reason("Received service UUID filter");
state->post("Watcher.Received filter failure", 0, [reason](WinState& s) { s.fail(reason.c_str()); }); return;
}
state->post("Watcher.Received", 0, [args](WinState& s) {
s.step("Received.Advertisement.ManufacturerData");
for (auto const& value : args.Advertisement().ManufacturerData()) {
if (value.CompanyId() != s.company) continue;
auto ep = s.advertisement_endpoint(args);
s.step("Received.RawSignalStrengthInDBm"); auto rssi = args.RawSignalStrengthInDBm();
s.step("Received manufacturer length"); auto length = value.Data().Length();
if (length > BLE_BROADCAST_MAX) {
++s.scan_oversized;
if (s.scan_oversized <= 3)
DEBUG_WARN(CAT, "BLE WinRT broadcast rejected endpoint=%s rssi=%d company=%04x bytes=%u max=%u reason=oversized",
ep.id, rssi, s.company, length, BLE_BROADCAST_MAX);
else DEBUG_TRACE(CAT, "BLE WinRT broadcast rejected endpoint=%s rssi=%d bytes=%u reason=oversized", ep.id, rssi, length);
continue;
}
s.step("Received manufacturer buffer/DataReader");
auto data = bytes(value.Data());
DEBUG_TRACE(CAT, "BLE WinRT broadcast endpoint=%s company=%04x bytes=%zu rssi=%d", ep.id, s.company,
data.size(), rssi);
s.step("ble_backend_broadcast");
auto advertisement = args.Advertisement();
for (auto const& uuid : advertisement.ServiceUuids()) {
uint64_t peer = 0; if (ble_service_uuid_decode(to_string(uuid).c_str(), &peer)) continue;
auto ep = s.advertisement_endpoint(args); auto name = to_string(advertisement.LocalName());
uint8_t data[BLE_BROADCAST_MAX]; int len = ble_identity_encode(data, peer, name.c_str());
if (len < 0) len = ble_identity_encode(data, peer, "");
++s.scan_forwarded;
ble_backend_broadcast(s.owner, &ep, data.data(), data.size(), rssi);
ble_backend_broadcast(s.owner, &ep, data, size_t(len), args.RawSignalStrengthInDBm());
}
});
});
@@ -649,75 +610,13 @@ struct WinState : std::enable_shared_from_this<WinState> {
scan_ready = true; ready();
}
void publish() {
if (failed || stopped) return;
step("Publisher.Status");
DEBUG_DEBUG(CAT, "BLE WinRT publish requested enabled=%d bytes=%zu generation=%llu", broadcast_enabled,
broadcast_size, (unsigned long long)publish_generation);
if (!publication_deadline) publication_deadline = get_time_us() + 20000000;
if (service_stop_pending) return;
if (provider && service_requested && broadcast_enabled) {
if (stopped || failed || service_stop_pending) return;
publication_deadline = get_time_us() + 20000000;
ble_backend_advertising(owner, BLE_STARTING, "updating GATT discoverability");
if (provider && service_requested) {
service_requested = false; service_stop_pending = true;
DEBUG_INFO(CAT, "BLE WinRT pause GATT advertisement before publisher update links=%zu generation=%llu",
links.size(), (unsigned long long)publish_generation);
step("GattServiceProvider.StopAdvertising for publisher"); provider.StopAdvertising(); return;
}
if (publisher) {
auto status = publisher.Status();
if (status == BluetoothLEAdvertisementPublisherStatus::Started || status == BluetoothLEAdvertisementPublisherStatus::Waiting) {
publisher.Stop(); return;
}
if (status == BluetoothLEAdvertisementPublisherStatus::Stopping) return;
publish_changed.revoke(); publisher = nullptr;
publisher_status = -1; publisher_wait_since = 0; publisher_wait_warned = false;
}
if (!broadcast_enabled) {
publication_pending = false; publication_deadline = 0; ensure_service(); return;
}
step("Publisher construction/manufacturer data");
BluetoothLEAdvertisement advertisement;
BluetoothLEManufacturerData data; data.CompanyId(company); data.Data(buffer(broadcast.data(), broadcast_size));
advertisement.ManufacturerData().Append(data); publisher = BluetoothLEAdvertisementPublisher(advertisement);
publication_pending = false;
publisher_status = int(BluetoothLEAdvertisementPublisherStatus::Created);
auto weak = weak_from_this(); auto generation = ++publish_generation;
step("Publisher.StatusChanged registration (auto_revoke)");
publish_changed = publisher.StatusChanged(auto_revoke, [weak, generation](auto const&, auto const& args) {
if (auto s = weak.lock()) s->post("Publisher.StatusChanged", 0, [args, generation](WinState& s) {
if (generation != s.publish_generation) {
DEBUG_DEBUG(CAT, "BLE WinRT stale publisher event generation=%llu current=%llu",
(unsigned long long)generation, (unsigned long long)s.publish_generation);
return;
}
auto status = args.Status(); auto error = args.Error();
s.publisher_status = int(status);
if (status == BluetoothLEAdvertisementPublisherStatus::Waiting) {
if (!s.publisher_wait_since) s.publisher_wait_since = get_time_us();
} else {
if (s.publisher_wait_since)
DEBUG_INFO(CAT, "BLE WinRT publisher left Waiting elapsed_ms=%llu state=%d generation=%llu",
(unsigned long long)((get_time_us() - s.publisher_wait_since) / 1000), (int)status,
(unsigned long long)generation);
s.publisher_wait_since = 0; s.publisher_wait_warned = false;
}
DEBUG_DEBUG(CAT, "BLE WinRT broadcast state=%d error=%d (%s) generation=%llu", (int)status, (int)error,
bluetooth_error(error), (unsigned long long)generation);
if (status == BluetoothLEAdvertisementPublisherStatus::Aborted) s.radio_error("broadcast publication aborted", error);
else if (status == BluetoothLEAdvertisementPublisherStatus::Stopped) {
ble_backend_advertising(s.owner, BLE_CLOSED, "publisher stopped"); s.publish();
} else if (status == BluetoothLEAdvertisementPublisherStatus::Started) {
ble_backend_advertising(s.owner, BLE_READY, "publisher started");
if (!s.publication_pending && s.publisher.Status() == BluetoothLEAdvertisementPublisherStatus::Started) {
s.publication_deadline = 0; s.ensure_service();
}
}
else if (status == BluetoothLEAdvertisementPublisherStatus::Waiting)
ble_backend_advertising(s.owner, BLE_STARTING, "publisher waiting for radio resources");
else if (status == BluetoothLEAdvertisementPublisherStatus::Stopping)
ble_backend_advertising(s.owner, BLE_STARTING, "publisher stopping");
else ble_backend_advertising(s.owner, BLE_STARTING, "publisher created");
});
});
step("Publisher.Start"); publisher.Start();
step("GattServiceProvider.StopAdvertising for discovery update"); provider.StopAdvertising();
} else ensure_service();
}
void connect(ble_link_id id, uint64_t address, BluetoothAddressType type) {
bool cached = type == BluetoothAddressType::Unspecified;
@@ -738,7 +637,9 @@ struct WinState : std::enable_shared_from_this<WinState> {
l->device = device; l->device_id = device.BluetoothDeviceId().Id();
DEBUG_DEBUG(CAT, "BLE WinRT device link=%llu endpoint=%s status=%d", (unsigned long long)id,
to_string(l->device_id).c_str(), (int)device.ConnectionStatus());
s.await("discover GATT service", device.GetGattServicesForUuidAsync(guid(BLE_SERVICE_UUID), BluetoothCacheMode::Uncached), id,
char peer_uuid[37];
if (ble_service_uuid_encode(peer_uuid, ble_backend_peer(s.owner, id))) { s.error(id, "missing peer service identity"); return; }
s.await("discover GATT service", device.GetGattServicesForUuidAsync(guid(peer_uuid), BluetoothCacheMode::Uncached), id,
[id](WinState& s, GattDeviceServicesResult const& result) {
auto protocol = result.ProtocolError();
DEBUG_DEBUG(CAT, "BLE WinRT services link=%llu status=%d ATT_error=%d count=%u", (unsigned long long)id,
@@ -865,17 +766,16 @@ struct WinState : std::enable_shared_from_this<WinState> {
if (diagnostics_timer) { uasync_cancel_timeout(ua, diagnostics_timer); diagnostics_timer = nullptr; }
discarded.clear();
if (task) uasync_cancel_post(ua, task);
received.revoke(); scan_stopped.revoke(); publish_changed.revoke(); service_changed.revoke();
received.revoke(); scan_stopped.revoke(); service_changed.revoke();
discovery_stats("shutdown");
write_requested.revoke(); subscriptions_changed.revoke(); invite_write_requested.revoke(); invite_subscriptions_changed.revoke();
ack_write_requested.revoke(); ack_subscriptions_changed.revoke();
while (!links.empty()) close(links.begin()->first);
try { if (startup) startup.Cancel(); } catch (...) { DEBUG_WARN(CAT, "%s", exception_reason("cancel startup").c_str()); }
try { if (watcher) watcher.Stop(); } catch (...) { DEBUG_WARN(CAT, "%s", exception_reason("stop scan").c_str()); }
try { if (publisher) publisher.Stop(); } catch (...) { DEBUG_WARN(CAT, "%s", exception_reason("stop publisher").c_str()); }
try { if (provider && service_requested) provider.StopAdvertising(); }
catch (...) { DEBUG_WARN(CAT, "%s", exception_reason("stop server").c_str()); }
watcher = nullptr; publisher = nullptr; provider = nullptr;
watcher = nullptr; provider = nullptr;
local_rx = nullptr; local_tx = nullptr; local_invite = nullptr; local_ack = nullptr; startup = nullptr;
if (apartment) { uninit_apartment(); apartment = false; }
}
@@ -885,7 +785,8 @@ extern "C" struct ble_backend* ble_backend_create(struct ble_transport* owner, s
if (cfg->adapter) { DEBUG_ERROR(CAT, "BLE WinRT explicit adapter selection unsupported"); return nullptr; }
auto state = std::make_shared<WinState>(); state->owner = owner; state->ua = ua; state->company = cfg->company_id;
state->broadcast_size = cfg->initial_advertisement_size;
state->broadcast_enabled = state->publication_pending = state->broadcast_size != 0;
state->broadcast_enabled = state->broadcast_size != 0;
char uuid[37]; if (ble_service_uuid_encode(uuid, cfg->node_id)) return nullptr; state->service_uuid = uuid;
if (state->broadcast_size > state->broadcast.size()) { DEBUG_ERROR(CAT, "BLE WinRT initial advertisement too large"); return nullptr; }
if (state->broadcast_size) std::memcpy(state->broadcast.data(), cfg->initial_advertisement, state->broadcast_size);
try {
@@ -921,7 +822,7 @@ extern "C" struct ble_backend* ble_backend_create(struct ble_transport* owner, s
s.fail("adapter lacks BLE central/peripheral support"); return;
}
s.adapter_ready = true; WinState::diagnostics_tick(&s);
if (s.broadcast_enabled) s.publish(); else s.ensure_service();
s.ensure_service();
});
void* memory = u_malloc(sizeof(ble_backend));
if (!memory) { DEBUG_ERROR(CAT, "BLE WinRT allocation failed"); state->shutdown(); return nullptr; }
@@ -938,12 +839,13 @@ extern "C" int ble_backend_advertise(struct ble_backend* b, const uint8_t* data,
if (s.failed || s.stopped) { DEBUG_WARN(CAT, "BLE WinRT advertisement requested on closed backend"); return BLE_NOT_READY; }
if (len > s.broadcast.size() || (len && !data)) { DEBUG_ERROR(CAT, "BLE WinRT invalid advertisement bytes=%zu", len); return BLE_ERROR; }
if (s.broadcast_enabled == (data != nullptr) && s.broadcast_size == len && (!len || !std::memcmp(s.broadcast.data(), data, len))) {
DEBUG_DEBUG(CAT, "BLE WinRT advertisement unchanged bytes=%zu generation=%llu", len, (unsigned long long)s.publish_generation);
DEBUG_DEBUG(CAT, "BLE WinRT discovery metadata unchanged bytes=%zu", len);
return BLE_OK;
}
s.broadcast_enabled = data != nullptr; s.broadcast_size = len; s.publication_pending = true;
bool changed = s.broadcast_enabled != (data != nullptr);
s.broadcast_enabled = data != nullptr; s.broadcast_size = len;
if (len) std::memcpy(s.broadcast.data(), data, len);
s.publish(); return BLE_OK;
if (changed) s.publish(); return BLE_OK;
} catch (...) { DEBUG_ERROR(CAT, "%s", exception_reason(b->state->operation).c_str()); return BLE_ERROR; }
}
extern "C" int ble_backend_connect(struct ble_backend* b, ble_link_id id, const struct ble_endpoint* ep) {