Skip to content

Add commands for firmware upgrade & control IAP mode#84

Open
21km43 wants to merge 36 commits into
ch32-rs:mainfrom
21km43:iap
Open

Add commands for firmware upgrade & control IAP mode#84
21km43 wants to merge 36 commits into
ch32-rs:mainfrom
21km43:iap

Conversation

@21km43

@21km43 21km43 commented Jan 17, 2025

Copy link
Copy Markdown
Contributor

I read this issue and implemented commands which controls IAP mode.

Commands:
  iap          Enter or quit IAP mode
  upgrade      Upgrade WCH-Link/E firmware

Example (Controling iap mode):

> wlink iap
[WARN] This is an experimental feature, better use the WCH-LinkUtility!
Please choose one mode to switch, either --enter or --quit

> wlink iap --enter
<WCH-Link#0 libusb device> Bus 001 Device 031 ID 1a86:8010(USB-FS 12 Mbps) (RV mode)
[WARN] This is an experimental feature, better use the WCH-LinkUtility!
[INFO] Enter IAP mode

> wlink iap --quit
<WCH-Link#0 libusb device> Bus 001 Device 026 ID 4348:55e0(USB-FS 12 Mbps) (IAP mode)
[WARN] This is an experimental feature, better use the WCH-LinkUtility!
[INFO] Quit IAP mode

> wlink mode-switch --dap
<WCH-Link#0 libusb device> Bus 001 Device 033 ID 1a86:8010(USB-FS 12 Mbps) (RV mode)
[WARN] This is an experimental feature, better use the WCH-LinkUtility!
[INFO] Connected to WCH-Link v2.14(v34) (WCH-LinkE-CH32V305)
[INFO] Switch mode for WCH-LinkRV

> wlink iap --enter
<WCH-Link#0 libusb device> Bus 001 Device 028 ID 1a86:8012(USB-FS 12 Mbps) (DAP mode)
[WARN] This is an experimental feature, better use the WCH-LinkUtility!
[INFO] Enter IAP mode

> wlink iap --quit
<WCH-Link#0 libusb device> Bus 001 Device 026 ID 4348:55e0(USB-FS 12 Mbps) (IAP mode)
[WARN] This is an experimental feature, better use the WCH-LinkUtility!
[INFO] Quit IAP mode

Example (Upgrade firmware)

> wlink upgrade FIRMWARE_CH32V305.bin
<WCH-Link#0 WCHLinkDLL device> CH375Driver Device 1a86:8010 (RV mode)
<WCH-Link#0 libusb device> Bus 001 Device 057 ID 1a86:8010(USB-FS 12 Mbps) (RV mode)
[WARN] This is an experimental feature, better use the WCH-LinkUtility!
[INFO] Read FIRMWARE_CH32V305.bin as Binary format
[INFO] Enter IAP mode
[INFO] Start firmware upgrade
[INFO] ######################################
[INFO] DO NOT UNPLUG WCH-Link/E until done!!!
[INFO] ######################################
[INFO] Erase flash
[INFO] Flashing 94912 bytes
[INFO] Flash done
[INFO] Verifying
[INFO] Verify OK
[INFO] Firmware upgrade completed successfully
[INFO] Quit IAP mode

# If the device is already IAP mode, skip entering IAP mode and start upgrade
> wlink upgrade FIRMWARE_CH32V305.bin
<WCH-Link#0 WCHLinkDLL device> CH375Driver Device 1a86:8010 (RV mode)
<WCH-Link#0 libusb device> Bus 001 Device 057 ID 1a86:8010(USB-FS 12 Mbps) (RV mode)
[WARN] This is an experimental feature, better use the WCH-LinkUtility!
[INFO] Read FIRMWARE_CH32V305.bin as Binary format
[INFO] Device is already in IAP mode
[INFO] Start firmware upgrade
[INFO] ######################################
[INFO] DO NOT UNPLUG WCH-Link/E until done!!!
[INFO] ######################################
[INFO] Erase flash
[INFO] Flashing 94912 bytes
[INFO] Flash done
[INFO] Verifying
[INFO] Verify OK
[INFO] Firmware upgrade completed successfully
[INFO] Quit IAP mode

@21km43
21km43 marked this pull request as draft January 19, 2025 08:30
@21km43
21km43 marked this pull request as ready for review January 19, 2025 09:15
@andelf

andelf commented Dec 8, 2025

Copy link
Copy Markdown
Contributor

It would be great to add firmware upgrade support.
Are you planning to implement this?

@21km43

21km43 commented Dec 8, 2025

Copy link
Copy Markdown
Contributor Author

It would be great to add firmware upgrade support. Are you planning to implement this?

