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()
|
endif()
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.21)
|
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)
|
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.
|
// success, -1 if index is out of range.
|
||||||
int xone_controller_mac(xone_dongle const *d, int index, char *buf, int len);
|
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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+55
-1
@@ -3,6 +3,7 @@
|
|||||||
#include "app/xone_api.h"
|
#include "app/xone_api.h"
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <atomic>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -46,6 +47,10 @@ struct xone_dongle {
|
|||||||
// Connected controllers (wcid 1..16). Guarded by lock.
|
// Connected controllers (wcid 1..16). Guarded by lock.
|
||||||
std::vector<controller> controllers;
|
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()
|
~xone_dongle()
|
||||||
{
|
{
|
||||||
// Stop the reader thread before the chip is torn down so a late IN
|
// 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,
|
auto handle_client_command(std::span<std::uint8_t const> payload,
|
||||||
std::uint8_t wcid,
|
std::uint8_t wcid,
|
||||||
std::span<std::uint8_t const> addr) -> void;
|
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 {
|
namespace {
|
||||||
@@ -109,6 +117,14 @@ auto xone_dongle::process_message(void const *data, std::size_t len) -> void
|
|||||||
// Strip the header + trailer.
|
// Strip the header + trailer.
|
||||||
buf = buf.subspan(cmd_hdr_len, len - 2 * cmd_hdr_len);
|
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) {
|
if (port == dma_msg_port::wlan_port) {
|
||||||
process_wlan(buf);
|
process_wlan(buf);
|
||||||
return;
|
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)) {
|
switch (field_get(mt_rx_fce_info_evt_type, info)) {
|
||||||
case cpu_evt::evt_button:
|
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;
|
break;
|
||||||
case cpu_evt::evt_packet_rx:
|
case cpu_evt::evt_packet_rx:
|
||||||
process_wlan(buf);
|
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.
|
// 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
|
auto xone_dongle::handle_association(std::span<std::uint8_t const> addr) -> void
|
||||||
{
|
{
|
||||||
// Find a free WCID slot (1..16).
|
// 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
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});
|
||||||
|
}
|
||||||
|
if (!pairing)
|
||||||
|
chip->set_led_mode(xone::mt76::led_mode::led_on);
|
||||||
xone::log_msg(xone::log_level::info,
|
xone::log_msg(xone::log_level::info,
|
||||||
"api: controller associated (wcid=%d)", wcid);
|
"api: controller associated (wcid=%d)", wcid);
|
||||||
}
|
}
|
||||||
@@ -206,6 +247,8 @@ auto xone_dongle::handle_disassociation(std::uint8_t wcid) -> void
|
|||||||
// Remove from the chip outside the lock.
|
// Remove from the chip outside the lock.
|
||||||
chip->remove_client(wcid);
|
chip->remove_client(wcid);
|
||||||
|
|
||||||
|
bool last = false;
|
||||||
|
{
|
||||||
std::lock_guard<std::mutex> guard(lock);
|
std::lock_guard<std::mutex> guard(lock);
|
||||||
for (auto it = controllers.begin(); it != controllers.end(); ++it) {
|
for (auto it = controllers.begin(); it != controllers.end(); ++it) {
|
||||||
if (it->wcid == wcid) {
|
if (it->wcid == wcid) {
|
||||||
@@ -213,6 +256,10 @@ auto xone_dongle::handle_disassociation(std::uint8_t wcid) -> void
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
last = controllers.empty();
|
||||||
|
}
|
||||||
|
if (last && !pairing)
|
||||||
|
chip->set_led_mode(xone::mt76::led_mode::led_off);
|
||||||
xone::log_msg(xone::log_level::info,
|
xone::log_msg(xone::log_level::info,
|
||||||
"api: controller removed (wcid=%d)", wcid);
|
"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]);
|
c.mac[0], c.mac[1], c.mac[2], c.mac[3], c.mac[4], c.mac[5]);
|
||||||
return 0;
|
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 {
|
HStack {
|
||||||
Button("Pair Controller") {
|
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()
|
Spacer()
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ auto usage() -> int
|
|||||||
" info Show dongle and chip state\n"
|
" info Show dongle and chip state\n"
|
||||||
" firmware <path> Load the firmware image\n"
|
" firmware <path> Load the firmware image\n"
|
||||||
" radio-init Initialize the radio\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"
|
" led <mode> Set LED mode (0 blink, 1 on, 2 off)\n"
|
||||||
" func <func> <val> MCU function select (debug)\n"
|
" func <func> <val> MCU function select (debug)\n"
|
||||||
" radio-deinit Suspend the radio\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();
|
int ret = chip.init_radio();
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
std::fprintf(stderr, "xone_cli: radio init failed (%d)\n", ret);
|
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")) {
|
} else if (!std::strcmp(cmd, "func")) {
|
||||||
if (i + 2 > argc)
|
if (i + 2 > argc)
|
||||||
return usage();
|
return usage();
|
||||||
|
|||||||
Reference in New Issue
Block a user