diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9898f5a..05f2352 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,8 +33,6 @@ jobs: arch: linux-x64 - os: macos-latest arch: macos-arm64 - - os: macos-13 - arch: macos-x64 runs-on: ${{ matrix.config.os }} steps: diff --git a/Cargo.lock b/Cargo.lock index 1bebb10..ecee86b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -211,9 +211,9 @@ dependencies = [ [[package]] name = "core-foundation" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" dependencies = [ "core-foundation-sys", "libc", @@ -240,12 +240,28 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + [[package]] name = "find-msvc-tools" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + [[package]] name = "heck" version = "0.5.0" @@ -370,16 +386,16 @@ dependencies = [ ] [[package]] -name = "libusb1-sys" -version = "0.7.0" +name = "linux-raw-sys" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da050ade7ac4ff1ba5379af847a10a10a8e284181e060105bf8d86960ce9ce0f" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "log" @@ -455,6 +471,24 @@ dependencies = [ "libc", ] +[[package]] +name = "nusb" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0226f4db3ee78f820747cf713767722877f6449d7a0fcfbf2ec3b840969763f" +dependencies = [ + "core-foundation", + "core-foundation-sys", + "futures-core", + "io-kit-sys", + "linux-raw-sys 0.9.4", + "log", + "once_cell", + "rustix", + "slab", + "windows-sys 0.60.2", +] + [[package]] name = "object" version = "0.38.0" @@ -513,13 +547,16 @@ dependencies = [ ] [[package]] -name = "rusb" -version = "0.9.4" +name = "rustix" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab9f9ff05b63a786553a4c02943b74b34a988448671001e9a27e2f0565cc05a4" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ + "bitflags 2.10.0", + "errno", "libc", - "libusb1-sys", + "linux-raw-sys 0.11.0", + "windows-sys 0.61.2", ] [[package]] @@ -565,9 +602,9 @@ dependencies = [ [[package]] name = "serialport" -version = "4.8.1" +version = "4.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21f60a586160667241d7702c420fc223939fb3c0bb8d3fac84f78768e8970dee" +checksum = "2acaf3f973e8616d7ceac415f53fc60e190b2a686fbcf8d27d0256c741c5007b" dependencies = [ "bitflags 2.10.0", "cfg-if", @@ -577,10 +614,9 @@ dependencies = [ "libudev", "mach2", "nix", - "quote", "scopeguard", "unescaper", - "windows-sys 0.52.0", + "winapi", ] [[package]] @@ -600,6 +636,12 @@ dependencies = [ "time", ] +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + [[package]] name = "strsim" version = "0.11.1" @@ -712,12 +754,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - [[package]] name = "wasm-bindgen" version = "0.2.106" @@ -773,6 +809,22 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + [[package]] name = "winapi-util" version = "0.1.11" @@ -782,6 +834,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-core" version = "0.62.2" @@ -843,9 +901,9 @@ dependencies = [ [[package]] name = "windows-sys" -version = "0.52.0" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ "windows-targets", ] @@ -861,10 +919,11 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.6" +version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ + "windows-link", "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", @@ -877,51 +936,51 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.6" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" [[package]] name = "windows_aarch64_msvc" -version = "0.52.6" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" [[package]] name = "windows_i686_gnu" -version = "0.52.6" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" [[package]] name = "windows_i686_gnullvm" -version = "0.52.6" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" [[package]] name = "windows_i686_msvc" -version = "0.52.6" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" [[package]] name = "windows_x86_64_gnu" -version = "0.52.6" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.6" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" [[package]] name = "windows_x86_64_msvc" -version = "0.52.6" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "wlink" @@ -938,8 +997,8 @@ dependencies = [ "libloading", "log", "nu-pretty-hex", + "nusb", "object", - "rusb", "serialport", "simplelog", "thiserror", diff --git a/Cargo.toml b/Cargo.toml index 5a3c9fa..fb6da88 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ hex = "0.4.3" ihex = "3.0.0" log = "0.4" nu-pretty-hex = "0.109" -rusb = "0.9.1" +nusb = "0.2" simplelog = "0.12.0" thiserror = "2" object = { version = "0.38", default-features = false, features = [ @@ -32,7 +32,7 @@ object = { version = "0.38", default-features = false, features = [ "std", ] } indicatif = "0.18" -serialport = "4.6" +serialport = "4.7" libloading = "0.9" chrono = "0.4" clap-verbosity-flag = "3" diff --git a/src/commands/control.rs b/src/commands/control.rs index 0b8eabc..d2987ed 100644 --- a/src/commands/control.rs +++ b/src/commands/control.rs @@ -1,6 +1,6 @@ //! Probe control commands. COMMAND_ID = 0x0d -use crate::{probe::WchLinkVariant, RiscvChip}; +use crate::{RiscvChip, probe::WchLinkVariant}; use super::*; diff --git a/src/error.rs b/src/error.rs index ce505cc..fd0216b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -10,7 +10,7 @@ pub enum Error { #[error("{0}")] Custom(String), #[error("USB error: {0}")] - Rusb(#[from] rusb::Error), + Usb(nusb::Error), #[error("WCH-Link not found, please check your connection")] ProbeNotFound, #[error("WCH-Link is connected, but is not in RV mode")] @@ -21,7 +21,9 @@ pub enum Error { UnknownLinkVariant(u8), #[error("Unknown RISC-V Chip: 0x{0:02x}")] UnknownChip(u8), - #[error("Probe is not attached to an MCU, or debug is not enabled. (hint: use wchisp to enable debug)")] + #[error( + "Probe is not attached to an MCU, or debug is not enabled. (hint: use wchisp to enable debug)" + )] NotAttached, #[error("Chip mismatch: expected {0:?}, got {1:?}")] ChipMismatch(RiscvChip, RiscvChip), diff --git a/src/firmware.rs b/src/firmware.rs index e489403..b34c306 100644 --- a/src/firmware.rs +++ b/src/firmware.rs @@ -4,8 +4,8 @@ use std::str; use anyhow::Result; use object::{ - elf::FileHeader32, elf::PT_LOAD, read::elf::FileHeader, read::elf::ProgramHeader, Endianness, - Object, ObjectSection, + Endianness, Object, ObjectSection, elf::FileHeader32, elf::PT_LOAD, read::elf::FileHeader, + read::elf::ProgramHeader, }; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -206,11 +206,11 @@ pub fn read_elf(elf_data: &[u8]) -> Result { .map_err(|_| anyhow::format_err!("Failed to access data for an ELF segment."))?; if !segment_data.is_empty() && segment.p_type(endian) == PT_LOAD { log::debug!( - "Found loadable segment, physical address: {:#010x}, virtual address: {:#010x}, flags: {:#x}", - p_paddr, - p_vaddr, - flags - ); + "Found loadable segment, physical address: {:#010x}, virtual address: {:#010x}, flags: {:#x}", + p_paddr, + p_vaddr, + flags + ); let (segment_offset, segment_filesize) = segment.file_range(endian); let mut section_names = vec![]; for section in binary.sections() { diff --git a/src/lib.rs b/src/lib.rs index 31b5ed2..89e65d5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,7 +11,7 @@ pub mod probe; pub mod regs; pub mod usb_device; -use clap::{builder::PossibleValue, ValueEnum}; +use clap::{ValueEnum, builder::PossibleValue}; use probe::WchLink; pub use crate::error::{Error, Result}; @@ -270,11 +270,15 @@ impl RiscvChip { } RiscvChip::CH57X | RiscvChip::CH582 => { log::warn!("The debug interface has been opened, there is a risk of code leakage."); - log::warn!("Please ensure that the debug interface has been closed before leaving factory!"); + log::warn!( + "Please ensure that the debug interface has been closed before leaving factory!" + ); } RiscvChip::CH56X => { log::warn!("The debug interface has been opened, there is a risk of code leakage."); - log::warn!("Please ensure that the debug interface has been closed before leaving factory!"); + log::warn!( + "Please ensure that the debug interface has been closed before leaving factory!" + ); // 81 0d 01 04 // should test return value let resp = probe.send_command(commands::RawCommand::<0x0d>(vec![0x04]))?; diff --git a/src/main.rs b/src/main.rs index 4cb8b4a..14a79e7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,12 +2,12 @@ use std::{thread::sleep, time::Duration}; use anyhow::Result; use wlink::{ - commands, + RiscvChip, commands, dmi::DebugModuleInterface, - firmware::{read_firmware_from_file, Firmware}, + firmware::{Firmware, read_firmware_from_file}, operations::ProbeSession, probe::WchLink, - regs, RiscvChip, + regs, }; use clap::{Parser, Subcommand}; diff --git a/src/operations.rs b/src/operations.rs index d4dedef..85c23aa 100644 --- a/src/operations.rs +++ b/src/operations.rs @@ -4,9 +4,9 @@ use indicatif::ProgressBar; use std::{thread::sleep, time::Duration}; use crate::{ + Error, Result, RiscvChip, commands::{self, Speed}, probe::WchLink, - Error, Result, RiscvChip, }; /// A running probe session, flash, erase, inspect, etc. @@ -324,7 +324,9 @@ impl ProbeSession { if mem.starts_with(&[0xA9, 0xBD, 0xF9, 0xF3]) { log::warn!("A9 BD F9 F3 sequence detected!"); - log::warn!("If the chip is just put into debug mode, you should flash the new firmware to the chip first"); + log::warn!( + "If the chip is just put into debug mode, you should flash the new firmware to the chip first" + ); log::warn!("Or else this indicates a reading to invalid location"); } diff --git a/src/probe.rs b/src/probe.rs index aa5e61e..e3011f8 100644 --- a/src/probe.rs +++ b/src/probe.rs @@ -1,8 +1,8 @@ //! The probe - WCH-Link use crate::commands::{self, RawCommand, Response}; +use crate::{Error, Result, RiscvChip, usb_device}; use crate::{commands::control::ProbeInfo, usb_device::USBDeviceBackend}; -use crate::{usb_device, Error, Result, RiscvChip}; use std::fmt; pub const VENDOR_ID: u16 = 0x1a86; diff --git a/src/usb_device.rs b/src/usb_device.rs index b6016c0..b758738 100644 --- a/src/usb_device.rs +++ b/src/usb_device.rs @@ -22,11 +22,11 @@ pub fn open_nth(vid: u16, pid: u16, nth: usize) -> Result Result> { pub mod libusb { use std::fmt; + use std::io::{Read, Write}; use super::*; - use rusb::{DeviceHandle, Speed, UsbContext}; + use nusb::transfer::{Bulk, In, Out}; + use nusb::MaybeFuture; pub fn list_libusb_devices(vid: u16, pid: u16) -> Result> { - let context = rusb::Context::new()?; - let devices = context.devices()?; + let devices = nusb::list_devices().wait().map_err(crate::Error::Usb)?; let mut result = vec![]; let mut idx = 0; - for device in devices.iter() { - let device_desc = device.device_descriptor()?; - if device_desc.vendor_id() == vid && device_desc.product_id() == pid { + for device in devices { + if device.vendor_id() == vid && device.product_id() == pid { result.push(format!( - " Bus {:03} Device {:03} ID {:04x}:{:04x}({})", + " ID {:04x}:{:04x}({})", idx, - device.bus_number(), - device.address(), - device_desc.vendor_id(), - device_desc.product_id(), + device.vendor_id(), + device.product_id(), get_speed(device.speed()) )); idx += 1; @@ -80,79 +78,89 @@ pub mod libusb { Ok(result) } - pub struct LibUSBDevice { - handle: DeviceHandle, + pub struct NusbDevice { + interface: nusb::Interface, + #[allow(dead_code)] timeout: Duration, } - impl fmt::Debug for LibUSBDevice { + impl fmt::Debug for NusbDevice { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("USBDevice") - .field("provider", &"libusb") - .field("handle", &self.handle.device()) + .field("provider", &"nusb") .finish() } } - impl USBDeviceBackend for LibUSBDevice { + impl USBDeviceBackend for NusbDevice { fn set_timeout(&mut self, timeout: Duration) { self.timeout = timeout; } fn open_nth(vid: u16, pid: u16, nth: usize) -> Result> { - let context = rusb::Context::new()?; - let devices = context.devices()?; - let mut result = vec![]; - for device in devices.iter() { - let device_desc = device.device_descriptor()?; - if device_desc.vendor_id() == vid && device_desc.product_id() == pid { - result.push(device); - } - } - if nth >= result.len() { + let devices: Vec<_> = nusb::list_devices() + .wait() + .map_err(crate::Error::Usb)? + .filter(|d| d.vendor_id() == vid && d.product_id() == pid) + .collect(); + + if nth >= devices.len() { return Err(crate::Error::ProbeNotFound); } - let device = result.remove(nth); - let handle = device.open()?; - log::trace!("Device: {:?}", &device); + let device_info = &devices[nth]; + log::trace!("Device: {:04x}:{:04x}", device_info.vendor_id(), device_info.product_id()); + + if let Some(serial) = device_info.serial_number() { + log::debug!("Serial number: {:?}", serial); + } - let desc = device.device_descriptor()?; - let serial_number = handle.read_serial_number_string_ascii(&desc)?; - log::debug!("Serial number: {:?}", serial_number); + let device = device_info.open().wait().map_err(|e| { + log::error!("Failed to open USB device: {}", e); + #[cfg(target_os = "windows")] + log::warn!("It's likely no WinUSB driver installed. Please install it from Zadig. See also: https://zadig.akeo.ie"); + #[cfg(target_os = "linux")] + log::warn!("It's likely the udev rules are not installed properly. Please refer to README.md for more details."); + crate::Error::Usb(e) + })?; - handle.claim_interface(0)?; + let interface = device.claim_interface(0).wait().map_err(crate::Error::Usb)?; - Ok(Box::new(LibUSBDevice { - handle, + Ok(Box::new(NusbDevice { + interface, timeout: Duration::from_millis(5000), })) } fn read_endpoint(&mut self, ep: u8, buf: &mut [u8]) -> Result { - let bytes_read = self.handle.read_bulk(ep, buf, self.timeout)?; - Ok(bytes_read) + let endpoint = self + .interface + .endpoint::(ep) + .map_err(|e| crate::Error::Custom(format!("Failed to get endpoint: {}", e)))?; + let mut reader = endpoint.reader(64); + let n = reader.read(buf)?; + Ok(n) } fn write_endpoint(&mut self, ep: u8, buf: &[u8]) -> Result<()> { - self.handle.write_bulk(ep, buf, self.timeout)?; + let endpoint = self + .interface + .endpoint::(ep) + .map_err(|e| crate::Error::Custom(format!("Failed to get endpoint: {}", e)))?; + let mut writer = endpoint.writer(64); + writer.write_all(buf)?; + writer.flush()?; Ok(()) } } - impl Drop for LibUSBDevice { - fn drop(&mut self) { - let _ = self.handle.release_interface(0); - } - } - - fn get_speed(speed: Speed) -> &'static str { + fn get_speed(speed: Option) -> &'static str { match speed { - Speed::SuperPlus => "USB-SS+ 10000 Mbps", - Speed::Super => "USB-SS 5000 Mbps", - Speed::High => "USB-HS 480 Mbps", - Speed::Full => "USB-FS 12 Mbps", - Speed::Low => "USB-LS 1.5 Mbps", + Some(nusb::Speed::SuperPlus) => "USB-SS+ 10000 Mbps", + Some(nusb::Speed::Super) => "USB-SS 5000 Mbps", + Some(nusb::Speed::High) => "USB-HS 480 Mbps", + Some(nusb::Speed::Full) => "USB-FS 12 Mbps", + Some(nusb::Speed::Low) => "USB-LS 1.5 Mbps", _ => "(unknown)", } }