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:
@@ -13,6 +13,8 @@
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include "mt76/mt76_defs.hpp"
|
||||
|
||||
namespace xone::usb {
|
||||
class transport;
|
||||
}
|
||||
@@ -48,6 +50,25 @@ public:
|
||||
// Build string of the loaded firmware image (empty until load_firmware).
|
||||
auto firmware_build() const -> char const *;
|
||||
|
||||
// MCU commands (port of the xone_mt76_* wrappers around send_command).
|
||||
auto set_led_mode(std::uint32_t mode) -> int;
|
||||
auto select_function(std::uint32_t func, std::uint32_t val) -> int;
|
||||
auto set_power_mode(power_mode mode) -> int;
|
||||
auto load_cr(cr_mode mode) -> int;
|
||||
auto write_burst(std::uint32_t idx, void *data, std::size_t len) -> int;
|
||||
auto send_ms_command(ms_command cmd, void *data, std::size_t len) -> int;
|
||||
auto calibrate(calibration calib, std::uint32_t val) -> int;
|
||||
|
||||
// Initialize the radio (port of xone_mt76_init_radio). Returns 0 or -errno.
|
||||
auto init_radio() -> int;
|
||||
|
||||
// Suspend or resume the radio (ports of xone_mt76_suspend/resume_radio).
|
||||
auto suspend_radio() -> int;
|
||||
auto resume_radio() -> int;
|
||||
|
||||
// Enter or leave pairing mode (port of xone_mt76_set_pairing).
|
||||
auto set_pairing(bool enable) -> int;
|
||||
|
||||
private:
|
||||
// Send an MCU command (port of xone_mt76_send_command). `cmd` is the
|
||||
// MT_MCU_MSG_CMD_TYPE field. Returns bytes written, or a negative errno.
|
||||
@@ -61,8 +82,27 @@ private:
|
||||
auto send_firmware(void const *fw_data, std::size_t fw_size) -> int;
|
||||
auto reset_firmware() -> int;
|
||||
|
||||
// Radio suspend/resume steps (ports of the set_wow_* wrappers).
|
||||
auto set_wow_enable(bool enable) -> int;
|
||||
auto set_wow_traffic(wow_traffic traffic) -> int;
|
||||
|
||||
// Radio init steps (port of the xone_mt76_init_radio sub-functions).
|
||||
auto init_registers() -> void;
|
||||
auto calibrate_crystal() -> int;
|
||||
auto init_address() -> int;
|
||||
auto set_idle_time() -> int;
|
||||
auto calibrate_radio() -> int;
|
||||
auto get_channel_power(channel *chan) -> int;
|
||||
auto switch_channel(channel const *chan) -> int;
|
||||
auto evaluate_channels() -> int;
|
||||
auto init_channels() -> int;
|
||||
auto write_beacon(bool pair) -> int;
|
||||
auto set_channel_candidates() -> int;
|
||||
|
||||
usb::transport &transport_;
|
||||
char build_time_[17] = {};
|
||||
std::array<channel, num_channels> channels_{};
|
||||
channel current_channel_{};
|
||||
};
|
||||
|
||||
} // namespace xone::mt76
|
||||
|
||||
+271
-2
@@ -3,8 +3,8 @@
|
||||
// ==============================================================================
|
||||
// MT76 chip register definitions (port of transport/mt76_defs.h)
|
||||
// ==============================================================================
|
||||
// Only the registers needed so far are ported; the rest land with the radio
|
||||
// init and firmware load increments.
|
||||
// Only the registers needed so far are ported; the rest land with the
|
||||
// pairing and data path increments.
|
||||
// ==============================================================================
|
||||
|
||||
#include <cstddef>
|
||||
@@ -113,4 +113,273 @@ struct fw_header {
|
||||
char build_time[16];
|
||||
} XONE_PACKED;
|
||||
|
||||
// MCU command types (port of enum mt76_mcu_cmd).
|
||||
enum mcu_cmd : std::uint32_t {
|
||||
cmd_fun_set_op = 1,
|
||||
cmd_load_cr = 2,
|
||||
cmd_init_gain_op = 3,
|
||||
cmd_dync_vga_op = 6,
|
||||
cmd_tdls_ch_sw = 7,
|
||||
cmd_burst_write = 8,
|
||||
cmd_read_modify_write = 9,
|
||||
cmd_random_read = 10,
|
||||
cmd_burst_read = 11,
|
||||
cmd_random_write = 12,
|
||||
cmd_led_mode_op = 16,
|
||||
cmd_power_saving_op = 20,
|
||||
cmd_wow_config = 21,
|
||||
cmd_wow_query = 22,
|
||||
cmd_wow_feature = 24,
|
||||
cmd_carrier_detect_op = 28,
|
||||
cmd_rador_detect_op = 29,
|
||||
cmd_switch_channel_op = 30,
|
||||
cmd_calibration_op = 31,
|
||||
cmd_beacon_op = 32,
|
||||
cmd_antenna_op = 33,
|
||||
};
|
||||
|
||||
// MCU function select (port of enum mt76_mcu_function).
|
||||
enum mcu_function : std::uint32_t {
|
||||
fun_q_select = 1,
|
||||
fun_bw_setting = 2,
|
||||
fun_usb2_sw_disconnect = 2, // duplicate value upstream
|
||||
fun_usb3_sw_disconnect = 3,
|
||||
fun_log_fw_debug_msg = 4,
|
||||
fun_get_fw_version = 5,
|
||||
};
|
||||
|
||||
// CR load modes (port of enum mt76_mcu_cr_mode).
|
||||
enum cr_mode : std::uint32_t {
|
||||
rf_cr = 0,
|
||||
bbp_cr,
|
||||
rf_bbp_cr,
|
||||
hl_temp_cr_update,
|
||||
};
|
||||
|
||||
// Radio power modes (port of enum mt76_mcu_power_mode).
|
||||
enum power_mode : std::uint32_t {
|
||||
radio_off = 0x30,
|
||||
radio_on = 0x31,
|
||||
radio_off_auto_wakeup = 0x32,
|
||||
radio_off_advance = 0x33,
|
||||
radio_on_advance = 0x34,
|
||||
};
|
||||
|
||||
// Calibration types (port of enum mt76_mcu_calibration).
|
||||
enum calibration : std::uint32_t {
|
||||
cal_r = 1,
|
||||
cal_temp_sensor,
|
||||
cal_rxdcoc,
|
||||
cal_rc,
|
||||
cal_sx_logen,
|
||||
cal_lc,
|
||||
cal_tx_loft,
|
||||
cal_txiq,
|
||||
cal_tssi,
|
||||
cal_tssi_comp,
|
||||
cal_dpd,
|
||||
cal_rxiqc_fi,
|
||||
};
|
||||
|
||||
// Commands to Microsoft's proprietary firmware (port of enum xone_mt76_ms_command).
|
||||
enum ms_command : std::uint32_t {
|
||||
ms_set_mac_address = 0x00,
|
||||
ms_add_client = 0x01,
|
||||
ms_remove_client = 0x02,
|
||||
ms_set_idle_time = 0x05,
|
||||
ms_set_chan_candidates = 0x07,
|
||||
};
|
||||
|
||||
// LED modes (port of enum xone_mt76_led_mode).
|
||||
enum led_mode : std::uint32_t {
|
||||
led_blink = 0x00,
|
||||
led_on = 0x01,
|
||||
led_off = 0x02,
|
||||
};
|
||||
|
||||
// Wake-on-wireless features (port of enum xone_mt76_wow_feature).
|
||||
enum wow_feature : std::uint32_t {
|
||||
wow_enable = 0x01,
|
||||
wow_traffic_op = 0x03,
|
||||
};
|
||||
|
||||
// WOW traffic direction (port of enum xone_mt76_wow_traffic).
|
||||
enum wow_traffic : std::uint32_t {
|
||||
wow_to_firmware = 0x00,
|
||||
wow_to_host = 0x01,
|
||||
};
|
||||
|
||||
// PHY types (port of enum mt76_phy_type, used in MT_RXWI_RATE_PHY).
|
||||
enum phy_type : std::uint32_t {
|
||||
phy_cck = 0,
|
||||
phy_ofdm,
|
||||
phy_ht,
|
||||
phy_ht_gf,
|
||||
phy_vht,
|
||||
phy_he_su = 8,
|
||||
};
|
||||
|
||||
// PHY bandwidths (port of enum mt76_phy_bandwidth).
|
||||
enum phy_bandwidth : std::uint32_t {
|
||||
bw_20 = 0,
|
||||
bw_40,
|
||||
bw_80,
|
||||
};
|
||||
|
||||
// Calibration channel groups (port of enum mt76_cal_channel_group).
|
||||
enum cal_channel_group : std::uint32_t {
|
||||
ch_5g_japan = 0,
|
||||
ch_5g_unii_1,
|
||||
ch_5g_unii_2,
|
||||
ch_5g_unii_2e_1,
|
||||
ch_5g_unii_2e_2,
|
||||
ch_5g_unii_3,
|
||||
};
|
||||
|
||||
// Channel bands (port of XONE_MT_CH_*).
|
||||
constexpr std::uint8_t ch_2g_low = 0x01;
|
||||
constexpr std::uint8_t ch_2g_mid = 0x02;
|
||||
constexpr std::uint8_t ch_2g_high = 0x03;
|
||||
constexpr std::uint8_t ch_5g_low = 0x01;
|
||||
constexpr std::uint8_t ch_5g_high = 0x02;
|
||||
|
||||
// Channel table (port of xone_mt76_channels).
|
||||
struct channel {
|
||||
std::uint8_t index;
|
||||
std::uint8_t band; // ch_* value
|
||||
std::uint8_t bandwidth; // phy_bandwidth
|
||||
std::uint8_t group; // cal_channel_group (5G only)
|
||||
bool scan;
|
||||
std::uint8_t power;
|
||||
};
|
||||
constexpr std::size_t num_channels = 12;
|
||||
inline constexpr channel channels[num_channels] = {
|
||||
{ 0x01, ch_2g_low, bw_20, 0, true, 0 },
|
||||
{ 0x06, ch_2g_mid, bw_20, 0, true, 0 },
|
||||
{ 0x0b, ch_2g_high, bw_20, 0, true, 0 },
|
||||
{ 0x24, ch_5g_low, bw_40, ch_5g_unii_1, true, 0 },
|
||||
{ 0x28, ch_5g_low, bw_40, ch_5g_unii_1, false, 0 },
|
||||
{ 0x2c, ch_5g_high, bw_40, ch_5g_unii_1, true, 0 },
|
||||
{ 0x30, ch_5g_high, bw_40, ch_5g_unii_1, false, 0 },
|
||||
{ 0x95, ch_5g_low, bw_80, ch_5g_unii_3, true, 0 },
|
||||
{ 0x99, ch_5g_low, bw_80, ch_5g_unii_3, false, 0 },
|
||||
{ 0x9d, ch_5g_high, bw_80, ch_5g_unii_3, true, 0 },
|
||||
{ 0xa1, ch_5g_high, bw_80, ch_5g_unii_3, false, 0 },
|
||||
{ 0xa5, ch_5g_high, bw_80, ch_5g_unii_3, false, 0 },
|
||||
};
|
||||
|
||||
// EFUSE TX power tables and crystal trim.
|
||||
constexpr std::uint16_t mt_ee_xtal_trim_1 = 0x003a;
|
||||
constexpr std::uint16_t mt_ee_xtal_trim_2 = 0x009e;
|
||||
constexpr std::uint16_t mt_ee_tx_power_0_start_2g = 0x0056;
|
||||
constexpr std::size_t tx_power_group_size_5g = 5;
|
||||
constexpr std::uint16_t mt_ee_tx_power_0_start_5g = 0x0062;
|
||||
|
||||
// Crystal oscillator control registers.
|
||||
constexpr std::uint32_t mt_cmb_ctrl = 0x0020;
|
||||
constexpr std::uint32_t mt_xo_ctrl5 = 0x0114;
|
||||
constexpr std::uint32_t mt_xo_ctrl5_c2_val = genmask(14, 8);
|
||||
constexpr std::uint32_t mt_xo_ctrl6 = 0x0118;
|
||||
constexpr std::uint32_t mt_xo_ctrl6_c2_ctrl = genmask(14, 8);
|
||||
|
||||
// MAC address registers and MCU memory map (port of MT_MCU_MEMMAP_WLAN).
|
||||
constexpr std::uint32_t mt_mcu_memmap_wlan = 0x410000;
|
||||
constexpr std::uint32_t mt_mac_addr_dw0 = 0x1008;
|
||||
constexpr std::uint32_t mt_mac_bssid_dw0 = 0x1010;
|
||||
|
||||
// MAC system control register.
|
||||
constexpr std::uint32_t mt_mac_sys_ctrl = 0x1004;
|
||||
constexpr std::uint32_t mt_mac_sys_ctrl_reset_csr = bit(0);
|
||||
constexpr std::uint32_t mt_mac_sys_ctrl_reset_bbp = bit(1);
|
||||
constexpr std::uint32_t mt_mac_sys_ctrl_enable_tx = bit(2);
|
||||
constexpr std::uint32_t mt_mac_sys_ctrl_enable_rx = bit(3);
|
||||
|
||||
// Radio init registers (port of the xone_mt76_init_registers writes).
|
||||
constexpr std::uint32_t mt_usb_dma_cfg = 0x0238;
|
||||
constexpr std::uint32_t mt_pwr_pin_cfg = 0x1204;
|
||||
constexpr std::uint32_t mt_ldo_ctrl_1 = 0x0070;
|
||||
constexpr std::uint32_t mt_wpdma_glo_cfg = 0x0208;
|
||||
constexpr std::uint32_t mt_wmm_aifsn = 0x0214;
|
||||
constexpr std::uint32_t mt_wmm_cwmin = 0x0218;
|
||||
constexpr std::uint32_t mt_wmm_cwmax = 0x021c;
|
||||
constexpr std::uint32_t mt_tso_ctrl = 0x0250;
|
||||
constexpr std::uint32_t mt_pbf_sys_ctrl = 0x0400;
|
||||
constexpr std::uint32_t mt_pbf_tx_max_pcnt = 0x0408;
|
||||
constexpr std::uint32_t mt_auto_rsp_cfg = 0x1404;
|
||||
constexpr std::uint32_t mt_max_len_cfg = 0x1018;
|
||||
constexpr std::uint32_t mt_ampdu_max_len_20m1s = 0x1030;
|
||||
constexpr std::uint32_t mt_ampdu_max_len_20m2s = 0x1034;
|
||||
constexpr std::uint32_t mt_bkoff_slot_cfg = 0x1104;
|
||||
constexpr std::uint32_t mt_edca_cfg_base = 0x1300;
|
||||
inline constexpr auto mt_edca_cfg_ac(std::size_t n) -> std::uint32_t
|
||||
{
|
||||
return mt_edca_cfg_base + (n << 2);
|
||||
}
|
||||
constexpr std::uint32_t mt_tx_pin_cfg = 0x1328;
|
||||
constexpr std::uint32_t mt_tx_sw_cfg0 = 0x1330;
|
||||
constexpr std::uint32_t mt_tx_sw_cfg1 = 0x1334;
|
||||
constexpr std::uint32_t mt_txop_ctrl_cfg = 0x1340;
|
||||
constexpr std::uint32_t mt_tx_rts_cfg = 0x1344;
|
||||
constexpr std::uint32_t mt_tx_timeout_cfg = 0x1348;
|
||||
constexpr std::uint32_t mt_tx_retry_cfg = 0x134c;
|
||||
constexpr std::uint32_t mt_cck_prot_cfg = 0x1364;
|
||||
constexpr std::uint32_t mt_ofdm_prot_cfg = 0x1368;
|
||||
constexpr std::uint32_t mt_mm20_prot_cfg = 0x136c;
|
||||
constexpr std::uint32_t mt_gf20_prot_cfg = 0x1374;
|
||||
constexpr std::uint32_t mt_gf40_prot_cfg = 0x1378;
|
||||
constexpr std::uint32_t mt_exp_ack_time = 0x1380;
|
||||
constexpr std::uint32_t mt_tx_alc_cfg_0 = 0x13b0;
|
||||
constexpr std::uint32_t mt_tx_alc_cfg_2 = 0x13a8;
|
||||
constexpr std::uint32_t mt_tx_alc_cfg_3 = 0x13ac;
|
||||
constexpr std::uint32_t mt_tx_alc_cfg_4 = 0x13c0;
|
||||
constexpr std::uint32_t mt_pifs_tx_cfg = 0x13ec;
|
||||
constexpr std::uint32_t mt_rx_filtr_cfg = 0x1400;
|
||||
constexpr std::uint32_t mt_legacy_basic_rate = 0x1408;
|
||||
constexpr std::uint32_t mt_ht_basic_rate = 0x140c;
|
||||
constexpr std::uint32_t mt_pn_pad_mode = 0x150c;
|
||||
constexpr std::uint32_t mt_txop_hldr_et = 0x1608;
|
||||
constexpr std::uint32_t mt_tx_prot_cfg6 = 0x13e0;
|
||||
constexpr std::uint32_t mt_tx_prot_cfg7 = 0x13e4;
|
||||
constexpr std::uint32_t mt_tx_prot_cfg8 = 0x13e8;
|
||||
constexpr std::uint32_t mt_dacclk_en_dly_cfg = 0x1264;
|
||||
constexpr std::uint32_t mt_rf_pa_mode_adj0 = 0x1228;
|
||||
constexpr std::uint32_t mt_rf_pa_mode_adj1 = 0x122c;
|
||||
constexpr std::uint32_t mt_tx0_rf_gain_corr = 0x13a0;
|
||||
constexpr std::uint32_t mt_tx1_rf_gain_corr = 0x13a4;
|
||||
constexpr std::uint32_t mt_pbf_cfg = 0x0404;
|
||||
constexpr std::uint32_t mt_pause_enable_control1 = 0x0a38;
|
||||
constexpr std::uint32_t mt_rf_bypass_0 = 0x0504;
|
||||
constexpr std::uint32_t mt_rf_setting_0 = 0x050c;
|
||||
constexpr std::uint32_t mt_xifs_time_cfg = 0x1100;
|
||||
constexpr std::uint32_t mt_fce_l2_stuff = 0x080c;
|
||||
constexpr std::uint32_t mt_ext_cca_cfg = 0x141c;
|
||||
constexpr std::uint32_t mt_ch_time_cfg = 0x110c;
|
||||
|
||||
// BBP registers (port of MT_BBP(type, n)).
|
||||
constexpr std::uint32_t mt_bbp_agc_base = 0x2300;
|
||||
inline constexpr auto mt_bbp_agc(std::size_t n) -> std::uint32_t
|
||||
{
|
||||
return mt_bbp_agc_base + (n << 2);
|
||||
}
|
||||
|
||||
// Beacon timer register.
|
||||
constexpr std::uint32_t mt_beacon_time_cfg = 0x1114;
|
||||
constexpr std::uint32_t mt_beacon_time_cfg_intval = genmask(15, 0);
|
||||
constexpr std::uint32_t mt_beacon_time_cfg_timer_en = bit(16);
|
||||
constexpr std::uint32_t mt_beacon_time_cfg_sync_mode = genmask(18, 17);
|
||||
constexpr std::uint32_t mt_beacon_time_cfg_tbtt_en = bit(19);
|
||||
constexpr std::uint32_t mt_beacon_time_cfg_beacon_tx = bit(20);
|
||||
|
||||
// Broadcast MAC address (port of eth_broadcast_addr).
|
||||
inline constexpr std::uint8_t broadcast_address[6] = {
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
};
|
||||
|
||||
// Beacon frame storage and TXWI fields.
|
||||
constexpr std::uint32_t mt_beacon_base = 0xc000;
|
||||
constexpr std::uint32_t mt_rxwi_rate_phy = genmask(15, 13);
|
||||
constexpr std::uint8_t mt_txwi_ack_ctl_nseq = bit(1);
|
||||
// 802.11 frame control for a beacon (MGMT type, BEACON subtype).
|
||||
constexpr std::uint16_t ieee80211_fc_beacon = 0x0080;
|
||||
|
||||
} // namespace xone::mt76
|
||||
|
||||
Reference in New Issue
Block a user