stm32cubeprog_rs/
lib.rs

1//!
2//! Rust bindings for the STM32CubeProgrammer API library.
3//!
4//! # Compatibility
5//!
6//! This crate is compatible with both Linux and Windows.
7//!
8//! # Requirements
9//! This crate requires STM32CubeProgrammer (version 2.14.0 or later) to be installed.
10//!
11//! # Example
12//!
13//! The code below will discover connected STLinks on Linux, retrieve information, read and write memory, and finally program the attached device.
14//!
15//! ```
16//! fn main() -> Result<(), Box<dyn std::error::Error>> {
17//!     // Generate path to STM32CubeProgrammer folder
18//!     let home_dir = std::env::var_os("HOME")
19//!         .map(std::path::PathBuf::from)
20//!         .expect("Failed to get home directory, $HOME variable missing");
21//!     let binding = home_dir.join("Applications/STMicroelectronics/STM32Cube/STM32CubeProgrammer");
22//!     let stm32prog_path = binding
23//!         .to_str()
24//!         .expect("Failed to join STM32CubeProgrammer path");
25//!     
26//!     // Load STM32CubeProgmmer API library
27//!     let stm32prog = stm32cubeprog_rs::STM32CubeProg::new(stm32prog_path)?;
28//!
29//!     // Find connected STLinks
30//!     let mut stlinks = stm32prog.discover()?;
31//!     for stlink in stlinks.iter_mut() {
32//!         println!("{stlink}");
33//!         
34//!         // Configure the reset mode and the connection mode
35//!         stlink.reset_mode(stm32cubeprog_rs::DebugResetMode::HardwareReset);
36//!         stlink.connection_mode(stm32cubeprog_rs::DebugConnectMode::UnderReset);
37//!
38//!         // Connect the STlink
39//!         stm32prog.connect(stlink)?;
40//!         
41//!         // Fetch device information
42//!         let device_info = stm32prog.device_info()?;
43//!         println!("{device_info}");
44//!         
45//!         // Read and write register R0
46//!         stm32prog.write_core_register(stm32cubeprog_rs::Register::R0, 0xAABBCCDD)?;
47//!         let data = stm32prog.read_core_register(stm32cubeprog_rs::Register::R0)?;
48//!         println!("R0:  0x{data:X}");
49//!         
50//!         // Read and write memory
51//!         let data = stm32prog.read_memory8(0x1FFF7590, 16)?;
52//!         println!("0x1FFF7590: {data:x?}");
53//!
54//!         stm32prog.write_memory8(0x20000100, data)?;
55//!
56//!         let data = stm32prog.read_memory32(0x1FFF7590, 4)?;
57//!         println!("0x1FFF7590: {data:x?}");
58//!
59//!         stm32prog.write_memory32(0x20000200, data)?;
60//!         
61//!         // Mass erase the device
62//!         stm32prog.mass_erase()?;
63//!
64//!         // Flash the device
65//!         stm32prog.download("demo.hex", None, None, None)?;
66//!         
67//!         // Reset and disconnect the STLink
68//!         stm32prog.reset(stlink)?;
69//!         stm32prog.disconnect();
70//!     }
71//!
72//!     Ok(())
73//! }
74//! ```
75
76pub mod err;
77
78#[cfg(unix)]
79#[allow(non_camel_case_types)]
80pub type wchar = u32;
81#[cfg(windows)]
82#[allow(non_camel_case_types)]
83pub type wchar = u16;
84
85#[repr(C)]
86pub struct DisplayCallbacks {
87    pub init_progress_bar: extern "C" fn(),
88    pub log_message: extern "C" fn(msg_type: std::os::raw::c_int, msg: *const std::os::raw::c_int),
89    pub load_bar: extern "C" fn(current: std::os::raw::c_int, total: std::os::raw::c_int),
90}
91
92extern "C" fn init_progress_bar() {}
93
94extern "C" fn log_message(_msg_type: std::os::raw::c_int, _msg: *const std::os::raw::c_int) {}
95
96extern "C" fn load_bar(_current: std::os::raw::c_int, _total: std::os::raw::c_int) {}
97
98#[repr(C)]
99#[derive(Debug, Copy, Clone)]
100pub enum Verbosity {
101    Level0 = 0,
102    Level1 = 1,
103    Level2 = 2,
104    Level3 = 3,
105}
106
107#[repr(C)]
108#[derive(Debug, Copy, Clone)]
109pub enum DebugPort {
110    Jtag = 0,
111    Swd = 1,
112}
113
114#[repr(C)]
115#[derive(Debug, Copy, Clone)]
116pub enum DebugConnectMode {
117    Normal = 0,
118    HotPlug = 1,
119    UnderReset = 2,
120    PowerDown = 3,
121    PreReset = 4,
122}
123
124#[repr(C)]
125#[derive(Debug, Copy, Clone)]
126pub enum DebugResetMode {
127    SoftwareReset = 0,
128    HardwareReset = 1,
129    CoreReset = 2,
130}
131
132#[repr(C)]
133#[derive(Debug, Copy, Clone)]
134pub struct Frequencies {
135    pub jtag_freq: [std::os::raw::c_uint; 12usize],
136    pub jtag_freq_count: std::os::raw::c_uint,
137    pub swd_freq: [std::os::raw::c_uint; 12usize],
138    pub swd_freq_count: std::os::raw::c_uint,
139}
140
141impl Frequencies {
142    pub fn jtag_frequencies(&self) -> Vec<u32> {
143        let size = std::convert::TryInto::try_into(self.jtag_freq_count).unwrap_or_default();
144        self.jtag_freq.to_vec()[0..size].to_vec()
145    }
146
147    pub fn swd_frequencies(&self) -> Vec<u32> {
148        let size = std::convert::TryInto::try_into(self.swd_freq_count).unwrap_or_default();
149        self.swd_freq.to_vec()[0..size].to_vec()
150    }
151}
152
153impl std::fmt::Display for Frequencies {
154    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
155        write!(
156            f,
157            "\
158JTAG Frequencies Available: {:?},
159SWD Frequencies Available: {:?}",
160            self.jtag_frequencies(),
161            self.swd_frequencies(),
162        )
163    }
164}
165
166#[repr(C)]
167#[derive(Debug, Copy, Clone)]
168pub struct DeviceGeneralInfo {
169    pub device_id: ::std::os::raw::c_ushort,
170    pub flash_size: ::std::os::raw::c_int,
171    pub bootloader_version: ::std::os::raw::c_int,
172    pub category: [::std::os::raw::c_char; 4usize],
173    pub cpu: [::std::os::raw::c_char; 20usize],
174    pub name: [::std::os::raw::c_char; 100usize],
175    pub series: [::std::os::raw::c_char; 100usize],
176    pub description: [::std::os::raw::c_char; 150usize],
177    pub revision_id: [::std::os::raw::c_char; 8usize],
178    pub board: [::std::os::raw::c_char; 100usize],
179}
180
181#[repr(C)]
182#[derive(Debug, Copy, Clone)]
183pub struct DebugConnectParameters {
184    pub debug_port: DebugPort,
185    pub index: std::os::raw::c_int,
186    pub serial_number: [std::os::raw::c_char; 33usize],
187    pub firmware_version: [std::os::raw::c_char; 20usize],
188    pub target_voltage: [std::os::raw::c_char; 5usize],
189    pub access_port_count: std::os::raw::c_int,
190    pub access_port: std::os::raw::c_int,
191    pub connection_mode: DebugConnectMode,
192    pub reset_mode: DebugResetMode,
193    pub old_firmware: std::os::raw::c_int,
194    pub frequencies: Frequencies,
195    pub frequency: std::os::raw::c_int,
196    pub bridge: std::os::raw::c_int,
197    pub shared: std::os::raw::c_int,
198    pub board: [std::os::raw::c_char; 100usize],
199    pub debug_sleep: std::os::raw::c_int,
200    pub speed: std::os::raw::c_int,
201}
202
203type SetLoaderPath = unsafe extern "C" fn(path: *const std::os::raw::c_char);
204type SetDisplayCallbacks = unsafe extern "C" fn(c: DisplayCallbacks);
205type SetVerbosityLevel = unsafe extern "C" fn(level: Verbosity);
206type GetStLinkList = unsafe extern "C" fn(
207    debug_connect_parameters: *mut *mut DebugConnectParameters,
208    shared: std::os::raw::c_int,
209) -> std::os::raw::c_int;
210type ConnectStLink =
211    unsafe extern "C" fn(debug_connect_parameters: DebugConnectParameters) -> std::os::raw::c_int;
212type Disconnect = unsafe extern "C" fn();
213type Reset = unsafe extern "C" fn(reset_mode: DebugResetMode) -> std::os::raw::c_int;
214type MassErase = unsafe extern "C" fn() -> std::os::raw::c_int;
215type DownloadFile = unsafe extern "C" fn(
216    file_path: *const wchar,
217    address: std::os::raw::c_uint,
218    skip_erase: std::os::raw::c_uint,
219    verify: std::os::raw::c_uint,
220    path: *const wchar,
221) -> std::os::raw::c_int;
222type GetDeviceGeneralInfo = unsafe extern "C" fn() -> *mut DeviceGeneralInfo;
223type ReadMemory = unsafe extern "C" fn(
224    address: ::std::os::raw::c_uint,
225    data: *mut *mut ::std::os::raw::c_uchar,
226    size: ::std::os::raw::c_uint,
227) -> ::std::os::raw::c_int;
228type WriteMemory = unsafe extern "C" fn(
229    address: ::std::os::raw::c_uint,
230    data: *mut ::std::os::raw::c_uchar,
231    size: ::std::os::raw::c_uint,
232) -> ::std::os::raw::c_int;
233type ReadCoreRegister = unsafe extern "C" fn(
234    register: std::os::raw::c_uint,
235    data: *mut std::os::raw::c_uint,
236) -> ::std::os::raw::c_int;
237type WriteCoreRegister = unsafe extern "C" fn(
238    register: std::os::raw::c_uint,
239    data: std::os::raw::c_uint,
240) -> ::std::os::raw::c_int;
241
242#[cfg(unix)]
243pub struct VTable {
244    set_loaders_path: libloading::os::unix::Symbol<SetLoaderPath>,
245    set_display_callbacks: libloading::os::unix::Symbol<SetDisplayCallbacks>,
246    set_verbosity_level: libloading::os::unix::Symbol<SetVerbosityLevel>,
247    get_stlink_list: libloading::os::unix::Symbol<GetStLinkList>,
248    connect_stlink: libloading::os::unix::Symbol<ConnectStLink>,
249    disconnect: libloading::os::unix::Symbol<Disconnect>,
250    reset: libloading::os::unix::Symbol<Reset>,
251    mass_erase: libloading::os::unix::Symbol<MassErase>,
252    download_file: libloading::os::unix::Symbol<DownloadFile>,
253    get_device_general_info: libloading::os::unix::Symbol<GetDeviceGeneralInfo>,
254    read_memory: libloading::os::unix::Symbol<ReadMemory>,
255    write_memory: libloading::os::unix::Symbol<WriteMemory>,
256    read_core_register: libloading::os::unix::Symbol<ReadCoreRegister>,
257    write_core_register: libloading::os::unix::Symbol<WriteCoreRegister>,
258}
259
260#[cfg(windows)]
261pub struct VTable {
262    set_loaders_path: libloading::os::windows::Symbol<SetLoaderPath>,
263    set_display_callbacks: libloading::os::windows::Symbol<SetDisplayCallbacks>,
264    set_verbosity_level: libloading::os::windows::Symbol<SetVerbosityLevel>,
265    get_stlink_list: libloading::os::windows::Symbol<GetStLinkList>,
266    connect_stlink: libloading::os::windows::Symbol<ConnectStLink>,
267    disconnect: libloading::os::windows::Symbol<Disconnect>,
268    reset: libloading::os::windows::Symbol<Reset>,
269    mass_erase: libloading::os::windows::Symbol<MassErase>,
270    download_file: libloading::os::windows::Symbol<DownloadFile>,
271    get_device_general_info: libloading::os::windows::Symbol<GetDeviceGeneralInfo>,
272    read_memory: libloading::os::windows::Symbol<ReadMemory>,
273    write_memory: libloading::os::windows::Symbol<WriteMemory>,
274    read_core_register: libloading::os::windows::Symbol<ReadCoreRegister>,
275    write_core_register: libloading::os::windows::Symbol<WriteCoreRegister>,
276}
277
278impl VTable {
279    fn new(library: &libloading::Library) -> Result<Self, err::Error> {
280        let set_loaders_path: libloading::Symbol<SetLoaderPath> =
281            unsafe { library.get(b"setLoadersPath\0")? };
282        let set_loaders_path = unsafe { set_loaders_path.into_raw() };
283        let set_display_callbacks: libloading::Symbol<SetDisplayCallbacks> =
284            unsafe { library.get(b"setDisplayCallbacks\0")? };
285        let set_display_callbacks = unsafe { set_display_callbacks.into_raw() };
286        let set_verbosity_level: libloading::Symbol<SetVerbosityLevel> =
287            unsafe { library.get(b"setVerbosityLevel\0")? };
288        let set_verbosity_level = unsafe { set_verbosity_level.into_raw() };
289        let get_stlink_list: libloading::Symbol<GetStLinkList> =
290            unsafe { library.get(b"getStLinkList\0")? };
291        let get_stlink_list = unsafe { get_stlink_list.into_raw() };
292        let connect_stlink: libloading::Symbol<ConnectStLink> =
293            unsafe { library.get(b"connectStLink\0")? };
294        let connect_stlink = unsafe { connect_stlink.into_raw() };
295        let disconnect: libloading::Symbol<Disconnect> = unsafe { library.get(b"disconnect\0")? };
296        let disconnect = unsafe { disconnect.into_raw() };
297        let reset: libloading::Symbol<Reset> = unsafe { library.get(b"reset\0")? };
298        let reset = unsafe { reset.into_raw() };
299        let mass_erase: libloading::Symbol<MassErase> = unsafe { library.get(b"massErase\0")? };
300        let mass_erase = unsafe { mass_erase.into_raw() };
301        let download_file: libloading::Symbol<DownloadFile> =
302            unsafe { library.get(b"downloadFile\0")? };
303        let download_file = unsafe { download_file.into_raw() };
304        let get_device_general_info: libloading::Symbol<GetDeviceGeneralInfo> =
305            unsafe { library.get(b"getDeviceGeneralInf\0")? };
306        let get_device_general_info = unsafe { get_device_general_info.into_raw() };
307        let read_memory: libloading::Symbol<ReadMemory> = unsafe { library.get(b"readMemory\0")? };
308        let read_memory = unsafe { read_memory.into_raw() };
309        let write_memory: libloading::Symbol<WriteMemory> =
310            unsafe { library.get(b"writeMemory\0")? };
311        let write_memory = unsafe { write_memory.into_raw() };
312        let read_core_register: libloading::Symbol<ReadCoreRegister> =
313            unsafe { library.get(b"readCortexReg\0")? };
314        let read_core_register = unsafe { read_core_register.into_raw() };
315        let write_core_register: libloading::Symbol<WriteCoreRegister> =
316            unsafe { library.get(b"writeCortexRegistres\0")? };
317        let write_core_register = unsafe { write_core_register.into_raw() };
318
319        Ok(VTable {
320            set_loaders_path,
321            set_display_callbacks,
322            set_verbosity_level,
323            get_stlink_list,
324            connect_stlink,
325            disconnect,
326            reset,
327            mass_erase,
328            download_file,
329            get_device_general_info,
330            read_memory,
331            write_memory,
332            read_core_register,
333            write_core_register,
334        })
335    }
336}
337
338#[derive(Debug, Clone)]
339pub struct STLink {
340    debug_connect_parameters: DebugConnectParameters,
341}
342
343impl STLink {
344    pub fn frequencies(&self) -> Frequencies {
345        self.debug_connect_parameters.frequencies
346    }
347
348    pub fn debug_port(&self) -> DebugPort {
349        self.debug_connect_parameters.debug_port
350    }
351
352    pub fn index(&self) -> i32 {
353        self.debug_connect_parameters.index
354    }
355
356    pub fn access_port_count(&self) -> i32 {
357        self.debug_connect_parameters.access_port_count
358    }
359
360    pub fn access_port(&self) -> i32 {
361        self.debug_connect_parameters.access_port
362    }
363
364    pub fn connection_mode(&self) -> DebugConnectMode {
365        self.debug_connect_parameters.connection_mode
366    }
367
368    pub fn reset_mode(&self) -> DebugResetMode {
369        self.debug_connect_parameters.reset_mode
370    }
371
372    pub fn old_firmware(&self) -> bool {
373        self.debug_connect_parameters.old_firmware == 1
374    }
375
376    pub fn frequency(&self) -> i32 {
377        self.debug_connect_parameters.frequency
378    }
379
380    pub fn bridge(&self) -> bool {
381        self.debug_connect_parameters.bridge == 1
382    }
383
384    pub fn shared(&self) -> bool {
385        self.debug_connect_parameters.shared == 1
386    }
387
388    pub fn debug_sleep(&self) -> bool {
389        self.debug_connect_parameters.debug_sleep == 1
390    }
391
392    pub fn speed(&self) -> i32 {
393        self.debug_connect_parameters.speed
394    }
395
396    pub fn target_voltage(&self) -> Result<f32, err::Error> {
397        Ok(String::from_utf8(
398            self.debug_connect_parameters
399                .target_voltage
400                .iter()
401                .map(|&c| c as u8)
402                .collect(),
403        )?.trim_matches(char::from(0)).parse()?)
404    }
405
406    pub fn serial_number(&self) -> Result<String, err::Error> {
407        Ok(String::from_utf8(
408            self.debug_connect_parameters
409                .serial_number
410                .iter()
411                .map(|&c| c as u8)
412                .collect(),
413        )?.trim_matches(char::from(0)).to_owned())
414    }
415
416    pub fn firmware_version(&self) -> Result<String, err::Error> {
417        Ok(String::from_utf8(
418            self.debug_connect_parameters
419                .firmware_version
420                .iter()
421                .map(|&c| c as u8)
422                .collect(),
423        )?.trim_matches(char::from(0)).to_owned())
424    }
425
426    pub fn board(&self) -> Result<String, err::Error> {
427        Ok(String::from_utf8(
428            self.debug_connect_parameters
429                .board
430                .iter()
431                .map(|&c| c as u8)
432                .collect(),
433        )?.trim_matches(char::from(0)).to_owned())
434    }
435
436    pub fn set_access_port(&mut self, access_port: i32) {
437        self.debug_connect_parameters.access_port = access_port;
438    }
439
440    pub fn set_frequency(&mut self, frequency: i32) {
441        self.debug_connect_parameters.frequency = frequency;
442    }
443
444    pub fn set_reset_mode(&mut self, reset_mode: DebugResetMode) {
445        self.debug_connect_parameters.reset_mode = reset_mode;
446    }
447
448    pub fn set_connection_mode(&mut self, connection_mode: DebugConnectMode) {
449        self.debug_connect_parameters.connection_mode = connection_mode
450    }
451}
452
453impl std::fmt::Display for STLink {
454    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
455        write!(
456            f,
457            "\
458Board: {},
459Serial Number: {},
460Firmware Version: {},
461Index: {},
462Connection Mode: {:?},
463Reset Mode: {:?},
464Access Port Count: {},
465Access Port: {},
466Debug Port: {:?},
467Old Firmware: {},
468{},
469Frequency: {},
470Bridge: {},
471Shared: {},
472Debug Sleep: {},
473Speed: {},
474Target Voltage: {}",
475            self.board().unwrap_or("undefined".into()),
476            self.serial_number().unwrap_or("undefined".into()),
477            self.firmware_version().unwrap_or("undefined".into()),
478            self.index(),
479            self.connection_mode(),
480            self.reset_mode(),
481            self.access_port_count(),
482            self.access_port(),
483            self.debug_port(),
484            self.old_firmware(),
485            self.frequencies(),
486            self.frequency(),
487            self.bridge(),
488            self.shared(),
489            self.debug_sleep(),
490            self.speed(),
491            self.target_voltage().unwrap_or(0.0)
492        )
493    }
494}
495
496#[derive(Debug, Clone)]
497pub struct DeviceInfo {
498    device_general_info: DeviceGeneralInfo,
499}
500
501impl DeviceInfo {
502    pub fn category(&self) -> Result<String, err::Error> {
503        Ok(String::from_utf8(
504            self.device_general_info
505                .category
506                .iter()
507                .map(|&c| c as u8)
508                .collect(),
509        )?.trim_matches(char::from(0)).to_owned())
510    }
511
512    pub fn cpu(&self) -> Result<String, err::Error> {
513        Ok(String::from_utf8(
514            self.device_general_info
515                .cpu
516                .iter()
517                .map(|&c| c as u8)
518                .collect(),
519        )?.trim_matches(char::from(0)).to_owned())
520    }
521
522    pub fn name(&self) -> Result<String, err::Error> {
523        Ok(String::from_utf8(
524            self.device_general_info
525                .name
526                .iter()
527                .map(|&c| c as u8)
528                .collect(),
529        )?.trim_matches(char::from(0)).to_owned())
530    }
531
532    pub fn series(&self) -> Result<String, err::Error> {
533        Ok(String::from_utf8(
534            self.device_general_info
535                .series
536                .iter()
537                .map(|&c| c as u8)
538                .collect(),
539        )?.trim_matches(char::from(0)).to_owned())
540    }
541
542    pub fn description(&self) -> Result<String, err::Error> {
543        Ok(String::from_utf8(
544            self.device_general_info
545                .description
546                .iter()
547                .map(|&c| c as u8)
548                .collect(),
549        )?.trim_matches(char::from(0)).to_owned())
550    }
551
552    pub fn revision_id(&self) -> Result<String, err::Error> {
553        Ok(String::from_utf8(
554            self.device_general_info
555                .revision_id
556                .iter()
557                .map(|&c| c as u8)
558                .collect(),
559        )?.trim_matches(char::from(0)).to_owned())
560    }
561
562    pub fn board(&self) -> Result<String, err::Error> {
563        Ok(String::from_utf8(
564            self.device_general_info
565                .board
566                .iter()
567                .map(|&c| c as u8)
568                .collect(),
569        )?.trim_matches(char::from(0)).to_owned())
570    }
571
572    pub fn device_id(&self) -> i32 {
573        self.device_general_info.device_id.into()
574    }
575
576    pub fn flash_size(&self) -> i32 {
577        self.device_general_info.flash_size
578    }
579
580    pub fn bootloader_version(&self) -> i32 {
581        self.device_general_info.bootloader_version
582    }
583}
584
585impl std::fmt::Display for DeviceInfo {
586    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
587        write!(
588            f,
589            "\
590Category: {},
591Cpu: {},
592Name: {},
593Series: {},
594Description: {},
595Revision Id: {},
596Board: {},
597Device Id: 0x{:X},
598Flash Size: 0x{:X},
599Bootloader Version: 0x{:X}",
600            self.category().unwrap_or("undefined".into()),
601            self.cpu().unwrap_or("undefined".into()),
602            self.name().unwrap_or("undefined".into()),
603            self.series().unwrap_or("undefined".into()),
604            self.description().unwrap_or("undefined".into()),
605            self.revision_id().unwrap_or("undefined".into()),
606            self.board().unwrap_or("undefined".into()),
607            self.device_id(),
608            self.flash_size(),
609            self.bootloader_version()
610        )
611    }
612}
613
614pub enum Register {
615    R0,
616    R1,
617    R2,
618    R3,
619    R4,
620    R5,
621    R6,
622    R7,
623    R8,
624    R9,
625    R10,
626    R11,
627    R12,
628    SP,
629    LR,
630    PC,
631}
632
633impl From<Register> for u32 {
634    fn from(register: Register) -> Self {
635        match register {
636            Register::R0 => 0,
637            Register::R1 => 1,
638            Register::R2 => 2,
639            Register::R3 => 3,
640            Register::R4 => 4,
641            Register::R5 => 5,
642            Register::R6 => 6,
643            Register::R7 => 7,
644            Register::R8 => 8,
645            Register::R9 => 9,
646            Register::R10 => 10,
647            Register::R11 => 11,
648            Register::R12 => 12,
649            Register::SP => 13,
650            Register::LR => 14,
651            Register::PC => 15,
652        }
653    }
654}
655
656pub struct STM32CubeProg {
657    #[allow(dead_code)]
658    library: libloading::Library,
659    vtable: VTable,
660}
661
662impl STM32CubeProg {
663    #[cfg(unix)]
664    fn library_path(path: &std::path::Path) -> std::path::PathBuf {
665        path.join("lib/libCubeProgrammer_API.so")
666    }
667
668    #[cfg(windows)]
669    fn library_path(path: &std::path::Path) -> std::path::PathBuf {
670        path.join("api\\lib\\CubeProgrammer_API.dll")
671    }
672
673    #[cfg(unix)]
674    fn flashloader_path(path: &std::path::Path) -> std::path::PathBuf {
675        path.join("bin")
676    }
677
678    #[cfg(windows)]
679    fn flashloader_path(path: &std::path::Path) -> std::path::PathBuf {
680        path.join("bin")
681    }
682
683    #[cfg(unix)]
684    fn load_library(path: &std::path::Path) -> Result<libloading::Library, err::Error> {
685        let library: libloading::Library =
686            unsafe { libloading::os::unix::Library::new(path)?.into() };
687        Ok(library)
688    }
689
690    #[cfg(windows)]
691    fn load_library(path: &std::path::Path) -> Result<libloading::Library, err::Error> {
692        let library: libloading::Library = unsafe {
693            libloading::os::windows::Library::load_with_flags(
694                path,
695                libloading::os::windows::LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR
696                    | libloading::os::windows::LOAD_LIBRARY_SEARCH_SYSTEM32
697                    | libloading::os::windows::LOAD_LIBRARY_SEARCH_DEFAULT_DIRS,
698            )?
699            .into()
700        };
701        Ok(library)
702    }
703
704    pub fn new<P: AsRef<std::path::Path>>(path: P) -> Result<Self, err::Error> {
705        let library_path = Self::library_path(path.as_ref());
706        let library = Self::load_library(library_path.as_ref())?;
707        let vtable = VTable::new(&library)?;
708
709        unsafe {
710            (vtable.set_loaders_path)(
711                Self::flashloader_path(path.as_ref())
712                    .as_os_str()
713                    .as_encoded_bytes()
714                    .as_ptr() as *const i8,
715            )
716        };
717
718        let cb: DisplayCallbacks = DisplayCallbacks {
719            init_progress_bar: init_progress_bar,
720            log_message: log_message,
721            load_bar: load_bar,
722        };
723
724        unsafe { (vtable.set_display_callbacks)(cb) };
725
726        unsafe { (vtable.set_verbosity_level)(Verbosity::Level0) };
727
728        Ok(STM32CubeProg { library, vtable })
729    }
730
731    pub fn discover(&self) -> Result<Vec<STLink>, err::Error> {
732        let mut debug_connect_parameters = std::ptr::null_mut();
733        let stlink_count =
734            unsafe { (self.vtable.get_stlink_list)(&mut debug_connect_parameters, 0) };
735
736        let params_slice =
737            unsafe { std::slice::from_raw_parts(debug_connect_parameters, stlink_count as usize) };
738
739        params_slice
740            .iter()
741            .map(|param| -> Result<STLink, err::Error> {
742                let debug_connect_parameters = param.clone();
743                Ok(STLink {
744                    debug_connect_parameters,
745                })
746            })
747            .collect::<Result<Vec<STLink>, err::Error>>()
748    }
749
750    pub fn connect(&self, stlink: &STLink) -> Result<(), err::Error> {
751        let error = unsafe { (self.vtable.connect_stlink)(stlink.debug_connect_parameters) };
752        if error == 0 {
753            Ok(())
754        } else {
755            Err(err::CubeProgrammerError::from(error).into())
756        }
757    }
758
759    pub fn disconnect(&self) {
760        unsafe { (self.vtable.disconnect)() };
761    }
762
763    pub fn reset(&self, stlink: &STLink) -> Result<(), err::Error> {
764        let error = unsafe { (self.vtable.reset)(stlink.debug_connect_parameters.reset_mode) };
765        if error == 0 {
766            Ok(())
767        } else {
768            Err(err::CubeProgrammerError::from(error).into())
769        }
770    }
771
772    pub fn mass_erase(&self) -> Result<(), err::Error> {
773        let error = unsafe { (self.vtable.mass_erase)() };
774        if error == 0 {
775            Ok(())
776        } else {
777            Err(err::CubeProgrammerError::from(error).into())
778        }
779    }
780
781    pub fn download<P: AsRef<std::path::Path>>(
782        &self,
783        path: P,
784        address: Option<u32>,
785        skip_erase: Option<bool>,
786        verify: Option<bool>,
787    ) -> Result<(), err::Error> {
788        let c_path = widestring::WideCString::from_os_str(
789            std::fs::canonicalize(path.as_ref())?.as_os_str(),
790        )?;
791
792        let error = unsafe {
793            (self.vtable.download_file)(
794                c_path.as_ptr(),
795                address.unwrap_or(0),
796                skip_erase.unwrap_or(true).into(),
797                verify.unwrap_or(true).into(),
798                std::ptr::null(),
799            )
800        };
801        if error == 0 {
802            Ok(())
803        } else {
804            Err(err::CubeProgrammerError::from(error).into())
805        }
806    }
807
808    pub fn device_info(&self) -> Result<DeviceInfo, err::Error> {
809        let device_general_info = unsafe { (self.vtable.get_device_general_info)().as_ref() };
810
811        match device_general_info {
812            Some(value) => Ok(DeviceInfo {
813                device_general_info: value.clone(),
814            }),
815            None => Err(err::CubeProgrammerError::NoDeviceFound.into()),
816        }
817    }
818
819    pub fn read_core_register(&self, register: Register) -> Result<u32, err::Error> {
820        let mut data = 0;
821        let error = unsafe { (self.vtable.read_core_register)(register.into(), &mut data) };
822        if error == 0 {
823            Ok(data)
824        } else {
825            Err(err::CubeProgrammerError::from(error).into())
826        }
827    }
828
829    pub fn write_core_register(&self, register: Register, data: u32) -> Result<(), err::Error> {
830        let error = unsafe { (self.vtable.write_core_register)(register.into(), data) };
831        if error == 0 {
832            Ok(())
833        } else {
834            Err(err::CubeProgrammerError::from(error).into())
835        }
836    }
837
838    pub fn read_memory8(&self, address: u32, size: u32) -> Result<Vec<u8>, err::Error> {
839        let mut data = std::ptr::null_mut();
840        let error = unsafe { (self.vtable.read_memory)(address, &mut data, size) };
841        if error == 0 {
842            let data: &mut [u8] = unsafe { core::slice::from_raw_parts_mut(data, size as usize) };
843            Ok(data.to_vec())
844        } else {
845            Err(err::CubeProgrammerError::from(error).into())
846        }
847    }
848
849    pub fn read_memory32(&self, address: u32, size: u32) -> Result<Vec<u32>, err::Error> {
850        let mut data = std::ptr::null_mut();
851        let error = unsafe { (self.vtable.read_memory)(address, &mut data, size * 4) };
852        if error == 0 {
853            let data: &mut [u8] =
854                unsafe { core::slice::from_raw_parts_mut(data, (size * 4) as usize) };
855            data.chunks(4)
856                .map(|chunk| -> Result<u32, err::Error> {
857                    let chunk = std::convert::TryInto::try_into(chunk)?;
858                    Ok(u32::from_le_bytes(chunk))
859                })
860                .collect::<Result<Vec<u32>, err::Error>>()
861        } else {
862            Err(err::CubeProgrammerError::from(error).into())
863        }
864    }
865
866    pub fn write_memory8(&self, address: u32, data: Vec<u8>) -> Result<(), err::Error> {
867        let size: u32 = std::convert::TryInto::try_into(data.len())?;
868
869        let error = unsafe { (self.vtable.write_memory)(address, data.clone().as_mut_ptr(), size) };
870        if error == 0 {
871            Ok(())
872        } else {
873            Err(err::CubeProgrammerError::from(error).into())
874        }
875    }
876
877    pub fn write_memory32(&self, address: u32, data_u32: Vec<u32>) -> Result<(), err::Error> {
878        let size: u32 = std::convert::TryInto::try_into(data_u32.len())?;
879        let mut data_u8: Vec<u8> = Vec::new();
880        for &num in &data_u32 {
881            data_u8.extend_from_slice(&num.to_le_bytes());
882        }
883
884        let error = unsafe { (self.vtable.write_memory)(address, data_u8.as_mut_ptr(), size * 4) };
885        if error == 0 {
886            Ok(())
887        } else {
888            Err(err::CubeProgrammerError::from(error).into())
889        }
890    }
891}