Yes. I'll implement firmware upgrade option later.

Should I integrate IAP control function and firmware upgrade function?

@21km43
21km43 marked this pull request as draft December 8, 2025 04:07
@andelf

andelf commented Dec 8, 2025

Copy link
Copy Markdown
Contributor

Should I integrate IAP control function and firmware upgrade function?

Yes, please integrate them together.

Switching IAP mode separately would leave wch-link in an intermediate state,
which isn't ideal. The official PC software treats "IAP switch + firmware upgrade"
as a single operation, so we should follow that pattern for consistency and reliability.

21km43 and others added 4 commits December 8, 2025 16:31
* initial checkout

* implement iap_flash_firmware

* fix fileend copy

* fix read endpoint

* fix loginfo

* fix sleep time

* add log detail info

* Remove redundant notation

* fix write verify commands to enum

* add readme

* add warning
@21km43 21km43 changed the title Add commands for controling IAP mode Add commands for firmware upgrade & control IAP mode Dec 8, 2025
@21km43
21km43 marked this pull request as ready for review December 8, 2025 07:44
21km43 and others added 9 commits December 8, 2025 16:46
…ices (#6)

* Initial plan

* Refactor library selection to support dynamic DLL loading for IAP and normal modes

Co-authored-by: 21km43 <48169975+21km43@users.noreply.github.com>

* Don't refresh device_index

* Remove dll fallback

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: 21km43 <48169975+21km43@users.noreply.github.com>
Co-authored-by: Koki Mizumoto <21km43@gmail.com>
Removed unreleased changes for IAP mode switch and WCH-LinkE firmware upgrade.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds experimental firmware upgrade capabilities and IAP (In-Application Programming) mode control for WCH-Link/E devices. The implementation draws from community references and adds two new CLI commands: iap for entering/exiting IAP mode, and upgrade for flashing WCH-Link firmware.

Changes:

  • Added IAP mode control commands (enter/quit) and firmware upgrade functionality
  • Enhanced CH375 driver support to handle both WCHLinkDLL.dll and CH375DLL.dll for IAP mode devices on Windows
  • Added new USB constants and endpoints for IAP mode device communication

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 13 comments.

Show a summary per file
File Description
src/main.rs Added CLI commands for IAP mode switching and firmware upgrade with appropriate warnings
src/probe.rs Implemented IAP mode functions (enter, quit, erase, flash) with USB communication logic
src/usb_device.rs Enhanced CH375 driver to support dual DLL loading and IAP mode device handling
src/commands/mod.rs Added IapProgram enum for write/verify operations and updated command documentation
README.md Updated feature list to include IAP mode switching and firmware upgrade capabilities

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/probe.rs
txbuf.fill(0);

// First 4 bytes: cmd, size, addr low, addr high
txbuf[0] = cmd;

Copilot AI Jan 18, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The copy_size is cast to u8 which limits it to a maximum value of 255. However, the code initializes copy_size to 60 and only reduces it when near the end of the data. If the protocol is ever changed to support larger chunk sizes, this cast could silently truncate the value. While not currently a bug, consider adding an assertion or debug check to ensure copy_size never exceeds 255.

Suggested change
txbuf[0] = cmd;
txbuf[0] = cmd;
debug_assert!(
copy_size <= u8::MAX as usize,
"copy_size ({}) exceeds u8::MAX ({})",
copy_size,
u8::MAX
);

Copilot uses AI. Check for mistakes.
Comment thread src/probe.rs
Ok(())
}

/// Switch IAP mode

Copilot AI Jan 18, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment says "Switch IAP mode" but the function name is "iap_enter". The comment should be more specific, such as "Enter IAP mode" to match the function's purpose.

Suggested change
/// Switch IAP mode
/// Enter IAP mode

Copilot uses AI. Check for mistakes.
Comment thread src/probe.rs
/// Switch IAP mode
// ref: https://github.com/cjacker/wlink-iap/blob/main/src/main.c
pub fn iap_enter(nth: usize) -> Result<()> {

Copilot AI Jan 18, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's an unnecessary blank line at the start of the function body. Remove it for consistency with the codebase style.

Suggested change

Copilot uses AI. Check for mistakes.
Comment thread src/probe.rs
log::info!("Quit IAP mode");
let buf = [0x83, 0x02, 0x00, 0x00];
log::trace!("send {} {}", hex::encode(&buf[..3]), hex::encode(&buf[3..]));
let _ = dev.write_endpoint(ENDPOINT_OUT_IAP, &buf);

Copilot AI Jan 18, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The write_endpoint result is being ignored when sending the IAP quit command. If the write fails, the function will return Ok despite the mode switch command not being sent, potentially leaving the device in IAP mode. Consider propagating the error instead of silently ignoring it.

Suggested change
let _ = dev.write_endpoint(ENDPOINT_OUT_IAP, &buf);
dev.write_endpoint(ENDPOINT_OUT_IAP, &buf)?;

Copilot uses AI. Check for mistakes.
Comment thread src/probe.rs
} else {
return Err(Error::ProbeNotFound);
};

