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
+12 -3
View File
@@ -87,7 +87,8 @@ struct transport::state {
// struct, and methods are called as (*ref)->Method(ref, ...).
io_service_t service = 0;
IOCFPlugInInterface **dev_iodev = nullptr;
IOUSBDeviceInterface197 **dev_ref = nullptr;
// Version 500 interface: adds ResetDevice (port of usb_reset_device).
IOUSBDeviceInterface500 **dev_ref = nullptr;
bool dev_opened = false;
struct iface_conn {
@@ -232,12 +233,12 @@ auto transport::open(frame_callback frames, disconnect_callback disconnected) ->
void *slot = nullptr;
HRESULT hr = (*state_->dev_iodev)->QueryInterface(state_->dev_iodev,
CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID197), &slot);
CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID500), &slot);
if (hr != S_OK || !slot) {
xone::log_msg(log_level::error, "usb: query device interface failed");
return false;
}
state_->dev_ref = static_cast<IOUSBDeviceInterface197 **>(slot);
state_->dev_ref = static_cast<IOUSBDeviceInterface500 **>(slot);
kr = (*state_->dev_ref)->USBDeviceOpen(state_->dev_ref);
if (kr != kIOReturnSuccess) {
@@ -246,6 +247,14 @@ auto transport::open(frame_callback frames, disconnect_callback disconnected) ->
}
state_->dev_opened = true;
// Reset the chip so it starts from the boot ROM (port of the
// usb_reset_device call in xone_dongle_probe).
kr = (*state_->dev_ref)->ResetDevice(state_->dev_ref);
if (kr != kIOReturnSuccess) {
xone::log_msg(log_level::error, "usb: reset device failed (%d)", kr);
return false;
}
// Open every interface and collect the endpoint pipes we need.
io_iterator_t children = 0;
kr = IORegistryEntryGetChildIterator(state_->service, kIOServicePlane, &children);