diff --git a/doc/ble_transport.txt b/doc/ble_transport.txt index 1573a23c..a6df46d4 100644 --- a/doc/ble_transport.txt +++ b/doc/ble_transport.txt @@ -169,8 +169,10 @@ Windows выбирает PHY с peer. Отсутствие/отказ API не Перед остановкой uasync снять источники событий, отменить операции и posted_task. WinRT может сохранять физический ACL для других сервисов. Гарантия close относится к нашей сессии и ресурсам; принудительный разрыв общего ACL в API не обещается. -Нужен адаптер с central+peripheral и Windows с GATT server API/BytesSent -(Windows 10 1709+); версия ОС сама по себе поддержку драйвером не гарантирует. +Нужен адаптер с central+peripheral и Windows 10 2004 / build 19041 или новее: +помимо GATT server API/BytesSent требуется BluetoothAddressType принятого объявления. +Наличие свойства проверяется при старте, интерфейса EventArgs2 — на фактическом событии; +отсутствие даёт явную причину отказа. Версия ОС сама по себе поддержку драйвером не гарантирует. Радио Windows необходимо проверить на реальном Windows-ПК; Wine этого не заменяет. Сборка и проверка @@ -188,8 +190,14 @@ test_ble_transport: реальные common transports с управляемым test_ble_bluez: отдельный dbus-run-session с подставным BlueZ; настоящее экспортирование сервиса/объявлений, два AcquireNotify fd, адресные подтверждения, приём кадров и независимое закрытие. К системному Bluetooth тест не обращается. +test_ble_winrt (Windows): настоящий mailbox и hresult_error без радио; исключение +одного link не останавливает другого, ошибка radio прекращает доставку batch, +shutdown отменяет posted_task и поздние callbacks. Тест можно запускать в Wine; +он не проверяет драйвер, доступность Bluetooth API и физическую передачу. ble_probe 3: ручная проверка реального радио (scan, GATT registration, публикация и замена тестового manufacturer-объявления); не подключается к чужим устройствам. +Успех требует READY радио, первоначальной и обновлённой публикации. Одного +scan/GATT ready недостаточно. Для диагностики ожидания ресурсов: ble_probe 15. Логи: discovery=debug. INFO — состояния и режим DATA; DEBUG — подписки и сводка трафика; WARN/ERROR — причины отброса/отказа. Содержимое кадров/объявлений не выводится. @@ -201,16 +209,33 @@ WARN/ERROR — причины отброса/отказа. Содержимое Ранние ошибки подготовки INI всегда пишутся в chatgui.log до чтения настроек лога; их точные причины также показаны в окне ошибки независимо от фильтра. +INFO: семейство/версия Windows (DeviceFamilyVersion), доступность обязательного +BluetoothAddressType, wire=3, каналы DATA/INVITE/ACK. Waiting и Stopping publisher +имеют разные причины состояния; Waiting означает ожидание ресурсов публикации. + DEBUG: адаптер (BLE, central/peripheral, extended advertising, поддержка 2M/Coded), этап/результат каждой служебной async-операции, состояния рекламы и сканирования, адрес и роль соединения, список UUID/свойств характеристик, CCCD, MTU/PDU, сводка трафика, закрытие с pending-состоянием и очистка mailbox. -HRESULT записывается с названием операции и текстом Windows; BluetoothError — +HRESULT записывается с именем события, конкретной отмеченной операцией, link ID +и текстом Windows даже при выключенном DEBUG/TRACE. Например: +event=Watcher.Received event_link=0: ... phase=Received.BluetoothAddressType link=0 HRESULT=0x80004002. +Регистрация обработчиков помечена auto_revoke: E_NOINTERFACE на этом этапе +нужно отличать от ошибки доступа к свойству в уже принятом событии. +Ошибка события с известным link ID закрывает этот link; ошибка общего события +переводит radio в failed. Повторный запуск после общей ошибки: выключить Bluetooth +в настройках приложения, применить, затем включить и применить ещё раз. +BluetoothError записывается числом и именем, GATT status — числом, ATT protocol error — числом либо -1 (нет). GATT status: 0=Success, 1=Unreachable, 2=ProtocolError, 3=AccessDenied. PHY_2M_supported — возможность адаптера, НЕ измеренная скорость соединения; -1 у необязательных возможностей означает отсутствие API в этой версии Windows. Для отдельных кадров, ATT-фрагментов и переходов backpressure использовать discovery=trace. +WinRT TRACE содержит channel (0=DATA, 1=INVITE, 2=ACK), направление, длину и elapsed_us +локальной отправки до её обработки в uasync; это время включает ожидание mailbox. +DEBUG показывает число подписчиков DATA/INVITE/ACK, недостающие подписки конкретного +устройства и ATT-ошибку записи CCCD. Первое входящее значение перечитывает подписки, +если Windows ещё не доставил SubscribedClientsChanged. На DEBUG раз в 5 секунд активного соединения и при его закрытии пишется BLE traffic: TX/RX_Bps полезных байтов BLE-кадров (оба канала вместе, до вычета заголовков ETCP), число кадров/фрагментов/отбросов, среднее/максимальное локальное ожидание GATT, diff --git a/lib/ble/tests/ble_probe.c b/lib/ble/tests/ble_probe.c index 394727f0..a09d3bfd 100644 --- a/lib/ble/tests/ble_probe.c +++ b/lib/ble/tests/ble_probe.c @@ -5,33 +5,45 @@ #include #include #include -static int ready, failed; +static int ready, failed, published, changed, republished; +static enum ble_state advertising = BLE_STARTING; static struct ble_transport* transport; static void status(void* arg, enum ble_state state, const char* reason) { - (void)arg; printf("BLE status=%d reason=%s\n", state, reason); fflush(stdout); + (void)arg; DEBUG_INFO(DEBUG_CATEGORY_DISCOVERY, "BLE probe status=%d reason=%s", state, reason); if (state == BLE_READY) { ready = 1; if (ble_advertise(transport, "vibechat-probe", 14) != BLE_OK) failed = 1; } if (state == BLE_CLOSED && strcmp(reason, "stopped")) failed = 1; } +static void advertising_status(void* arg, enum ble_state state, const char* reason) { + (void)arg; advertising = state; + DEBUG_INFO(DEBUG_CATEGORY_DISCOVERY, "BLE probe advertising=%d updated=%d reason=%s", state, changed, reason); + if (state == BLE_READY) { if (changed) republished = 1; else published = 1; } +} static void broadcast(void* arg, const struct ble_endpoint* ep, const uint8_t* data, size_t len, int rssi) { - (void)arg; (void)data; printf("BLE broadcast endpoint=%s bytes=%zu rssi=%d\n", ep->id, len, rssi); + (void)arg; (void)data; + DEBUG_DEBUG(DEBUG_CATEGORY_DISCOVERY, "BLE probe broadcast endpoint=%s bytes=%zu rssi=%d", ep->id, len, rssi); } int main(int argc, char** argv) { debug_config_init(); debug_set_level(DEBUG_LEVEL_INFO); debug_set_category_level(DEBUG_CATEGORY_DISCOVERY, DEBUG_LEVEL_DEBUG); int seconds = argc > 1 ? atoi(argv[1]) : 5; - if (seconds < 1 || seconds > 60) return 2; + if (seconds < 1 || seconds > 60) { DEBUG_ERROR(DEBUG_CATEGORY_DISCOVERY, "BLE probe seconds must be 1..60"); return 2; } struct UASYNC* ua = uasync_create(); + if (!ua) { DEBUG_ERROR(DEBUG_CATEGORY_DISCOVERY, "BLE probe cannot create uasync"); return 1; } struct ble_config cfg = {0}; cfg.node_id = 0x5643424c45544553ULL; cfg.company_id = 0xffff; cfg.callbacks.status = status; cfg.callbacks.broadcast = broadcast; + cfg.callbacks.advertising = advertising_status; transport = ble_create(ua, &cfg); if (!transport || ble_start(transport) != BLE_OK) failed = 1; uint64_t start = get_time_us(), end = start + (uint64_t)seconds * 1000000; - int changed = 0; while (!failed && get_time_us() < end) { uasync_poll(ua, 100); - if (ready && !changed && get_time_us() >= start + 1000000) { + if (published && !changed && get_time_us() >= start + 1000000) { changed = 1; if (ble_advertise(transport, "vibechat-updated", 16) != BLE_OK) failed = 1; } } + int ok = ready && published && republished && advertising == BLE_READY && !failed; + if (ok) DEBUG_INFO(DEBUG_CATEGORY_DISCOVERY, "BLE probe PASS: radio ready, advertisement published and updated"); + else DEBUG_ERROR(DEBUG_CATEGORY_DISCOVERY, "BLE probe FAIL: ready=%d published=%d updated=%d advertising=%d failed=%d seconds=%d", + ready, published, republished, advertising, failed, seconds); ble_destroy(transport); uasync_destroy(ua, 0); - printf("BLE probe ready=%d failed=%d\n", ready, failed); return failed || !ready; + return !ok; }