feat: pairing mode and RX frame logging
The dongle beacon carries a pairing flag in the Microsoft OUI IE; with it clear, controllers ignore the beacon. Port xone_dongle_toggle_pairing: xone_set_pairing() rewrites the beacon with the flag set and blinks the LED, the GUI button triggers it, EVT_BUTTON (dongle button) does too, and the LED follows upstream state (blink while pairing, on when a controller is connected, off otherwise). The CLI gains a pair command. Log every inbound frame (info word + first 16 bytes) so the RX path can be traced against hardware. Co-Authored-By: qwen3.8-27b@q2_k_xl: pairing mode and RX logging
This commit is contained in:
+1
-1
@@ -6,7 +6,7 @@ if(NOT CMAKE_GENERATOR MATCHES "^(Ninja|Xcode)$")
|
||||
endif()
|
||||
|
||||
cmake_minimum_required(VERSION 3.21)
|
||||
project(xone_macos VERSION 0.1.10 LANGUAGES CXX Swift)
|
||||
project(xone_macos VERSION 0.1.11 LANGUAGES CXX Swift)
|
||||
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
|
||||
@@ -72,6 +72,9 @@ int xone_controller_count(xone_dongle const *d);
|
||||
// success, -1 if index is out of range.
|
||||
int xone_controller_mac(xone_dongle const *d, int index, char *buf, int len);
|
||||
|
||||
// Enter or leave pairing mode (beacon pairing flag + LED blink).
|
||||
void xone_set_pairing(xone_dongle *d, bool enable);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
+62
-8
@@ -3,6 +3,7 @@
|
||||
#include "app/xone_api.h"
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
@@ -46,6 +47,10 @@ struct xone_dongle {
|
||||
// Connected controllers (wcid 1..16). Guarded by lock.
|
||||
std::vector<controller> controllers;
|
||||
|
||||
// Pairing mode state (port of dongle->pairing). Shared between the UI
|
||||
// thread and the transport reader thread.
|
||||
std::atomic<bool> pairing{false};
|
||||
|
||||
~xone_dongle()
|
||||
{
|
||||
// Stop the reader thread before the chip is torn down so a late IN
|
||||
@@ -66,6 +71,9 @@ struct xone_dongle {
|
||||
auto handle_client_command(std::span<std::uint8_t const> payload,
|
||||
std::uint8_t wcid,
|
||||
std::span<std::uint8_t const> addr) -> void;
|
||||
|
||||
// Enter or leave pairing mode (port of xone_dongle_toggle_pairing).
|
||||
auto set_pairing(bool enable) -> void;
|
||||
};
|
||||
|
||||
namespace {
|
||||
@@ -109,6 +117,14 @@ auto xone_dongle::process_message(void const *data, std::size_t len) -> void
|
||||
// Strip the header + trailer.
|
||||
buf = buf.subspan(cmd_hdr_len, len - 2 * cmd_hdr_len);
|
||||
|
||||
char hex[64] = "";
|
||||
int off = 0;
|
||||
for (std::size_t i = 0; i < buf.size() && i < 16 && off < 48; ++i)
|
||||
off += std::snprintf(hex + off, sizeof(hex) - off, "%02x ", buf[i]);
|
||||
xone::log_msg(xone::log_level::debug,
|
||||
"api: rx info=0x%08x port=%d len=%zu %s",
|
||||
(unsigned)info, (unsigned)port, buf.size(), hex);
|
||||
|
||||
if (port == dma_msg_port::wlan_port) {
|
||||
process_wlan(buf);
|
||||
return;
|
||||
@@ -119,7 +135,9 @@ auto xone_dongle::process_message(void const *data, std::size_t len) -> void
|
||||
|
||||
switch (field_get(mt_rx_fce_info_evt_type, info)) {
|
||||
case cpu_evt::evt_button:
|
||||
// Pairing mode is entered via the CLI for now.
|
||||
// The dongle pairing button was pressed (port of
|
||||
// xone_dongle_handle_button).
|
||||
set_pairing(true);
|
||||
break;
|
||||
case cpu_evt::evt_packet_rx:
|
||||
process_wlan(buf);
|
||||
@@ -166,6 +184,25 @@ auto xone_dongle::process_wlan(std::span<std::uint8_t const> buf) -> void
|
||||
// DATA|QOS_DATA (GIP) is handled in a later increment.
|
||||
}
|
||||
|
||||
auto xone_dongle::set_pairing(bool enable) -> void
|
||||
{
|
||||
if (!chip)
|
||||
return;
|
||||
pairing = enable;
|
||||
chip->set_pairing(enable);
|
||||
bool has_clients = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(lock);
|
||||
has_clients = !controllers.empty();
|
||||
}
|
||||
auto led = enable ? xone::mt76::led_mode::led_blink
|
||||
: (has_clients ? xone::mt76::led_mode::led_on
|
||||
: xone::mt76::led_mode::led_off);
|
||||
chip->set_led_mode(led);
|
||||
xone::log_msg(xone::log_level::info, "api: pairing mode %s",
|
||||
enable ? "enabled" : "disabled");
|
||||
}
|
||||
|
||||
auto xone_dongle::handle_association(std::span<std::uint8_t const> addr) -> void
|
||||
{
|
||||
// Find a free WCID slot (1..16).
|
||||
@@ -192,8 +229,12 @@ auto xone_dongle::handle_association(std::span<std::uint8_t const> addr) -> void
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> guard(lock);
|
||||
controllers.push_back(controller{wcid, mac});
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(lock);
|
||||
controllers.push_back(controller{wcid, mac});
|
||||
}
|
||||
if (!pairing)
|
||||
chip->set_led_mode(xone::mt76::led_mode::led_on);
|
||||
xone::log_msg(xone::log_level::info,
|
||||
"api: controller associated (wcid=%d)", wcid);
|
||||
}
|
||||
@@ -206,13 +247,19 @@ auto xone_dongle::handle_disassociation(std::uint8_t wcid) -> void
|
||||
// Remove from the chip outside the lock.
|
||||
chip->remove_client(wcid);
|
||||
|
||||
std::lock_guard<std::mutex> guard(lock);
|
||||
for (auto it = controllers.begin(); it != controllers.end(); ++it) {
|
||||
if (it->wcid == wcid) {
|
||||
controllers.erase(it);
|
||||
break;
|
||||
bool last = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(lock);
|
||||
for (auto it = controllers.begin(); it != controllers.end(); ++it) {
|
||||
if (it->wcid == wcid) {
|
||||
controllers.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
last = controllers.empty();
|
||||
}
|
||||
if (last && !pairing)
|
||||
chip->set_led_mode(xone::mt76::led_mode::led_off);
|
||||
xone::log_msg(xone::log_level::info,
|
||||
"api: controller removed (wcid=%d)", wcid);
|
||||
}
|
||||
@@ -400,3 +447,10 @@ extern "C" int xone_controller_mac(xone_dongle const *d, int index, char *buf,
|
||||
c.mac[0], c.mac[1], c.mac[2], c.mac[3], c.mac[4], c.mac[5]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Enter or leave pairing mode: sets the beacon pairing flag and blinks the
|
||||
// LED while enabled (port of xone_dongle_toggle_pairing).
|
||||
extern "C" void xone_set_pairing(xone_dongle *d, bool enable)
|
||||
{
|
||||
d->set_pairing(enable);
|
||||
}
|
||||
|
||||
+4
-2
@@ -58,9 +58,11 @@ struct ContentView: View {
|
||||
|
||||
HStack {
|
||||
Button("Pair Controller") {
|
||||
// TODO(phase 5): pairing UI (LED blink + controller press)
|
||||
if let s = session {
|
||||
xone_set_pairing(s, true)
|
||||
}
|
||||
}
|
||||
.disabled(!donglePresent)
|
||||
.disabled(radioState != XONE_STATE_READY)
|
||||
|
||||
Spacer()
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ auto usage() -> int
|
||||
" info Show dongle and chip state\n"
|
||||
" firmware <path> Load the firmware image\n"
|
||||
" radio-init Initialize the radio\n"
|
||||
" pair Enter pairing mode (beacon flag + LED blink)\n"
|
||||
" led <mode> Set LED mode (0 blink, 1 on, 2 off)\n"
|
||||
" func <func> <val> MCU function select (debug)\n"
|
||||
" radio-deinit Suspend the radio\n"
|
||||
@@ -68,6 +69,13 @@ auto run(int argc, char **argv, xone::usb::transport &transport,
|
||||
int ret = chip.init_radio();
|
||||
if (ret != 0)
|
||||
std::fprintf(stderr, "xone_cli: radio init failed (%d)\n", ret);
|
||||
} else if (!std::strcmp(cmd, "pair")) {
|
||||
int ret = chip.set_pairing(true);
|
||||
if (ret != 0) {
|
||||
std::fprintf(stderr, "xone_cli: pair failed (%d)\n", ret);
|
||||
return 1;
|
||||
}
|
||||
chip.set_led_mode(xone::mt76::led_mode::led_blink);
|
||||
} else if (!std::strcmp(cmd, "func")) {
|
||||
if (i + 2 > argc)
|
||||
return usage();
|
||||
|
||||
Reference in New Issue
Block a user