Copilot AI Jan 18, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's trailing whitespace at the end of this line. Remove it for consistency with the codebase style.

Suggested change

Copilot uses AI. Check for mistakes.
Comment thread src/main.rs
WchLink::iap_quit(device_index)?;
}
Firmware::Sections(_sections) => {
log::error!("HEX firmware format not supported for WCH-Link upgrade");

Copilot AI Jan 18, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the firmware format is HEX (Sections), the function only logs an error but doesn't return an error. This means the upgrade process will continue silently and quit IAP mode without actually upgrading the firmware, potentially leaving the device in an inconsistent state. The function should return an error to prevent the code from continuing to line 282 (quit IAP mode).

Suggested change
log::error!("HEX firmware format not supported for WCH-Link upgrade");
log::error!("HEX firmware format not supported for WCH-Link upgrade");
return Err(
wlink::Error::Custom(
"HEX firmware format not supported for WCH-Link upgrade".into(),
)
.into(),
);

Copilot uses AI. Check for mistakes.
Comment thread src/probe.rs
.copy_from_slice(&data[offset..offset + copy_size]);

// Write or verify binary
let _ = dev.write_endpoint(ENDPOINT_OUT_IAP, &txbuf[0..4 + copy_size]);

Copilot AI Jan 18, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The write_endpoint result is being ignored, which could hide critical errors during the IAP flash/verify operation. Since the subsequent read_endpoint is expected to return a response confirming the write was successful, a failure to write could lead to misleading error messages. The write_endpoint call should propagate errors.

Suggested change
let _ = dev.write_endpoint(ENDPOINT_OUT_IAP, &txbuf[0..4 + copy_size]);
dev.write_endpoint(ENDPOINT_OUT_IAP, &txbuf[0..4 + copy_size])?;

Copilot uses AI. Check for mistakes.
Comment thread src/probe.rs
log::info!("Enter IAP mode");
let buf = [0x81, 0x0f, 0x01, 0x01];
log::trace!("send {} {}", hex::encode(&buf[..3]), hex::encode(&buf[3..]));
let _ = dev.write_endpoint(endp_out, &buf);

Copilot AI Jan 18, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The write_endpoint result is being ignored when sending the IAP enter command. If the write fails but the device is not already in IAP mode, the function will return Ok despite the mode switch command not being sent. Consider propagating the error instead of silently ignoring it.

Suggested change
let _ = dev.write_endpoint(endp_out, &buf);
if let Err(e) = dev.write_endpoint(endp_out, &buf) {
log::error!("Failed to send IAP enter command: {:?}", e);
return Err(e);
}

Copilot uses AI. Check for mistakes.
Comment thread src/probe.rs
log::info!("Erase flash");
let buf = [0x81, 0x02, 0x00, 0x00];
log::trace!("send {} {}", hex::encode(&buf[..3]), hex::encode(&buf[3..]));
let _ = dev.write_endpoint(ENDPOINT_OUT_IAP, &buf);

Copilot AI Jan 18, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The write_endpoint result is being ignored when sending the IAP erase command. If the write fails, the function will return Ok despite the erase command not being sent, potentially causing issues in the firmware upgrade process that expects the flash to be erased. Consider propagating the error instead of silently ignoring it.

Copilot uses AI. Check for mistakes.
Comment thread src/probe.rs
Comment on lines +255 to +259
// First 4 bytes: cmd, size, addr low, addr high
txbuf[0] = cmd;
txbuf[1] = copy_size as u8;
txbuf[2] = (offset & 0xFF) as u8;
txbuf[3] = ((offset >> 8) & 0xFF) as u8;

Copilot AI Jan 18, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The offset is encoded as a 16-bit value (using only 2 bytes at positions 2 and 3), which limits the maximum addressable offset to 65535 bytes. However, the function accepts data of any length without validating this constraint. If data.len() exceeds 65535 bytes, the offset will silently overflow and wrap around, causing firmware to be written to incorrect addresses. Add validation to ensure data.len() does not exceed 65535 bytes, or use a larger address field if the protocol supports it.

Copilot uses AI. Check for mistakes.
@andelf

andelf commented Jan 18, 2026

Copy link
Copy Markdown
Contributor

Leave the rebase work to me. I'll resolve them later.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants