From 325923751c9bfcb6112947773deac095fa15fd7a Mon Sep 17 00:00:00 2001 From: portersky Date: Sat, 29 Aug 2026 12:47:14 +0200 Subject: [PATCH] fix: complete wireless pairing frames Match the upstream 802.11 management layouts for association responses, pair responses, and reserved client commands by including the sequence control and association capability fields. Reuse existing WCIDs for repeated association requests and end pairing mode after a successful pair response. Also restore the upstream radio calibration sequence needed for reliable WLAN associations. Co-Authored-By: openai/gpt-5.6-luna: fixed pairing frame layouts and lifecycle --- CMakeLists.txt | 2 +- src/app/api.cpp | 39 ++++++++++++++++++++--------- src/mt76/mt76.cpp | 62 +++++++++++++++++++++++++++++------------------ 3 files changed, 68 insertions(+), 35 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bf86e84..4fc4497 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ if(NOT CMAKE_GENERATOR MATCHES "^(Ninja|Xcode)$") endif() cmake_minimum_required(VERSION 3.21) -project(xone_macos VERSION 0.1.12 LANGUAGES CXX Swift) +project(xone_macos VERSION 0.1.13 LANGUAGES CXX Swift) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) diff --git a/src/app/api.cpp b/src/app/api.cpp index 89895b1..eb55c17 100644 --- a/src/app/api.cpp +++ b/src/app/api.cpp @@ -203,23 +203,34 @@ auto xone_dongle::set_pairing(bool enable) -> void auto xone_dongle::handle_association(std::span addr) -> void { - // Find a free WCID slot (1..16). + std::array mac{}; + std::memcpy(mac.data(), addr.data(), mac.size()); + + // Find a free WCID slot (1..16), or reuse the WCID for a retransmitted + // request from a controller already tracked by the session. std::uint8_t wcid = 0; + bool existing = false; { std::lock_guard guard(lock); - for (std::uint8_t i = 1; i <= 16; ++i) { - bool used = false; - for (auto const& c : controllers) - if (c.wcid == i) { used = true; break; } - if (!used) { wcid = i; break; } + for (auto const& c : controllers) { + if (c.mac == mac) { + wcid = c.wcid; + existing = true; + break; + } + } + if (!existing) { + for (std::uint8_t i = 1; i <= 16; ++i) { + bool used = false; + for (auto const& c : controllers) + if (c.wcid == i) { used = true; break; } + if (!used) { wcid = i; break; } + } } } if (wcid == 0) return; // no free slot - std::array mac{}; - std::memcpy(mac.data(), addr.data(), mac.size()); - // Program the chip outside the lock (synchronous USB I/O). if (chip->associate_client(wcid, mac) != 0) { xone::log_msg(xone::log_level::warn, @@ -227,7 +238,7 @@ auto xone_dongle::handle_association(std::span addr) -> void return; } - { + if (!existing) { std::lock_guard guard(lock); controllers.push_back(controller{wcid, mac}); } @@ -274,7 +285,13 @@ auto xone_dongle::handle_client_command(std::span payload, case client_cmd::client_pair_req: { std::array mac{}; std::memcpy(mac.data(), addr.data(), mac.size()); - chip->pair_client(mac); + if (auto err = chip->pair_client(mac); err != 0) { + xone::log_msg(xone::log_level::warn, + "api: pair response failed (%d)", err); + break; + } + // Upstream ends the 30-second pairing window after PAIR_RESP. + set_pairing(false); xone::log_msg(xone::log_level::info, "api: controller paired (wcid=%d)", wcid); break; diff --git a/src/mt76/mt76.cpp b/src/mt76/mt76.cpp index 0165199..02e9dec 100644 --- a/src/mt76/mt76.cpp +++ b/src/mt76/mt76.cpp @@ -496,12 +496,25 @@ auto chip::set_idle_time() -> int auto chip::calibrate_radio() -> int { - // Enable AGC for all antennas. - write_register(mt_bbp_agc(0), 0x0000001f); - write_register(mt_bbp_agc(1), 0x0000001f); - write_register(mt_bbp_agc(2), 0x0000001f); + // Configure automatic gain control (port of upstream AGC setup). + write_register(mt_bbp_agc(8), 0x18365efa); + write_register(mt_bbp_agc(9), 0x18365efa); - return calibrate(calibration::cal_rc, 0); + // Required reset for reliable WLAN associations. + write_register(mt_mac_sys_ctrl, 0); + write_register(mt_rf_bypass_0, 0); + write_register(mt_rf_setting_0, 0); + + if (auto err = calibrate(calibration::cal_temp_sensor, 0); err != 0) + return err; + if (auto err = calibrate(calibration::cal_rxdcoc, 1); err != 0) + return err; + if (auto err = calibrate(calibration::cal_rc, 0); err != 0) + return err; + + write_register(mt_mac_sys_ctrl, + mt_mac_sys_ctrl_enable_rx | mt_mac_sys_ctrl_enable_tx); + return 0; } auto chip::get_channel_power(channel *chan) -> int @@ -800,16 +813,18 @@ auto chip::associate_client(std::uint8_t wcid, { auto address = mac_address(); - // struct ieee80211_mgmt (assoc_resp), 26 bytes: - // [frame_control][duration][da][sa][bssid][status_code][aid]. - std::uint8_t mgmt[26] = {}; + // struct ieee80211_mgmt (assoc_resp), 30 bytes: + // [header][capability_info][status_code][aid]. + std::uint8_t mgmt[30] = {}; xone::store_le16(mgmt + 0, ieee80211_ftype_mgmt | ieee80211_stype_assoc_resp); std::memcpy(mgmt + 4, addr.data(), 6); // da std::memcpy(mgmt + 10, address.data(), 6); // sa std::memcpy(mgmt + 16, address.data(), 6); // bssid - xone::store_le16(mgmt + 22, 0x0110); // status_code (original) - xone::store_le16(mgmt + 24, 0x0f00); // aid (original) + // Capability info stays zero, matching the upstream zero-initialized + // struct. The original status code and association ID follow it. + xone::store_le16(mgmt + 26, 0x0110); // status_code + xone::store_le16(mgmt + 28, 0x0f00); // aid // Payload: mgmt frame plus 8 bytes of zero padding. std::vector payload(sizeof(mgmt) + 8, 0); @@ -832,20 +847,20 @@ auto chip::pair_client(std::span addr) -> int { auto address = mac_address(); - // struct ieee80211_hdr_3addr (22 bytes) plus the reserved command and a - // 9-byte payload: [fc][duration][addr1][addr2][addr3][0x70][PAIR_RESP] - // [data]. - std::uint8_t frame[33] = {}; + // struct ieee80211_hdr_3addr (24 bytes) plus the reserved command and a + // 9-byte payload: [header][0x70][PAIR_RESP][data]. + std::uint8_t frame[35] = {}; xone::store_le16(frame + 0, ieee80211_ftype_mgmt | ieee80211_stype_wlan_reserved); std::memcpy(frame + 4, addr.data(), 6); // addr1 std::memcpy(frame + 10, address.data(), 6); // addr2 std::memcpy(frame + 16, address.data(), 6); // addr3 - frame[22] = ieee80211_stype_wlan_reserved; // reserved (0x70) - frame[23] = client_cmd::client_pair_resp; + // seq_ctrl stays zero, matching the upstream zero-initialized header. + frame[24] = ieee80211_stype_wlan_reserved; // reserved (0x70) + frame[25] = client_cmd::client_pair_resp; std::uint8_t data[9] = { 0x00, 0x45, 0x55, 0x01, 0x0f, 0x8f, 0xff, 0x87, 0x1f }; - std::memcpy(frame + 24, data, sizeof(data)); + std::memcpy(frame + 26, data, sizeof(data)); return send_wlan({ frame, frame + sizeof(frame) }); } @@ -857,9 +872,9 @@ auto chip::send_client_command(std::uint8_t wcid, { auto address = mac_address(); - // Payload: [info(8)][txwi(20)][hdr(22)][0x70][cmd][data]. - auto txwi = build_txwi(wcid - 1, 22 + 2 + data.size()); - std::vector payload(8 + txwi.size() + 22 + 2 + data.size()); + // Payload: [info(8)][txwi(20)][hdr(24)][0x70][cmd][data]. + auto txwi = build_txwi(wcid - 1, 24 + 2 + data.size()); + std::vector payload(8 + txwi.size() + 24 + 2 + data.size()); // info: {0, 0, 0, wcid-1, 0, 0, 0, 0}. payload[3] = wcid - 1; @@ -871,10 +886,11 @@ auto chip::send_client_command(std::uint8_t wcid, std::memcpy(hdr + 4, addr.data(), 6); // addr1 std::memcpy(hdr + 10, address.data(), 6); // addr2 std::memcpy(hdr + 16, address.data(), 6); // addr3 - hdr[22] = ieee80211_stype_wlan_reserved; // reserved (0x70) - hdr[23] = static_cast(cmd); + // seq_ctrl stays zero, matching the upstream zero-initialized header. + hdr[24] = ieee80211_stype_wlan_reserved; // reserved (0x70) + hdr[25] = static_cast(cmd); if (!data.empty()) - std::memcpy(hdr + 24, data.data(), data.size()); + std::memcpy(hdr + 26, data.data(), data.size()); return send_command(0, payload.data(), payload.size()); }