diff --git a/Cargo.lock b/Cargo.lock index f505c51..9e713ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "anstream" @@ -280,6 +280,16 @@ version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +[[package]] +name = "libloading" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +dependencies = [ + "cfg-if", + "windows-targets", +] + [[package]] name = "libusb1-sys" version = "0.7.0" @@ -380,9 +390,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.83" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" dependencies = [ "unicode-ident", ] @@ -523,9 +533,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.65" +version = "2.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" dependencies = [ "proc-macro2", "quote", @@ -650,6 +660,7 @@ dependencies = [ "hxdmp", "ihex", "indicatif", + "libloading", "log", "object", "rand", diff --git a/Cargo.toml b/Cargo.toml index 5284189..adcc128 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,3 +39,4 @@ object = { version = "0.36.0", default-features = false, features = [ ] } indicatif = "0.17" serialport = { version = "4.5", default-features = false } +libloading = "0.8.1" diff --git a/README.md b/README.md index 908fd64..7361399 100644 --- a/README.md +++ b/README.md @@ -38,10 +38,9 @@ Click the newest runs at [Github Actions Page](https://github.com/ch32-rs/wchisp ### Note for Windows -If you are using Windows, you need to install the WinUSB driver for your device. -See [Zadig](https://zadig.akeo.ie/). +If you are using Windows, you may need to install the CH375DLL64.dll if you do not have it in your system. If you encounter a "CH375DLL64.dll not found" error, please download it from the WCH official website and put the dll next to this executable. You may download it from https://www.wch-ic.com/downloads/CH372DRV_ZIP.html, or search for 'CH375' on WCH websites if the link is broken. When you use the official WCH driver you installed with IDE or WCHISPTOOL, CH375DLL64.dll allows you to program the target chips without changing the driver. -NOTE: This is not compatible with the Official WCH driver you installed with IDE. +It is also OK to use the WinUSB driver with [Zadig](https://zadig.akeo.ie/). ### Note for Linux diff --git a/devices/0x13-CH57x.yaml b/devices/0x13-CH57x.yaml index 4a0aae6..50b5db1 100644 --- a/devices/0x13-CH57x.yaml +++ b/devices/0x13-CH57x.yaml @@ -39,6 +39,7 @@ config_registers_ch571_ch573: &config_registers_ch571_ch573 # CFG_DEBUG_EN=1、CFG_RESET_EN=0、CFG_ROM_READ=0 # enable 2-wire debug reset: 0x4FFF0F55 + disable_debug: 0x4FFF0F45 type: u32 fields: - bit_range: [2, 0] diff --git a/src/device.rs b/src/device.rs index f33c369..066cea9 100644 --- a/src/device.rs +++ b/src/device.rs @@ -96,6 +96,7 @@ pub struct ConfigRegister { pub description: String, pub reset: Option, pub enable_debug: Option, + pub disable_debug: Option, #[serde(default)] pub explaination: BTreeMap, #[serde(default)] diff --git a/src/flashing.rs b/src/flashing.rs index e572014..e3e2cdf 100644 --- a/src/flashing.rs +++ b/src/flashing.rs @@ -343,6 +343,40 @@ impl<'a> Flashing<'a> { Ok(()) } + pub fn disable_debug(&mut self) -> Result<()> { + let read_conf = Command::read_config(CFG_MASK_RDPR_USER_DATA_WPR); + let resp = self.transport.transfer(read_conf)?; + anyhow::ensure!(resp.is_ok(), "read_config failed"); + + let mut raw = resp.payload()[2..].to_vec(); + + log::info!("Current config registers: {}", hex::encode(&raw)); + + for reg_desc in &self.chip.config_registers { + if let Some(reset) = reg_desc.reset { + raw.pwrite_with(reset, reg_desc.offset, scroll::LE)?; + } + if let Some(disable_debug) = reg_desc.disable_debug { + raw.pwrite_with(disable_debug, reg_desc.offset, scroll::LE)?; + } + } + + log::info!( + "Reset config registers to debug disabled: {}", + hex::encode(&raw) + ); + let write_conf = Command::write_config(CFG_MASK_RDPR_USER_DATA_WPR, raw); + let resp = self.transport.transfer(write_conf)?; + anyhow::ensure!(resp.is_ok(), "write_config failed"); + + // read back + let read_conf = Command::read_config(CFG_MASK_RDPR_USER_DATA_WPR); + let resp = self.transport.transfer(read_conf)?; + anyhow::ensure!(resp.is_ok(), "read_config failed"); + + Ok(()) + } + /// Dump EEPROM, i.e. data flash. pub fn dump_eeprom(&mut self) -> Result> { const CHUNK: usize = 0x3a; diff --git a/src/main.rs b/src/main.rs index 22717f0..f7fb6ce 100644 --- a/src/main.rs +++ b/src/main.rs @@ -39,6 +39,10 @@ struct Cli { #[arg(long, short, ignore_case = true, value_enum, requires = "serial")] baudrate: Option, + /// Retry scan for certain seconds, helpful on slow USB devices + #[arg(long, short, default_value = "0")] + retry: u32, + #[command(subcommand)] command: Option, } @@ -93,6 +97,8 @@ enum ConfigCommands { Reset {}, /// Enable SWD mode(simulation mode) EnableDebug {}, + /// Disable SWD mode(simulation mode) + DisableDebug {}, /// Set config register to new value Set { /// New value of the config register @@ -141,6 +147,25 @@ fn main() -> Result<()> { ); } + if cli.retry > 0 { + log::info!("Retrying scan for {} seconds", cli.retry); + let start_time = std::time::Instant::now(); + while start_time.elapsed().as_secs() < cli.retry as u64 { + if cli.usb { + let ndevices = UsbTransport::scan_devices()?; + if ndevices > 0 { + break; + } + } else if cli.serial { + let ports = SerialTransport::scan_ports()?; + if !ports.is_empty() { + break; + } + } + sleep(Duration::from_millis(100)); + } + } + match &cli.command { None | Some(Commands::Probe {}) => { if cli.usb { @@ -324,6 +349,10 @@ fn main() -> Result<()> { flashing.enable_debug()?; log::info!("Debug mode enabled"); } + Some(ConfigCommands::DisableDebug {}) => { + flashing.disable_debug()?; + log::info!("Debug mode disabled"); + } Some(ConfigCommands::Set { value }) => { // flashing.write_config(value)?; log::info!("setting cfg value {}", value); diff --git a/src/transport/usb.rs b/src/transport/usb.rs index 706da36..0a34c29 100644 --- a/src/transport/usb.rs +++ b/src/transport/usb.rs @@ -11,12 +11,26 @@ const ENDPOINT_IN: u8 = 0x82; const USB_TIMEOUT_MS: u64 = 5000; + pub struct UsbTransport { - device_handle: DeviceHandle, + device_handle: Option>, + #[cfg(all(target_os = "windows", target_arch = "x86_64"))] + ch375_index: isize, } impl UsbTransport { pub fn scan_devices() -> Result { + #[cfg(all(target_os = "windows", target_arch = "x86_64"))] + { + let devices_ch375 = ch375_driver::list_devices()?; + let devices_ch375_count = devices_ch375.len(); + if devices_ch375_count>0{ + //just return the count + log::debug!("Found {} CH375USBDevice", devices_ch375_count); + return Ok(devices_ch375_count); + } + } + let context = Context::new()?; let n = context @@ -42,6 +56,17 @@ impl UsbTransport { pub fn open_nth(nth: usize) -> Result { log::info!("Opening USB device #{}", nth); + #[cfg(all(target_os = "windows", target_arch = "x86_64"))] + { + let ch375_index = ch375_driver::open_nth(nth)?; + if ch375_index >= 0 { + return Ok(UsbTransport { + device_handle: None, + ch375_index, + }); + } + } + let context = Context::new()?; let device = context @@ -110,7 +135,11 @@ impl UsbTransport { device_handle.claim_interface(0)?; - Ok(UsbTransport { device_handle }) + Ok(UsbTransport { + device_handle: Some(device_handle), + #[cfg(all(target_os = "windows", target_arch = "x86_64"))] + ch375_index: -1, + }) } pub fn open_any() -> Result { @@ -121,23 +150,248 @@ impl UsbTransport { impl Drop for UsbTransport { fn drop(&mut self) { // ignore any communication error - let _ = self.device_handle.release_interface(0); + if let Some(ref mut handle) = self.device_handle { + let _ = handle.release_interface(0); + }else{ + #[cfg(all(target_os = "windows", target_arch = "x86_64"))]{ + if self.ch375_index >= 0 { + ch375_driver::drop(self.ch375_index as usize); + } + } + } // self.device_handle.reset().unwrap(); } } impl Transport for UsbTransport { fn send_raw(&mut self, raw: &[u8]) -> Result<()> { - self.device_handle - .write_bulk(ENDPOINT_OUT, raw, Duration::from_millis(USB_TIMEOUT_MS))?; + if let Some(ref mut handle) = self.device_handle { + handle.write_bulk(ENDPOINT_OUT, raw, Duration::from_millis(USB_TIMEOUT_MS))?; + } else { + #[cfg(all(target_os = "windows", target_arch = "x86_64"))]{ + if self.ch375_index >= 0 { + //log::debug!("CH375USBDevice index {} send_raw {:?}", self.ch375_index, raw); + ch375_driver::write_raw(self.ch375_index as usize, raw)?; + return Ok(()); + } + } + anyhow::bail!("USB device handle is None while ch375_index is negative or not set"); + } Ok(()) } fn recv_raw(&mut self, timeout: Duration) -> Result> { let mut buf = [0u8; 64]; - let nread = self - .device_handle - .read_bulk(ENDPOINT_IN, &mut buf, timeout)?; - Ok(buf[..nread].to_vec()) + if let Some(ref mut handle) = self.device_handle { + let nread = handle.read_bulk(ENDPOINT_IN, &mut buf, timeout)?; + return Ok(buf[..nread].to_vec()); + } else { + #[cfg(all(target_os = "windows", target_arch = "x86_64"))]{ + if self.ch375_index >= 0 { + let len = ch375_driver::read_raw(self.ch375_index as usize, &mut buf)?; + // log::debug!("CH375USBDevice index {} , len {} recv_raw {:?}", self.ch375_index, len, buf); + return Ok(buf[..len].to_vec()); + } + } + } + anyhow::bail!("USB device handle is None while ch375_index is negative or not set"); + } +} + +#[cfg(all(target_os = "windows", target_arch = "x86_64"))] +pub mod ch375_driver { + use libloading::os::windows::*; + + use super::*; + + static mut CH375_DRIVER: Option = None; + + fn ensure_library_load() -> Result<&'static Library> { + unsafe { + if CH375_DRIVER.is_none() { + CH375_DRIVER = Some( + Library::new("CH375DLL64.dll") + .map_err(|_| { + anyhow::Error::msg( + "CH375DLL64.dll not found. \ + Please download it from the WCH official website \ + and put the dll next to this executable. \ + You may download it from https://www.wch-ic.com/downloads/CH372DRV_ZIP.html, \ + or search for 'CH375' in the WCH websites if the link is broken." + ) + })?, + ); + let lib = CH375_DRIVER.as_ref().unwrap(); + let get_version: Symbol u32> = + { lib.get(b"CH375GetVersion").unwrap() }; + let get_driver_version: Symbol u32> = + { lib.get(b"CH375GetDrvVersion").unwrap() }; + + log::debug!( + "DLL version {}, driver version {}", + get_version(), + get_driver_version() + ); + Ok(lib) + } else { + Ok(CH375_DRIVER.as_ref().unwrap()) + } + } + } + + #[allow(non_snake_case, unused)] + #[derive(Debug)] + #[repr(packed)] + pub struct UsbDeviceDescriptor { + bLength: u8, + bDescriptorType: u8, + bcdUSB: u16, + bDeviceClass: u8, + bDeviceSubClass: u8, + bDeviceProtocol: u8, + bMaxPacketSize0: u8, + idVendor: u16, + idProduct: u16, + bcdDevice: u16, + iManufacturer: u8, + iProduct: u8, + iSerialNumber: u8, + bNumConfigurations: u8, + } + + pub fn list_devices() -> Result> { + let lib = ensure_library_load()?; + let mut ret: Vec = vec![]; + + let open_device: Symbol u32> = + unsafe { lib.get(b"CH375OpenDevice").unwrap() }; + let close_device: Symbol = + unsafe { lib.get(b"CH375CloseDevice").unwrap() }; + let get_device_descriptor: Symbol< + unsafe extern "stdcall" fn(u32, *mut UsbDeviceDescriptor, *mut u32) -> bool, + > = unsafe { lib.get(b"CH375GetDeviceDescr").unwrap() }; + + const INVALID_HANDLE: u32 = 0xffffffff; + + for i in 0..8 { + let h = unsafe { open_device(i) }; + if h != INVALID_HANDLE { + let mut descr = unsafe { core::mem::zeroed() }; + let mut len = core::mem::size_of::() as u32; + let _ = unsafe { get_device_descriptor(i, &mut descr, &mut len) }; + + let id_vendor = descr.idVendor; + let id_product = descr.idProduct; + + if (id_vendor == 0x4348 || id_vendor == 0x1a86) && id_product == 0x55e0 { + ret.push(format!( + " CH375Driver Device {:04x}:{:04x}", + i, id_vendor, id_product + )); + + log::debug!("Device #{}: {:04x}:{:04x}", i, id_vendor, id_product); + } + unsafe { close_device(i) }; + } + } + + Ok(ret) + } + + pub fn open_nth(nth: usize) -> Result { + let lib = ensure_library_load()?; + let open_device: Symbol u32> = + unsafe { lib.get(b"CH375OpenDevice").unwrap() }; + let close_device: Symbol = + unsafe { lib.get(b"CH375CloseDevice").unwrap() }; + let get_device_descriptor: Symbol< + unsafe extern "stdcall" fn(u32, *mut UsbDeviceDescriptor, *mut u32) -> bool, + > = unsafe { lib.get(b"CH375GetDeviceDescr").unwrap() }; + + const INVALID_HANDLE: u32 = 0xffffffff; + + let mut idx = 0; + for i in 0..8 { + let h = unsafe { open_device(i) }; + if h != INVALID_HANDLE { + let mut descr = unsafe { core::mem::zeroed() }; + let mut len = core::mem::size_of::() as u32; + let _ = unsafe { get_device_descriptor(i, &mut descr, &mut len) }; + + let id_vendor = descr.idVendor; + let id_product = descr.idProduct; + + if (id_vendor == 0x4348 || id_vendor == 0x1a86) + && id_product == 0x55e0 + { + if idx == nth { + log::debug!("Device #{}: {:04x}:{:04x}", i, id_vendor, id_product); + return Ok(i as isize); + } else { + idx += 1; + } + } + unsafe { close_device(i) }; + } + } + + return Ok(-1 as isize); + } + + pub fn write_raw(nth: usize, buf: &[u8]) -> Result<()> { + let lib = ensure_library_load()?; + let write_data: Symbol< + unsafe extern "stdcall" fn(u32, *mut u8, *mut u32) -> bool, + > = unsafe { lib.get(b"CH375WriteData").unwrap() }; + + let mut len = buf.len() as u32; + let ret = unsafe { + write_data(nth as u32, buf.as_ptr() as *mut u8, &mut len) + }; + if ret { + Ok(()) + } else { + Err(anyhow::anyhow!("Failed to write data with CH375USBDevice")) + } + } + + pub fn read_raw(nth: usize, buf: &mut [u8]) -> Result { + let lib = ensure_library_load()?; + let read_data: Symbol< + unsafe extern "stdcall" fn(u32, *mut u8, *mut u32) -> bool, + > = unsafe { lib.get(b"CH375ReadData").unwrap() }; + + let mut len = buf.len() as u32; + let ret = unsafe { + read_data(nth as u32, buf.as_mut_ptr(), &mut len) + }; + if ret { + Ok(len as usize) + } else { + Err(anyhow::anyhow!("Failed to read data with CH375USBDevice")) + } + } + + pub fn set_timeout(nth: usize, timeout: Duration) { + let lib = ensure_library_load().unwrap(); + + let set_timeout_ex: Symbol< + unsafe extern "stdcall" fn(u32, u32, u32, u32, u32) -> bool, + > = unsafe { lib.get(b"CH375SetTimeoutEx").unwrap() }; + + let ds = timeout.as_millis() as u32; + + unsafe { + set_timeout_ex(nth as u32, ds, ds, ds, ds); + } + } + + pub fn drop(nth: usize) { + let lib = ensure_library_load().unwrap(); + let close_device: Symbol = + unsafe { lib.get(b"CH375CloseDevice").unwrap() }; + unsafe { + close_device(nth as u32); + } } }