Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add commands for controling IAP mode #84

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

- Add functions for switching IAP mode

## [0.1.1] - 2024-11-15

### Fixed
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
- [x] Read/write chip register - very handy for debugging
- [x] Code-Protect & Code-Unprotect for supported chips
- [x] Enable or Disable 3.3V, 5V output
- [x] Switch IAP mode
- [x] [SDI print](https://www.cnblogs.com/liaigu/p/17628184.html) support, requires 2.10+ firmware
- [x] [Serial port watching](https://github.com/ch32-rs/wlink/pull/36) for a smooth development experience
- [x] Windows native driver support, no need to install libusb manually (requires x86 build)
Expand Down
3 changes: 2 additions & 1 deletion src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -426,4 +426,5 @@ impl Command for DisableDebug {
// 81 0D 02 08 xx ClearCodeFlash
// 81 11 01 0D unknown in query info, before GetChipRomRamSplit
// 81 0D 02 EE 00/02/03 SetSDLineMode
// 81 0F 01 01 SetIAPMode
// 81 0F 01 01 EnterIAPMode
// 83 02 00 00 QuitIAPMode
18 changes: 18 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,13 @@ enum Commands {
#[arg(long)]
dap: bool,
},
/// Enter or quit IAP mode
Iap {
#[arg(long)]
enter: bool,
#[arg(long)]
quit: bool,
},
/// List probes
List {},
/// Enable or disable power output
Expand Down Expand Up @@ -206,6 +213,17 @@ fn main() -> Result<()> {
WchLink::switch_from_dap_to_rv(device_index)?;
}
}
Some(Commands::Iap { enter, quit }) => {
WchLink::list_probes()?;
log::warn!("This is an experimental feature, better use the WCH-LinkUtility!");
if !(enter ^ quit) {
println!("Please choose one mode to switch, either --enter or --quit");
} else if enter {
WchLink::iap_enter(device_index)?;
} else {
WchLink::iap_quit(device_index)?;
}
}
Some(Commands::List {}) => {
WchLink::list_probes()?;
}
Expand Down
55 changes: 54 additions & 1 deletion src/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ pub const PRODUCT_ID_DAP: u16 = 0x8012;

pub const ENDPOINT_OUT_DAP: u8 = 0x02;

pub const VENDOR_ID_IAP: u16 = 0x4348;
pub const PRODUCT_ID_IAP: u16 = 0x55e0;

pub const ENDPOINT_OUT_IAP: u8 = 0x02;
pub const ENDPOINT_IN_IAP: u8 = 0x02;

/// All WCH-Link probe variants, see-also: <http://www.wch-ic.com/products/WCH-Link.html>
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
#[repr(u8)]
Expand Down Expand Up @@ -99,9 +105,11 @@ impl WchLink {
let device = match crate::usb_device::open_nth(VENDOR_ID, PRODUCT_ID, nth) {
Ok(dev) => dev,
Err(e) => {
// Detect if it is in DAP mode
// Detect if it is in DAP or IAP mode
if crate::usb_device::open_nth(VENDOR_ID_DAP, PRODUCT_ID_DAP, nth).is_ok() {
return Err(Error::ProbeModeNotSupported);
} else if crate::usb_device::open_nth(VENDOR_ID_IAP, PRODUCT_ID_IAP, nth).is_ok() {
return Err(Error::ProbeModeNotSupported);
} else {
return Err(e);
}
Expand Down Expand Up @@ -135,6 +143,10 @@ impl WchLink {
for dev in devs {
println!("{} (DAP mode)", dev)
}
let devs = usb_device::list_devices(VENDOR_ID_IAP, PRODUCT_ID_IAP)?;
for dev in devs {
println!("{} (IAP mode)", dev)
}
Ok(())
}

Expand Down Expand Up @@ -173,6 +185,47 @@ impl WchLink {
Ok(())
}

/// Switch IAP mode
// ref: https://github.com/cjacker/wlink-iap/blob/main/src/main.c
pub fn iap_enter(nth: usize) -> Result<()> {

// Check device mode
let vid; let pid; let endp_out;
if crate::usb_device::open_nth(VENDOR_ID, PRODUCT_ID, nth).is_ok() {
vid = VENDOR_ID;
pid = PRODUCT_ID;
endp_out = ENDPOINT_OUT;
} else {
if crate::usb_device::open_nth(VENDOR_ID_DAP, PRODUCT_ID_DAP, nth).is_ok() {
vid = VENDOR_ID_DAP;
pid = PRODUCT_ID_DAP;
endp_out = ENDPOINT_OUT_DAP;
} else {
return Err(crate::Error::ProbeNotFound);
}
}

let mut dev = crate::usb_device::open_nth(vid, pid, nth)?;

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);

Ok(())
}

pub fn iap_quit(nth: usize) -> Result<()> {
let mut dev = crate::usb_device::open_nth(VENDOR_ID_IAP, PRODUCT_ID_IAP, nth)?;

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);

Ok(())
}

pub fn set_power_output_enabled(nth: usize, cmd: commands::control::SetPower) -> Result<()> {
let mut probe = Self::open_nth(nth)?;

Expand Down
23 changes: 19 additions & 4 deletions src/usb_device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,12 @@ pub mod libusb {

log::trace!("Device: {:?}", &device);

let desc = device.device_descriptor()?;
let serial_number = handle.read_serial_number_string_ascii(&desc)?;
log::debug!("Serial number: {:?}", serial_number);
// In IAP mode, the device does not have a serial number
if !(vid == crate::probe::VENDOR_ID_IAP && pid == crate::probe::PRODUCT_ID_IAP) {
let desc = device.device_descriptor()?;
let serial_number = handle.read_serial_number_string_ascii(&desc)?;
log::debug!("Serial number: {:?}", serial_number);
}

handle.claim_interface(0)?;

Expand Down Expand Up @@ -175,7 +178,19 @@ pub mod ch375_driver {
Library::new("WCHLinkDLL.dll")
.map_err(|_| Error::Custom("WCHLinkDLL.dll not found".to_string()))?,
);
let lib = CH375_DRIVER.as_ref().unwrap();
let mut lib = CH375_DRIVER.as_ref().unwrap();

// For IAP mode, load CH375DLL.dll if USB ID is zero
let get_usb_id: Symbol<unsafe extern "stdcall" fn(u32) -> u32> =
{ lib.get(b"CH375GetUsbID").unwrap() };
if get_usb_id(0) == 0x0000_0000 {
CH375_DRIVER = Some(
Library::new("CH375DLL.dll")
.map_err(|_| Error::Custom("CH375DLL.dll not found".to_string()))?,
);
lib = CH375_DRIVER.as_ref().unwrap();
}

let get_version: Symbol<unsafe extern "stdcall" fn() -> u32> =
{ lib.get(b"CH375GetVersion").unwrap() };
let get_driver_version: Symbol<unsafe extern "stdcall" fn() -> u32> =
Expand Down
Loading