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()); }