From c6e80857c7351fc3f39d73b6f45c7ed93402ed00 Mon Sep 17 00:00:00 2001 From: 21km43 Date: Fri, 17 Jan 2025 16:51:22 +0900 Subject: [PATCH 1/8] add functions to switch iap mode --- CHANGELOG.md | 2 ++ README.md | 1 + src/commands/mod.rs | 1 + src/main.rs | 18 ++++++++++++++ src/probe.rs | 57 ++++++++++++++++++++++++++++++++++++++++++++- src/usb_device.rs | 9 ++++--- 6 files changed, 84 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f9c7359..8891864 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/README.md b/README.md index ab6e4d7..c4c455a 100644 --- a/README.md +++ b/README.md @@ -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) diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 6fc05c1..90e5644 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -427,3 +427,4 @@ impl Command for DisableDebug { // 81 11 01 0D unknown in query info, before GetChipRomRamSplit // 81 0D 02 EE 00/02/03 SetSDLineMode // 81 0F 01 01 SetIAPMode +// 83 02 00 00 QuitIAPMode diff --git a/src/main.rs b/src/main.rs index 4cb8b4a..75443d4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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 @@ -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::enter_iap(device_index)?; + } else { + WchLink::quit_iap(device_index)?; + } + } Some(Commands::List {}) => { WchLink::list_probes()?; } diff --git a/src/probe.rs b/src/probe.rs index aa5e61e..7aa7ab6 100644 --- a/src/probe.rs +++ b/src/probe.rs @@ -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: #[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] #[repr(u8)] @@ -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); } @@ -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(()) } @@ -173,6 +185,49 @@ impl WchLink { Ok(()) } + /// Switch IAP mode + // ref: https://github.com/cjacker/wlink-iap/blob/main/src/main.c + pub fn enter_iap(nth: usize) -> Result<()> { + + // Check device mode + let vid; let pid; let endp_out; + let devs = usb_device::list_devices(VENDOR_ID, PRODUCT_ID)?; + if !devs.is_empty() { + vid = VENDOR_ID; + pid = PRODUCT_ID; + endp_out = ENDPOINT_OUT; + } else { + let devs = usb_device::list_devices(VENDOR_ID_DAP, PRODUCT_ID_DAP)?; + if !devs.is_empty() { + 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 quit_iap(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)?; diff --git a/src/usb_device.rs b/src/usb_device.rs index b83c744..dc01001 100644 --- a/src/usb_device.rs +++ b/src/usb_device.rs @@ -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)?; From 32065bc4431ffa5de6964c05bd860943f37058ad Mon Sep 17 00:00:00 2001 From: 21km43 Date: Fri, 17 Jan 2025 16:54:08 +0900 Subject: [PATCH 2/8] typo fix --- src/commands/mod.rs | 2 +- src/probe.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 90e5644..13d3aa0 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -426,5 +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 diff --git a/src/probe.rs b/src/probe.rs index 7aa7ab6..e016738 100644 --- a/src/probe.rs +++ b/src/probe.rs @@ -23,7 +23,7 @@ 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; +pub const ENDPOINT_IN_IAP: u8 = 0x02; /// All WCH-Link probe variants, see-also: #[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] From 67529b9e08c8832d5417359f0d7a2b5c24335662 Mon Sep 17 00:00:00 2001 From: Koki Mizumoto <21km43@gmail.com> Date: Fri, 17 Jan 2025 18:43:11 +0900 Subject: [PATCH 3/8] Simplified RV and DAP determination --- src/probe.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/probe.rs b/src/probe.rs index e016738..8964a17 100644 --- a/src/probe.rs +++ b/src/probe.rs @@ -191,14 +191,12 @@ impl WchLink { // Check device mode let vid; let pid; let endp_out; - let devs = usb_device::list_devices(VENDOR_ID, PRODUCT_ID)?; - if !devs.is_empty() { + if crate::usb_device::open_nth(VENDOR_ID, PRODUCT_ID, nth).is_ok() { vid = VENDOR_ID; pid = PRODUCT_ID; endp_out = ENDPOINT_OUT; } else { - let devs = usb_device::list_devices(VENDOR_ID_DAP, PRODUCT_ID_DAP)?; - if !devs.is_empty() { + 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; From db77c464b7079fb895f4185e314379174e9abb3c Mon Sep 17 00:00:00 2001 From: Koki Mizumoto <21km43@gmail.com> Date: Fri, 17 Jan 2025 21:37:05 +0900 Subject: [PATCH 4/8] function name fix --- src/main.rs | 4 ++-- src/probe.rs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main.rs b/src/main.rs index 75443d4..633242d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -219,9 +219,9 @@ fn main() -> Result<()> { if !(enter ^ quit) { println!("Please choose one mode to switch, either --enter or --quit"); } else if enter { - WchLink::enter_iap(device_index)?; + WchLink::iap_enter(device_index)?; } else { - WchLink::quit_iap(device_index)?; + WchLink::iap_quit(device_index)?; } } Some(Commands::List {}) => { diff --git a/src/probe.rs b/src/probe.rs index 8964a17..cbbb737 100644 --- a/src/probe.rs +++ b/src/probe.rs @@ -187,7 +187,7 @@ impl WchLink { /// Switch IAP mode // ref: https://github.com/cjacker/wlink-iap/blob/main/src/main.c - pub fn enter_iap(nth: usize) -> Result<()> { + pub fn iap_enter(nth: usize) -> Result<()> { // Check device mode let vid; let pid; let endp_out; @@ -206,8 +206,8 @@ impl WchLink { } let mut dev = crate::usb_device::open_nth(vid, pid, nth)?; - log::info!("Enter IAP mode"); + 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); @@ -215,10 +215,10 @@ impl WchLink { Ok(()) } - pub fn quit_iap(nth: usize) -> Result<()> { + 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"); + 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); From be0e8fd0a19efd912e9a9615981a3f4e73cd21ad Mon Sep 17 00:00:00 2001 From: 21km43 <21km43@gmail.com> Date: Sun, 19 Jan 2025 14:35:21 +0900 Subject: [PATCH 5/8] ch375dll64 test --- src/usb_device.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/usb_device.rs b/src/usb_device.rs index dc01001..2343c44 100644 --- a/src/usb_device.rs +++ b/src/usb_device.rs @@ -177,6 +177,8 @@ pub mod ch375_driver { CH375_DRIVER = Some( Library::new("WCHLinkDLL.dll") .map_err(|_| Error::Custom("WCHLinkDLL.dll not found".to_string()))?, + Library::new("CH375DLL64.dll") + .map_err(|_| Error::Custom("CH375DLL64.dll not found".to_string()))?, ); let lib = CH375_DRIVER.as_ref().unwrap(); let get_version: Symbol u32> = From beee4bad9fe44d7a2e57bc830c8cc4e33e36686b Mon Sep 17 00:00:00 2001 From: 21km43 <21km43@gmail.com> Date: Sun, 19 Jan 2025 14:37:15 +0900 Subject: [PATCH 6/8] test 2 --- src/usb_device.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/usb_device.rs b/src/usb_device.rs index 2343c44..cc7a427 100644 --- a/src/usb_device.rs +++ b/src/usb_device.rs @@ -175,8 +175,8 @@ pub mod ch375_driver { unsafe { if CH375_DRIVER.is_none() { CH375_DRIVER = Some( - Library::new("WCHLinkDLL.dll") - .map_err(|_| Error::Custom("WCHLinkDLL.dll not found".to_string()))?, + //Library::new("WCHLinkDLL.dll") + //.map_err(|_| Error::Custom("WCHLinkDLL.dll not found".to_string()))?, Library::new("CH375DLL64.dll") .map_err(|_| Error::Custom("CH375DLL64.dll not found".to_string()))?, ); From 9c17a2e73164fbbbe68ce7f77857263cad02a796 Mon Sep 17 00:00:00 2001 From: 21km43 <21km43@gmail.com> Date: Sun, 19 Jan 2025 14:40:27 +0900 Subject: [PATCH 7/8] rebase --- src/usb_device.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/usb_device.rs b/src/usb_device.rs index cc7a427..dc01001 100644 --- a/src/usb_device.rs +++ b/src/usb_device.rs @@ -175,10 +175,8 @@ pub mod ch375_driver { unsafe { if CH375_DRIVER.is_none() { CH375_DRIVER = Some( - //Library::new("WCHLinkDLL.dll") - //.map_err(|_| Error::Custom("WCHLinkDLL.dll not found".to_string()))?, - Library::new("CH375DLL64.dll") - .map_err(|_| Error::Custom("CH375DLL64.dll not found".to_string()))?, + Library::new("WCHLinkDLL.dll") + .map_err(|_| Error::Custom("WCHLinkDLL.dll not found".to_string()))?, ); let lib = CH375_DRIVER.as_ref().unwrap(); let get_version: Symbol u32> = From 75d40ee5d9a169bbaaa304007d47119979482bb5 Mon Sep 17 00:00:00 2001 From: Koki Mizumoto <21km43@gmail.com> Date: Sun, 19 Jan 2025 18:11:44 +0900 Subject: [PATCH 8/8] Support IAP mode without Zadig (x86) --- src/usb_device.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/usb_device.rs b/src/usb_device.rs index dc01001..95d73e3 100644 --- a/src/usb_device.rs +++ b/src/usb_device.rs @@ -178,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 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 u32> = { lib.get(b"CH375GetVersion").unwrap() }; let get_driver_version: Symbol u32> =