feat: port MT76 radio init and CLI tool
Complete the radio bring-up sequence: init_registers now writes the upstream register values (PBF out of reset, beacon TX off), crystal calibration, MAC/BSSID programming, channel evaluation, and beacon programming through an MCU burst into PBF shared memory. The beacon txwi is the full 20-byte struct and the RF patch is applied on the cold firmware path. Download firmware per product (xone_dongle_02e6.bin / 02fe.bin) and add the xone_cli debug tool (info, firmware, radio-init/deinit, burst, reg-read/write, led, recover). Recover uses USBDeviceReEnumerate for a host-side port reset. Co-Authored-By: grok4.6: internet search (firmware split, beacon SRAM, upstream issues) Co-Authored-By: qwen3.8-27b@q2_k_xl: initial radio init and CLI implementation Co-Authored-By: deepseek/deepseek-v4-pro-0813: final radio init fixes and verification
This commit is contained in:
+56
-20
@@ -5,6 +5,7 @@
|
||||
|
||||
#include <cerrno>
|
||||
#include <condition_variable>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
@@ -15,6 +16,7 @@
|
||||
#include <IOKit/IOCFPlugIn.h>
|
||||
#include <IOKit/IOKitLib.h>
|
||||
#include <IOKit/usb/IOUSBLib.h>
|
||||
#include <IOKit/usb/USB.h>
|
||||
|
||||
#include "common/log.hpp"
|
||||
|
||||
@@ -96,6 +98,7 @@ struct transport::state {
|
||||
IOUSBInterfaceInterface190 **iface_ref = nullptr;
|
||||
};
|
||||
std::vector<iface_conn> ifaces;
|
||||
bool re_enumerated = false;
|
||||
|
||||
struct pipe_ref {
|
||||
IOUSBInterfaceInterface190 **iface_ref = nullptr;
|
||||
@@ -138,37 +141,70 @@ auto transport::probe(frame_callback frames, disconnect_callback disconnected) -
|
||||
return t;
|
||||
}
|
||||
|
||||
void transport::stop_pump()
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(state_->lock);
|
||||
if (state_->stopping)
|
||||
return;
|
||||
state_->stopping = true;
|
||||
}
|
||||
|
||||
for (auto &slot : state_->slots)
|
||||
(*slot.iface_ref)->AbortPipe(slot.iface_ref, slot.pipe_ref);
|
||||
|
||||
if (!state_->thread_started)
|
||||
return;
|
||||
|
||||
CFRunLoopRef runloop = nullptr;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(state_->lock);
|
||||
state_->cv.wait(lock, [this] { return state_->loop_ready && state_->in_flight == 0; });
|
||||
runloop = state_->runloop;
|
||||
}
|
||||
|
||||
// The reader thread is joined below; no callback can run after this.
|
||||
CFRunLoopStop(runloop);
|
||||
state_->thread.join();
|
||||
}
|
||||
|
||||
auto transport::re_enumerate() -> int
|
||||
{
|
||||
// Stop the pump first so no completion callback touches the interface
|
||||
// references while the kernel tears them down.
|
||||
stop_pump();
|
||||
|
||||
auto kr = (*state_->dev_ref)->USBDeviceReEnumerate(state_->dev_ref, kUSBAddExtraResetTimeMask);
|
||||
if (kr != kIOReturnSuccess) {
|
||||
xone::log_msg(log_level::error, "usb: re-enumerate failed (%d)", kr);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
// The kernel terminated all of our clients; the USB references are dead.
|
||||
state_->re_enumerated = true;
|
||||
xone::log_msg(log_level::info, "usb: re-enumerate ok");
|
||||
return 0;
|
||||
}
|
||||
transport::~transport()
|
||||
{
|
||||
if (!state_)
|
||||
return;
|
||||
|
||||
// Stop generating completions, then wait for the in-flight ones to drain.
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(state_->lock);
|
||||
state_->stopping = true;
|
||||
}
|
||||
for (auto &slot : state_->slots)
|
||||
(*slot.iface_ref)->AbortPipe(slot.iface_ref, slot.pipe_ref);
|
||||
|
||||
if (state_->thread_started) {
|
||||
CFRunLoopRef runloop = nullptr;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(state_->lock);
|
||||
state_->cv.wait(lock, [this] { return state_->loop_ready && state_->in_flight == 0; });
|
||||
runloop = state_->runloop;
|
||||
}
|
||||
|
||||
// The reader thread is joined below; no callback can run after this.
|
||||
CFRunLoopStop(runloop);
|
||||
state_->thread.join();
|
||||
}
|
||||
stop_pump();
|
||||
|
||||
// Release the termination watch and async event sources.
|
||||
if (state_->termination_iter)
|
||||
IOObjectRelease(state_->termination_iter);
|
||||
if (state_->notify_port)
|
||||
IONotificationPortDestroy(state_->notify_port);
|
||||
|
||||
if (state_->re_enumerated) {
|
||||
// The kernel already tore down the device and interfaces.
|
||||
if (state_->service)
|
||||
IOObjectRelease(state_->service);
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto *source : state_->sources)
|
||||
CFRelease(source);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user