feat: port MT76 firmware load and download script

Add chip::load_firmware(): validates the xow_dongle.bin header, DMAs
the ILM and DLM images in 0x3800-byte chunks over EP 0x04 OUT with
FCE completion polling, then loads the IVB and waits for the firmware
to start. Probe now issues a USB reset first (port of
usb_reset_device), matching xone_dongle_probe.

The chip keeps its firmware across a USB reset on macOS and does not
set the upstream reset-complete bit, so load_firmware falls back to
the running firmware when the chip is still alive. Verified on
hardware: fresh load and re-plug both return success.

Add scripts/download-firmware.sh (port of install/firmware.sh), which
fetches the driver CAB from Windows Update and extracts
firmware/xow_dongle.bin, hash-verified.

Co-Authored-By: qwen3.8-27b@q2_k_xl: ported firmware load and download script
This commit is contained in:
portersky
2026-08-17 16:35:34 +02:00
parent ccc5bc819d
commit 21dc0e09b3
7 changed files with 318 additions and 4 deletions
+16
View File
@@ -41,7 +41,23 @@ public:
// MAC address from EFUSE with the 62:45:bd fallback applied.
auto mac_address() -> std::array<std::uint8_t, 6>;
// Load the firmware image from a file (port of xone_mt76_load_firmware).
// Resets the MCU if firmware is already loaded. Returns 0 or -errno.
auto load_firmware(char const *path) -> 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.
auto send_command(std::uint32_t cmd, void const *payload,
std::size_t payload_len) -> int;
// Firmware load steps (port of xone_mt76_*).
auto load_ivb() -> int;
auto send_firmware_part(std::uint32_t offset, void const *data,
std::size_t len) -> int;
auto send_firmware(void const *fw_data, std::size_t fw_size) -> int;
auto reset_firmware() -> int;
usb::transport &transport_;
};
+59
View File
@@ -7,8 +7,11 @@
// init and firmware load increments.
// ==============================================================================
#include <cstddef>
#include <cstdint>
#include "common/types.hpp"
namespace xone::mt76 {
// Bitfield helpers (port of the kernel BIT/GENMASK/FIELD_PREP macros).
@@ -54,4 +57,60 @@ enum efuse_mode : std::uint32_t {
efuse_physical_read,
};
// MCU message header fields.
constexpr std::uint32_t mt_mcu_msg_len = genmask(15, 0);
constexpr std::uint32_t mt_mcu_msg_cmd_seq = genmask(19, 16);
constexpr std::uint32_t mt_mcu_msg_cmd_type = genmask(26, 20);
constexpr std::uint32_t mt_mcu_msg_port = genmask(29, 27);
constexpr std::uint32_t mt_mcu_msg_type = genmask(31, 30);
constexpr std::uint32_t mt_mcu_msg_type_cmd = bit(30);
// DMA message ports (MT_MCU_MSG_PORT field).
enum dma_msg_port : std::uint32_t {
wlan_port = 0,
cpu_rx_port,
cpu_tx_port,
host_port,
virtual_cpu_rx_port,
virtual_cpu_tx_port,
discard,
};
// MCU message header length (port of MT_CMD_HDR_LEN).
constexpr std::size_t cmd_hdr_len = 4;
// FCE DMA registers.
constexpr std::uint32_t mt_fce_dma_addr = 0x0230;
constexpr std::uint32_t mt_fce_dma_len = 0x0234;
// Firmware load registers and constants (port of XONE_MT_FW_*).
constexpr std::uint32_t xone_mt_rf_patch = 0x0130;
constexpr std::uint32_t fw_load_ivb = 0x12;
constexpr std::uint32_t fw_ilm_offset = 0x080000;
constexpr std::uint32_t fw_dlm_offset = 0x110800;
constexpr std::size_t fw_chunk_size = 0x3800;
// USB DMA control register and bits.
constexpr std::uint32_t mt_usb_u3dma_cfg = 0x9018;
constexpr std::uint32_t mt_usb_dma_cfg_rx_bulk_en = bit(22);
constexpr std::uint32_t mt_usb_dma_cfg_tx_bulk_en = bit(23);
// Firmware load configuration registers.
constexpr std::uint32_t mt_fce_pse_ctrl = 0x0800;
constexpr std::uint32_t mt_tx_cpu_from_fce_base_ptr = 0x09a0;
constexpr std::uint32_t mt_tx_cpu_from_fce_max_count = 0x09a4;
constexpr std::uint32_t mt_tx_cpu_from_fce_cpu_desc_idx = 0x09a8;
constexpr std::uint32_t mt_fce_pdma_global_conf = 0x09c4;
constexpr std::uint32_t mt_fce_skip_fs = 0x0a6c;
// Firmware file header (port of struct mt76_fw_header).
struct fw_header {
std::uint32_t ilm_len; // little-endian on disk
std::uint32_t dlm_len; // little-endian on disk
std::uint16_t build_ver;
std::uint16_t fw_ver;
std::uint8_t pad[4];
char build_time[16];
} XONE_PACKED;
} // namespace xone::mt76