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
This commit is contained in:
+1
-1
@@ -6,7 +6,7 @@ if(NOT CMAKE_GENERATOR MATCHES "^(Ninja|Xcode)$")
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.21)
|
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)
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
|
|||||||
+28
-11
@@ -203,23 +203,34 @@ auto xone_dongle::set_pairing(bool enable) -> void
|
|||||||
|
|
||||||
auto xone_dongle::handle_association(std::span<std::uint8_t const> addr) -> void
|
auto xone_dongle::handle_association(std::span<std::uint8_t const> addr) -> void
|
||||||
{
|
{
|
||||||
// Find a free WCID slot (1..16).
|
std::array<std::uint8_t, 6> 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;
|
std::uint8_t wcid = 0;
|
||||||
|
bool existing = false;
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> guard(lock);
|
std::lock_guard<std::mutex> guard(lock);
|
||||||
for (std::uint8_t i = 1; i <= 16; ++i) {
|
for (auto const& c : controllers) {
|
||||||
bool used = false;
|
if (c.mac == mac) {
|
||||||
for (auto const& c : controllers)
|
wcid = c.wcid;
|
||||||
if (c.wcid == i) { used = true; break; }
|
existing = true;
|
||||||
if (!used) { wcid = i; break; }
|
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)
|
if (wcid == 0)
|
||||||
return; // no free slot
|
return; // no free slot
|
||||||
|
|
||||||
std::array<std::uint8_t, 6> mac{};
|
|
||||||
std::memcpy(mac.data(), addr.data(), mac.size());
|
|
||||||
|
|
||||||
// Program the chip outside the lock (synchronous USB I/O).
|
// Program the chip outside the lock (synchronous USB I/O).
|
||||||
if (chip->associate_client(wcid, mac) != 0) {
|
if (chip->associate_client(wcid, mac) != 0) {
|
||||||
xone::log_msg(xone::log_level::warn,
|
xone::log_msg(xone::log_level::warn,
|
||||||
@@ -227,7 +238,7 @@ auto xone_dongle::handle_association(std::span<std::uint8_t const> addr) -> void
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
if (!existing) {
|
||||||
std::lock_guard<std::mutex> guard(lock);
|
std::lock_guard<std::mutex> guard(lock);
|
||||||
controllers.push_back(controller{wcid, mac});
|
controllers.push_back(controller{wcid, mac});
|
||||||
}
|
}
|
||||||
@@ -274,7 +285,13 @@ auto xone_dongle::handle_client_command(std::span<std::uint8_t const> payload,
|
|||||||
case client_cmd::client_pair_req: {
|
case client_cmd::client_pair_req: {
|
||||||
std::array<std::uint8_t, 6> mac{};
|
std::array<std::uint8_t, 6> mac{};
|
||||||
std::memcpy(mac.data(), addr.data(), mac.size());
|
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,
|
xone::log_msg(xone::log_level::info,
|
||||||
"api: controller paired (wcid=%d)", wcid);
|
"api: controller paired (wcid=%d)", wcid);
|
||||||
break;
|
break;
|
||||||
|
|||||||
+39
-23
@@ -496,12 +496,25 @@ auto chip::set_idle_time() -> int
|
|||||||
|
|
||||||
auto chip::calibrate_radio() -> int
|
auto chip::calibrate_radio() -> int
|
||||||
{
|
{
|
||||||
// Enable AGC for all antennas.
|
// Configure automatic gain control (port of upstream AGC setup).
|
||||||
write_register(mt_bbp_agc(0), 0x0000001f);
|
write_register(mt_bbp_agc(8), 0x18365efa);
|
||||||
write_register(mt_bbp_agc(1), 0x0000001f);
|
write_register(mt_bbp_agc(9), 0x18365efa);
|
||||||
write_register(mt_bbp_agc(2), 0x0000001f);
|
|
||||||
|
|
||||||
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
|
auto chip::get_channel_power(channel *chan) -> int
|
||||||
@@ -800,16 +813,18 @@ auto chip::associate_client(std::uint8_t wcid,
|
|||||||
{
|
{
|
||||||
auto address = mac_address();
|
auto address = mac_address();
|
||||||
|
|
||||||
// struct ieee80211_mgmt (assoc_resp), 26 bytes:
|
// struct ieee80211_mgmt (assoc_resp), 30 bytes:
|
||||||
// [frame_control][duration][da][sa][bssid][status_code][aid].
|
// [header][capability_info][status_code][aid].
|
||||||
std::uint8_t mgmt[26] = {};
|
std::uint8_t mgmt[30] = {};
|
||||||
xone::store_le16(mgmt + 0, ieee80211_ftype_mgmt
|
xone::store_le16(mgmt + 0, ieee80211_ftype_mgmt
|
||||||
| ieee80211_stype_assoc_resp);
|
| ieee80211_stype_assoc_resp);
|
||||||
std::memcpy(mgmt + 4, addr.data(), 6); // da
|
std::memcpy(mgmt + 4, addr.data(), 6); // da
|
||||||
std::memcpy(mgmt + 10, address.data(), 6); // sa
|
std::memcpy(mgmt + 10, address.data(), 6); // sa
|
||||||
std::memcpy(mgmt + 16, address.data(), 6); // bssid
|
std::memcpy(mgmt + 16, address.data(), 6); // bssid
|
||||||
xone::store_le16(mgmt + 22, 0x0110); // status_code (original)
|
// Capability info stays zero, matching the upstream zero-initialized
|
||||||
xone::store_le16(mgmt + 24, 0x0f00); // aid (original)
|
// 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.
|
// Payload: mgmt frame plus 8 bytes of zero padding.
|
||||||
std::vector<std::uint8_t> payload(sizeof(mgmt) + 8, 0);
|
std::vector<std::uint8_t> payload(sizeof(mgmt) + 8, 0);
|
||||||
@@ -832,20 +847,20 @@ auto chip::pair_client(std::span<std::uint8_t const> addr) -> int
|
|||||||
{
|
{
|
||||||
auto address = mac_address();
|
auto address = mac_address();
|
||||||
|
|
||||||
// struct ieee80211_hdr_3addr (22 bytes) plus the reserved command and a
|
// struct ieee80211_hdr_3addr (24 bytes) plus the reserved command and a
|
||||||
// 9-byte payload: [fc][duration][addr1][addr2][addr3][0x70][PAIR_RESP]
|
// 9-byte payload: [header][0x70][PAIR_RESP][data].
|
||||||
// [data].
|
std::uint8_t frame[35] = {};
|
||||||
std::uint8_t frame[33] = {};
|
|
||||||
xone::store_le16(frame + 0, ieee80211_ftype_mgmt
|
xone::store_le16(frame + 0, ieee80211_ftype_mgmt
|
||||||
| ieee80211_stype_wlan_reserved);
|
| ieee80211_stype_wlan_reserved);
|
||||||
std::memcpy(frame + 4, addr.data(), 6); // addr1
|
std::memcpy(frame + 4, addr.data(), 6); // addr1
|
||||||
std::memcpy(frame + 10, address.data(), 6); // addr2
|
std::memcpy(frame + 10, address.data(), 6); // addr2
|
||||||
std::memcpy(frame + 16, address.data(), 6); // addr3
|
std::memcpy(frame + 16, address.data(), 6); // addr3
|
||||||
frame[22] = ieee80211_stype_wlan_reserved; // reserved (0x70)
|
// seq_ctrl stays zero, matching the upstream zero-initialized header.
|
||||||
frame[23] = client_cmd::client_pair_resp;
|
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,
|
std::uint8_t data[9] = { 0x00, 0x45, 0x55, 0x01, 0x0f, 0x8f, 0xff, 0x87,
|
||||||
0x1f };
|
0x1f };
|
||||||
std::memcpy(frame + 24, data, sizeof(data));
|
std::memcpy(frame + 26, data, sizeof(data));
|
||||||
|
|
||||||
return send_wlan({ frame, frame + sizeof(frame) });
|
return send_wlan({ frame, frame + sizeof(frame) });
|
||||||
}
|
}
|
||||||
@@ -857,9 +872,9 @@ auto chip::send_client_command(std::uint8_t wcid,
|
|||||||
{
|
{
|
||||||
auto address = mac_address();
|
auto address = mac_address();
|
||||||
|
|
||||||
// Payload: [info(8)][txwi(20)][hdr(22)][0x70][cmd][data].
|
// Payload: [info(8)][txwi(20)][hdr(24)][0x70][cmd][data].
|
||||||
auto txwi = build_txwi(wcid - 1, 22 + 2 + data.size());
|
auto txwi = build_txwi(wcid - 1, 24 + 2 + data.size());
|
||||||
std::vector<std::uint8_t> payload(8 + txwi.size() + 22 + 2 + data.size());
|
std::vector<std::uint8_t> payload(8 + txwi.size() + 24 + 2 + data.size());
|
||||||
|
|
||||||
// info: {0, 0, 0, wcid-1, 0, 0, 0, 0}.
|
// info: {0, 0, 0, wcid-1, 0, 0, 0, 0}.
|
||||||
payload[3] = wcid - 1;
|
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 + 4, addr.data(), 6); // addr1
|
||||||
std::memcpy(hdr + 10, address.data(), 6); // addr2
|
std::memcpy(hdr + 10, address.data(), 6); // addr2
|
||||||
std::memcpy(hdr + 16, address.data(), 6); // addr3
|
std::memcpy(hdr + 16, address.data(), 6); // addr3
|
||||||
hdr[22] = ieee80211_stype_wlan_reserved; // reserved (0x70)
|
// seq_ctrl stays zero, matching the upstream zero-initialized header.
|
||||||
hdr[23] = static_cast<std::uint8_t>(cmd);
|
hdr[24] = ieee80211_stype_wlan_reserved; // reserved (0x70)
|
||||||
|
hdr[25] = static_cast<std::uint8_t>(cmd);
|
||||||
if (!data.empty())
|
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());
|
return send_command(0, payload.data(), payload.size());
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user