├── .gitignore ├── src ├── windows │ ├── mod.rs │ └── basetsd.rs ├── ch347lib │ ├── mod.rs │ ├── ch347lib.rs │ └── ch347dll.rs ├── lib.rs ├── spi_flash │ ├── mod.rs │ ├── spi_drive.rs │ ├── model │ │ ├── eon_silicon.rs │ │ ├── macronix.rs │ │ ├── mod.rs │ │ ├── gigadevice.rs │ │ └── winbond.rs │ └── spi_flash.rs └── bin │ └── ch347tool │ ├── spi_flash │ ├── detect.rs │ ├── erase.rs │ ├── read.rs │ ├── check.rs │ ├── mod.rs │ ├── utils.rs │ ├── write.rs │ └── reg.rs │ ├── main.rs │ ├── list.rs │ ├── gpio.rs │ └── i2c.rs ├── lib ├── Arm │ ├── libch347spi.so │ └── CH347SPILIB.h ├── Mips │ ├── libch347spi.so │ └── CH347SPILIB.h └── x86_64 │ ├── libch347.so │ ├── libch347spi.so │ └── CH347SPILIB.h ├── static_lib └── CH347DLLA64.LIB ├── Cargo.toml ├── README.md ├── LICENSE ├── examples └── enum_devices.rs └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /src/windows/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod basetsd; 2 | -------------------------------------------------------------------------------- /lib/Arm/libch347spi.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ma6254/ch347-rs/HEAD/lib/Arm/libch347spi.so -------------------------------------------------------------------------------- /lib/Mips/libch347spi.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ma6254/ch347-rs/HEAD/lib/Mips/libch347spi.so -------------------------------------------------------------------------------- /lib/x86_64/libch347.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ma6254/ch347-rs/HEAD/lib/x86_64/libch347.so -------------------------------------------------------------------------------- /lib/x86_64/libch347spi.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ma6254/ch347-rs/HEAD/lib/x86_64/libch347spi.so -------------------------------------------------------------------------------- /static_lib/CH347DLLA64.LIB: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ma6254/ch347-rs/HEAD/static_lib/CH347DLLA64.LIB -------------------------------------------------------------------------------- /src/ch347lib/mod.rs: -------------------------------------------------------------------------------- 1 | mod ch347dll; 2 | mod ch347lib; 3 | 4 | pub use ch347dll::*; 5 | pub use ch347lib::*; 6 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | mod ch347lib; 2 | mod spi_flash; 3 | mod windows; 4 | 5 | pub use ch347lib::*; 6 | pub use spi_flash::*; 7 | -------------------------------------------------------------------------------- /src/spi_flash/mod.rs: -------------------------------------------------------------------------------- 1 | mod model; 2 | mod spi_drive; 3 | mod spi_flash; 4 | 5 | pub use model::*; 6 | pub use spi_drive::*; 7 | pub use spi_flash::*; 8 | -------------------------------------------------------------------------------- /src/bin/ch347tool/spi_flash/detect.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | 3 | use clap::Parser; 4 | 5 | #[derive(Parser, Clone, Debug)] 6 | #[clap(about = "Detects spi flash chip model")] 7 | pub struct CmdSpiFlashDetect {} 8 | 9 | pub fn cli_spi_flash_detect( 10 | flash_args: &super::CmdSpiFlash, 11 | _args: &CmdSpiFlashDetect, 12 | ) -> Result<(), Box> { 13 | let _ = flash_args.init()?; 14 | 15 | Ok(()) 16 | } 17 | -------------------------------------------------------------------------------- /src/spi_flash/spi_drive.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use super::SpiFlash; 4 | 5 | pub trait SpiDrive { 6 | fn write_after_read( 7 | &self, 8 | write_len: u32, 9 | read_len: u32, 10 | iobuf: &mut [u8], 11 | ) -> Result<(), &'static str>; 12 | fn transfer(&self, iobuf: &mut [u8]) -> Result<(), &'static str>; 13 | } 14 | 15 | pub trait StatusRegister: fmt::Display { 16 | fn from_drive(spi_flash: &SpiFlash) -> Result 17 | where 18 | Self: Sized; 19 | } 20 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ch347_rs" 3 | description = "ch347 for rust" 4 | license = "MIT" 5 | version = "0.2.1" 6 | edition = "2021" 7 | build = "build.rs" 8 | 9 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 10 | 11 | [[bin]] 12 | name = "ch347tool" 13 | path = "src/bin/ch347tool/main.rs" 14 | doc = false 15 | 16 | [lib] 17 | test = true 18 | doctest = true 19 | doc = true 20 | 21 | [dependencies] 22 | clap = { version = "3.2", features = ["derive"] } 23 | cli-table = "0.4.7" 24 | console = "0.15" 25 | hex = "0.4.3" 26 | humantime = "2.1.0" 27 | indicatif = "0.17.1" 28 | libc = "0.2" 29 | serde = { version= "1.0", features = ["derive"] } 30 | serde_json = "1.0" 31 | shadow-rs = "0.16.3" 32 | 33 | [build-dependencies] 34 | shadow-rs = "0.16.3" 35 | -------------------------------------------------------------------------------- /src/bin/ch347tool/spi_flash/erase.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | error::Error, 3 | time::{Duration, SystemTime}, 4 | }; 5 | 6 | use clap::Parser; 7 | 8 | #[derive(Parser, Clone, Debug)] 9 | #[clap(about = "Erase spi flash chip")] 10 | pub struct CmdSpiFlashErase {} 11 | 12 | pub fn cli_spi_flash_erase( 13 | flash_args: &super::CmdSpiFlash, 14 | _args: &CmdSpiFlashErase, 15 | ) -> Result<(), Box> { 16 | let (device, _) = flash_args.init()?; 17 | 18 | println!("Start Erase Full Chip ..."); 19 | let start_time = SystemTime::now(); 20 | 21 | device.erase_full()?; 22 | 23 | let take_time = start_time.elapsed().unwrap().as_millis(); 24 | let take_time = Duration::from_millis(take_time as u64); 25 | println!("Done, Take time: {}", humantime::format_duration(take_time)); 26 | 27 | Ok(()) 28 | } 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CH347-RS 2 | 3 | > **Note: Not ready yet, please wait** 4 | 5 |

6 | 7 | CH347 driver library in Rust 8 | 9 | [![made-with-rust](https://img.shields.io/badge/Made%20with-Rust-red.svg)](https://www.rust-lang.org/) 10 | [![crates.io](https://img.shields.io/crates/l/ch347-rs.svg)](https://crates.io/crates/ch347_rs) 11 | [![crates.io](https://img.shields.io/crates/d/ch347-rs.svg)](https://crates.io/crates/ch347_rs) 12 | [![crates.io](https://img.shields.io/crates/v/ch347-rs.svg)](https://crates.io/crates/ch347_rs) 13 | [![crates.io](https://img.shields.io/badge/docs.rs-latest-blue)](https://docs.rs/ch347_rs/latest/ch347_rs/) 14 | 15 |

16 | 17 | # Install ch347tool 18 | 19 | ```bash 20 | > cargo install ch347_rs 21 | > ch347_tool -h 22 | ``` 23 | 24 | # Import 25 | 26 | ```bash 27 | cargo add ch347_rs 28 | ``` 29 | 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 ma jia cong 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/bin/ch347tool/main.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | 3 | mod gpio; 4 | mod i2c; 5 | mod list; 6 | mod spi_flash; 7 | 8 | use clap::{Parser, Subcommand}; 9 | use shadow_rs::shadow; 10 | 11 | shadow!(build); 12 | 13 | #[derive(Parser)] 14 | #[clap(author, version, about=build::ABOUT_MESSABE, long_about = None)] 15 | struct Cli { 16 | #[clap(subcommand)] 17 | command: Commands, 18 | } 19 | 20 | #[derive(Subcommand)] 21 | enum Commands { 22 | List(list::CmdListDevice), 23 | Info, 24 | Spi, 25 | SpiFlash(spi_flash::CmdSpiFlash), 26 | I2cDetect(i2c::CmdI2cDetect), 27 | I2cDump(i2c::CmdI2cDump), 28 | Gpio(gpio::CmdGpio), 29 | } 30 | 31 | fn main() -> Result<(), Box> { 32 | let cli = Cli::parse(); 33 | match &cli.command { 34 | Commands::List(args) => list::cli_list_device(args), 35 | Commands::Gpio(args) => gpio::cli_operator_gpio(args), 36 | Commands::I2cDetect(args) => i2c::cli_i2c_detect(args), 37 | Commands::I2cDump(args) => i2c::cli_i2c_dump(args), 38 | Commands::SpiFlash(args) => spi_flash::cli_spi_flash(args)?, 39 | _ => { 40 | return Err("undefined command".into()); 41 | } 42 | } 43 | 44 | Ok(()) 45 | } 46 | -------------------------------------------------------------------------------- /examples/enum_devices.rs: -------------------------------------------------------------------------------- 1 | // use std::io::{stdin, stdout, Read, Write}; 2 | 3 | use ch347_rs; 4 | 5 | fn main() { 6 | ch347_rs::set_notify_callback(0, "USB\\VID_1A86&PID_55D\0", |status| { 7 | println!("[notify_callback] {:?}", status); 8 | }); 9 | 10 | let a = ch347_rs::enum_ch347_device(); 11 | for (k, v) in a.iter().enumerate() { 12 | let v = match v.get_raw_info() { 13 | None => continue, 14 | Some(a) => a, 15 | }; 16 | 17 | // println!( 18 | // "#{} => {:?} {:?}\r\n\tdevice_id:{}\r\n\tdevice_path:{}\r\n\trpoduct_string:{}\r\n\tget_manufacturer_string:{}\r\n\tfunc_desc_str:{}", 19 | // k, 20 | // v.get_func_type(), 21 | // v.get_usb_class(), 22 | // v.get_device_id(), 23 | // v.get_device_path(), 24 | // v.get_rpoduct_string(), 25 | // v.get_manufacturer_string(), 26 | // v.get_func_desc_str(), 27 | // ); 28 | 29 | println!("#{} => {:?} {:?}", k, v.get_func_type(), v.get_usb_class()); 30 | println!(" device_id: {}", v.get_device_id()); 31 | println!(" device_path: {}", v.get_device_path()); 32 | println!(" rpoduct_string: {}", v.get_rpoduct_string()); 33 | println!(" get_manufacturer_string: {}", v.get_manufacturer_string()); 34 | println!(" func_desc_str: {}", v.get_func_desc_str()); 35 | } 36 | 37 | // let mut stdout = stdout(); 38 | // stdout.write(b"Press Enter to continue...").unwrap(); 39 | // stdout.flush().unwrap(); 40 | // stdin().read(&mut [0]).unwrap(); 41 | 42 | // println!("Exit"); 43 | } 44 | -------------------------------------------------------------------------------- /src/spi_flash/model/eon_silicon.rs: -------------------------------------------------------------------------------- 1 | use super::{Capacity, Chip, RegReadRet, Register, Vendor}; 2 | 3 | pub fn parse_jedec_id(vendor: &'static Vendor, data: (u8, u8)) -> Option { 4 | let memory_type = data.0; 5 | let capacity = data.1; 6 | // println!("{:02X} {:02X}", memory_type, capacity); 7 | 8 | match memory_type { 9 | 0x30 => match capacity { 10 | 0x16 => Some(Chip { 11 | name: "EN25Q32C".to_string(), 12 | vendor, 13 | capacity: Capacity::C32, 14 | }), 15 | _ => None, 16 | }, 17 | _ => None, 18 | } 19 | } 20 | 21 | pub const REGISTER_DEFINES: [Register; 2] = [ 22 | Register { 23 | name: "status", 24 | addr: 0x05, 25 | reader: |spi_flash| -> Result { 26 | let mut buf: [u8; 2] = [0x05, 0x00]; 27 | 28 | if let Err(_) = spi_flash.drive.transfer(&mut buf) { 29 | return Err("transfer fail"); 30 | } 31 | 32 | Ok(RegReadRet::One(buf[1])) 33 | }, 34 | writer: None, 35 | items: None, 36 | }, 37 | Register { 38 | name: "unique_id", 39 | addr: 0x5A, 40 | reader: |spi_flash| -> Result { 41 | const UID_BITS: usize = 96; 42 | let mut wbuf: [u8; 5 + UID_BITS / 8] = [0; 5 + UID_BITS / 8]; 43 | wbuf[0] = 0x5A; 44 | wbuf[1] = 0x00; 45 | wbuf[2] = 0x00; 46 | wbuf[3] = 0x80; 47 | 48 | if let Err(e) = spi_flash.drive.transfer(&mut wbuf) { 49 | return Err(e); 50 | } 51 | 52 | return Ok(RegReadRet::Muti(wbuf[5..wbuf.len()].to_vec())); 53 | }, 54 | writer: None, 55 | items: None, 56 | }, 57 | ]; 58 | -------------------------------------------------------------------------------- /src/bin/ch347tool/spi_flash/read.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | error::Error, 3 | fmt::Write, 4 | fs, 5 | time::{Duration, SystemTime}, 6 | }; 7 | 8 | use clap::Parser; 9 | use indicatif::{ProgressBar, ProgressState, ProgressStyle}; 10 | 11 | use super::utils::format_byte_per_sec; 12 | 13 | #[derive(Parser, Clone, Debug)] 14 | #[clap(about = "Read spi flash chip")] 15 | pub struct CmdSpiFlashRead { 16 | /// output to file 17 | #[clap(value_parser)] 18 | file: String, 19 | } 20 | 21 | pub fn cli_spi_flash_read( 22 | flash_args: &super::CmdSpiFlash, 23 | args: &CmdSpiFlashRead, 24 | ) -> Result<(), Box> { 25 | let (device, chip_info) = flash_args.init()?; 26 | 27 | let chip_capacity: usize = chip_info.capacity.into(); 28 | 29 | let mut all_buf: Vec = Vec::new(); 30 | let pb = ProgressBar::new(chip_capacity as u64); 31 | pb.set_style(ProgressStyle::with_template("{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({binary_bytes_per_sec}) ({eta})") 32 | .unwrap() 33 | .with_key("eta", |state: &ProgressState, w: &mut dyn Write| write!(w, "{:.1}s", state.eta().as_secs_f64()).unwrap()) 34 | .progress_chars("#>-")); 35 | 36 | println!("Reading ..."); 37 | let start_time = SystemTime::now(); 38 | 39 | const BLOCK_SIZE: usize = 4096; 40 | 41 | for i in 0..(chip_capacity / BLOCK_SIZE) { 42 | let mut rbuf: [u8; BLOCK_SIZE] = [0; BLOCK_SIZE]; 43 | device.read((i * BLOCK_SIZE) as u32, &mut rbuf); 44 | all_buf.extend_from_slice(&rbuf); 45 | pb.set_position((i * BLOCK_SIZE) as u64); 46 | } 47 | let take_time = start_time.elapsed().unwrap().as_millis(); 48 | let take_time = Duration::from_millis(take_time as u64); 49 | pb.finish_and_clear(); 50 | fs::write(args.file.as_str(), &all_buf)?; 51 | 52 | println!("Done, Take time: {}", humantime::format_duration(take_time)); 53 | let speed = (all_buf.len() as f64) / take_time.as_secs_f64(); 54 | println!("{}", format_byte_per_sec(speed)); 55 | 56 | Ok(()) 57 | } 58 | -------------------------------------------------------------------------------- /src/windows/basetsd.rs: -------------------------------------------------------------------------------- 1 | use libc; 2 | 3 | // see https://docs.microsoft.com/en-us/windows/win32/winprog/windows-data-types 4 | 5 | /// windows filesystem path limit 6 | /// 7 | /// 8 | #[allow(dead_code)] 9 | pub const MAX_PATH: usize = 260; 10 | 11 | /// A Boolean variable (should be TRUE or FALSE). 12 | /// 13 | /// This type is declared in `WinDef.h` as follows: 14 | /// ```c 15 | /// typedef int BOOL; 16 | /// ``` 17 | #[allow(dead_code)] 18 | pub type BOOL = libc::c_int; 19 | 20 | /// An 8-bit Windows (ANSI) character. For more information, see Character Sets Used By Fonts. 21 | /// 22 | /// This type is declared in `WinNT.h` as follows: 23 | /// ```c 24 | /// typedef char CHAR; 25 | /// ``` 26 | #[allow(dead_code)] 27 | pub type CHAR = libc::c_char; 28 | 29 | /// An unsigned CHAR. 30 | /// 31 | /// This type is declared in `WinDef.h` as follows: 32 | /// ```c 33 | /// typedef unsigned char UCHAR; 34 | /// ``` 35 | #[allow(dead_code)] 36 | pub type UCHAR = libc::c_uchar; 37 | 38 | #[allow(dead_code)] 39 | pub type PCHAR = *mut libc::c_char; 40 | 41 | #[allow(dead_code)] 42 | pub type PUCHAR = *mut libc::c_uchar; 43 | 44 | #[allow(dead_code)] 45 | pub type USHORT = libc::c_ushort; 46 | 47 | #[allow(dead_code)] 48 | pub type SHORT = libc::c_short; 49 | 50 | #[allow(dead_code)] 51 | pub type ULONG = libc::c_ulong; 52 | 53 | #[allow(dead_code)] 54 | pub type PULONG = *mut ULONG; 55 | 56 | #[allow(dead_code)] 57 | pub type LONG = libc::c_long; 58 | 59 | #[allow(dead_code)] 60 | pub type VOID = libc::c_void; 61 | 62 | #[allow(dead_code)] 63 | pub type PVOID = *mut VOID; 64 | 65 | /// A handle to an object. 66 | /// 67 | /// This type is declared in WinNT.h as follows: 68 | /// ```c 69 | /// typedef PVOID HANDLE; 70 | /// ``` 71 | #[allow(dead_code)] 72 | pub type HANDLE = PVOID; 73 | 74 | /// The calling convention for callback functions. 75 | /// 76 | /// This type is declared in WinDef.h as follows: 77 | /// ```c 78 | /// #define CALLBACK __stdcall 79 | /// ``` 80 | /// 81 | /// CALLBACK, WINAPI, and APIENTRY are all used to define functions with the __stdcall calling convention. 82 | /// 83 | /// Most functions in the Windows API are declared using WINAPI. You may wish to use CALLBACK for the callback functions that you implement to help identify the function as a callback function. 84 | #[allow(dead_code)] 85 | pub type CALLBACK = fn(); 86 | -------------------------------------------------------------------------------- /src/bin/ch347tool/list.rs: -------------------------------------------------------------------------------- 1 | use ch347_rs::{self, close_device, FuncType, UsbClass}; 2 | use clap::{Parser, ValueEnum}; 3 | use serde::Serialize; 4 | use serde_json; 5 | 6 | #[derive(Parser, Debug)] 7 | #[clap(about = "List all plugged in devices")] 8 | pub struct CmdListDevice { 9 | #[clap(short, long, action)] 10 | #[clap(default_value_t = ListFormat::Tree, value_enum)] 11 | pub format: ListFormat, 12 | } 13 | 14 | #[derive(ValueEnum, Clone, Debug)] 15 | pub enum ListFormat { 16 | Tree, 17 | Json, 18 | } 19 | 20 | #[derive(Serialize)] 21 | struct DeviceInfo { 22 | index: u8, 23 | name: String, 24 | func_type: String, 25 | usb_id: String, 26 | usb_class: String, 27 | } 28 | 29 | impl DeviceInfo { 30 | fn from_base_info(i: ch347_rs::DeviceInfo) -> DeviceInfo { 31 | DeviceInfo { 32 | index: i.index, 33 | name: i.get_func_desc_str(), 34 | func_type: match i.get_func_type() { 35 | FuncType::Uart => String::from("UART"), 36 | FuncType::SpiI2c => String::from("SPI & I2C & GPIO"), 37 | FuncType::JtagI2c => String::from("JTAG & I2C"), 38 | }, 39 | usb_id: i.get_device_id(), 40 | usb_class: match i.get_usb_class() { 41 | UsbClass::Ch341 => String::from("ch341"), 42 | UsbClass::Hid => String::from("hid"), 43 | UsbClass::Vcp => String::from("vcp"), 44 | }, 45 | } 46 | } 47 | } 48 | 49 | impl Into for ch347_rs::DeviceInfo { 50 | fn into(self) -> DeviceInfo { 51 | DeviceInfo::from_base_info(self) 52 | } 53 | } 54 | 55 | pub fn cli_list_device(args: &CmdListDevice) { 56 | let mut l: Vec = Vec::new(); 57 | 58 | for i in ch347_rs::enum_ch347_device() { 59 | if let Some(info) = i.get_raw_info() { 60 | l.push(info.into()); 61 | } 62 | } 63 | 64 | for i in &l { 65 | close_device(i.index as u32); 66 | } 67 | 68 | match args.format { 69 | ListFormat::Tree => { 70 | println!("'Ch347 device list:"); 71 | 72 | for i in l { 73 | println!("{}# {}", i.index, i.name) 74 | } 75 | } 76 | ListFormat::Json => { 77 | let j = serde_json::to_string_pretty(&l); 78 | 79 | match j { 80 | Ok(j) => { 81 | println!("{}", j); 82 | } 83 | Err(e) => { 84 | println!("{}", e); 85 | } 86 | } 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/bin/ch347tool/spi_flash/check.rs: -------------------------------------------------------------------------------- 1 | use std::{cmp, error::Error, fmt::Write, fs}; 2 | 3 | use clap::Parser; 4 | use indicatif::{ProgressBar, ProgressState, ProgressStyle}; 5 | 6 | #[derive(Parser, Clone, Debug)] 7 | #[clap(about = "Check spi flash chip memory")] 8 | pub struct CmdSpiFlashCheck { 9 | /// output to file 10 | #[clap(value_parser)] 11 | file: String, 12 | } 13 | 14 | pub fn cli_spi_flash_check( 15 | flash_args: &super::CmdSpiFlash, 16 | args: &CmdSpiFlashCheck, 17 | ) -> Result<(), Box> { 18 | let file_buf = fs::read(args.file.as_str())?; 19 | let (device, chip_info) = flash_args.init()?; 20 | 21 | let wsize = cmp::min(file_buf.len(), chip_info.capacity.into()); 22 | 23 | // let mut all_buf: Vec = Vec::new(); 24 | let pb = ProgressBar::new(wsize as u64); 25 | pb.set_style(ProgressStyle::with_template("{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({binary_bytes_per_sec}) ({eta})") 26 | .unwrap() 27 | .with_key("eta", |state: &ProgressState, w: &mut dyn Write| write!(w, "{:.1}s", state.eta().as_secs_f64()).unwrap()) 28 | .progress_chars("#>-")); 29 | 30 | println!("Checking..."); 31 | const BLOCK_SIZE: usize = 4096; 32 | for i in (0..wsize).step_by(BLOCK_SIZE) { 33 | if (wsize - i) >= BLOCK_SIZE { 34 | let mut rbuf: [u8; BLOCK_SIZE] = [0; BLOCK_SIZE]; 35 | device.read(i as u32, &mut rbuf); 36 | // all_buf.extend_from_slice(&rbuf); 37 | for x in 0..BLOCK_SIZE { 38 | if rbuf[x] != file_buf[i + x] { 39 | pb.finish_and_clear(); 40 | return Err(format!( 41 | "diff 0x{:04X}_{:04X} {:02X} => {:02X}", 42 | (i + x) >> 16, 43 | (i + x) & 0xFFFF, 44 | file_buf[i + x], 45 | rbuf[x] 46 | ) 47 | .into()); 48 | } 49 | } 50 | } else { 51 | let mut rbuf: Vec = Vec::new(); 52 | for _ in 0..(wsize - i) { 53 | rbuf.push(0x00); 54 | } 55 | device.read(i as u32, &mut rbuf); 56 | for x in 0..rbuf.len() { 57 | if rbuf[x] != file_buf[i + x] { 58 | pb.finish_and_clear(); 59 | return Err(format!( 60 | "diff 0x{:04X}_{:04X} {:02X} => {:02X}", 61 | (i + x) >> 16, 62 | (i + x) & 0xFFFF, 63 | file_buf[i + x], 64 | rbuf[x] 65 | ) 66 | .into()); 67 | } 68 | } 69 | } 70 | 71 | pb.set_position(i as u64); 72 | // pb.inc(BLOCK_SIZE as u64); 73 | } 74 | pb.finish(); 75 | 76 | Ok(()) 77 | } 78 | -------------------------------------------------------------------------------- /src/bin/ch347tool/spi_flash/mod.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | 3 | use clap::{Parser, Subcommand, ValueEnum}; 4 | 5 | mod utils; 6 | 7 | mod check; 8 | mod detect; 9 | mod erase; 10 | mod read; 11 | mod reg; 12 | mod write; 13 | 14 | #[derive(Parser, Debug)] 15 | #[clap(about = "Operate spi flash chip")] 16 | pub struct CmdSpiFlash { 17 | /// device number 18 | #[clap(value_parser, default_value_t = 0)] 19 | index: u32, 20 | 21 | /// chip select 22 | #[clap(value_enum, value_parser,default_value_t=CS::CS0)] 23 | cs: CS, 24 | 25 | /// clock freq, 0=60MHz 1=30MHz 2=15MHz 3=7.5MHz 4=3.75MHz 5=1.875MHz 6=937.5KHz 7=468.75KHz 26 | #[clap(short, long, value_parser, default_value_t = 2)] 27 | freq: u8, 28 | 29 | #[clap(subcommand)] 30 | command: Commands, 31 | } 32 | 33 | #[derive(ValueEnum, Subcommand, Clone, Debug)] 34 | enum CS { 35 | CS0, 36 | CS1, 37 | } 38 | 39 | #[derive(Subcommand, Debug)] 40 | pub enum Commands { 41 | Detect(detect::CmdSpiFlashDetect), 42 | Erase(erase::CmdSpiFlashErase), 43 | Write(write::CmdSpiFlashWrite), 44 | Read(read::CmdSpiFlashRead), 45 | Check(check::CmdSpiFlashCheck), 46 | Reg(reg::CmdReg), 47 | } 48 | 49 | impl CmdSpiFlash { 50 | pub fn init( 51 | &self, 52 | ) -> Result<(ch347_rs::SpiFlash, ch347_rs::Chip), Box> { 53 | let clock_level = match ch347_rs::SpiClockLevel::from_byte(self.freq) { 54 | None => { 55 | return Err(format!("Unknow SPI clock level: {}", self.freq).into()); 56 | } 57 | Some(level) => level, 58 | }; 59 | println!("Select SPI Clock: {}", clock_level); 60 | 61 | let mut device = ch347_rs::Ch347Device::new(self.index)?; 62 | device.change_spi_raw_config(|spi_cfg| { 63 | spi_cfg.byte_order = 1; 64 | spi_cfg.clock = self.freq; 65 | })?; 66 | let device = device.spi_flash()?; 67 | 68 | let chip_info = match device.detect() { 69 | Err(e) => return Err(e.into()), 70 | Ok(chip_info) => chip_info, 71 | }; 72 | 73 | let unique_id = match device.read_uuid(chip_info.vendor) { 74 | Err(e) => format!("{}: {}", console::style("error").red(), e), 75 | Ok(chip_uuid) => format!("{} Bit {:02X?}", chip_uuid.len() * 8, chip_uuid), 76 | }; 77 | 78 | println!("ChipInfo:"); 79 | println!(" Manufacturer: {}", chip_info.vendor.name); 80 | println!(" Name: {}", chip_info.name); 81 | println!(" Capacity: {}", chip_info.capacity); 82 | println!(" UID: {}", unique_id); 83 | 84 | Ok((device, chip_info)) 85 | } 86 | } 87 | 88 | pub fn cli_spi_flash(args: &CmdSpiFlash) -> Result<(), Box> { 89 | match &args.command { 90 | Commands::Detect(sub_args) => detect::cli_spi_flash_detect(args, sub_args)?, 91 | Commands::Erase(sub_args) => erase::cli_spi_flash_erase(args, sub_args)?, 92 | Commands::Write(sub_args) => write::cli_spi_flash_write(args, sub_args)?, 93 | Commands::Read(sub_args) => read::cli_spi_flash_read(args, sub_args)?, 94 | Commands::Check(sub_args) => check::cli_spi_flash_check(args, sub_args)?, 95 | Commands::Reg(sub_args) => reg::cli_main(args, sub_args)?, 96 | }; 97 | 98 | Ok(()) 99 | } 100 | -------------------------------------------------------------------------------- /src/bin/ch347tool/spi_flash/utils.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | 3 | pub fn format_byte_unit<'a>(a: usize) -> String { 4 | let mut ret = String::new(); 5 | 6 | if a < 1024 { 7 | ret = format!("{}B", a) 8 | } else if a < 1024 * 1024 { 9 | ret.push_str(&format!("{}KB", a / 1024)); 10 | 11 | if (a % 1024) != 0 { 12 | ret.push_str(" "); 13 | ret.push_str(&format_byte_unit(a % 1024)); 14 | } 15 | } else { 16 | ret.push_str(&format!("{}MB", a / (1024 * 1024))); 17 | 18 | if (a % (1024 * 1024)) != 0 { 19 | ret.push_str(" "); 20 | ret.push_str(&format_byte_unit(a % (1024 * 1024))); 21 | } 22 | } 23 | 24 | return ret; 25 | } 26 | 27 | pub fn format_byte_per_sec(a: f64) -> String { 28 | if a < (1024.0) { 29 | format!("{:.2} B/S ", a) 30 | } else if a < (1024.0 * 1024.0) { 31 | format!("{:.2} KB/S ", a / 1024.0) 32 | } else { 33 | format!("{:.2} MB/S ", a / 1024.0 / 1024.0) 34 | } 35 | } 36 | 37 | pub enum FindRegType<'a> { 38 | Reg(&'a ch347_rs::Register), 39 | RegItem(&'a ch347_rs::Register, usize), 40 | } 41 | 42 | pub fn find_reg_by_name<'a>( 43 | name: &str, 44 | reg_defines: &'a [ch347_rs::Register], 45 | ) -> Option> { 46 | let name = &name.to_lowercase(); 47 | 48 | // search register name 49 | for r in reg_defines { 50 | if name.eq(&r.name.to_lowercase()) { 51 | return Some(FindRegType::Reg(r)); 52 | } 53 | } 54 | 55 | // search register item name 56 | for r in reg_defines { 57 | let r_items = match r.items { 58 | None => continue, 59 | Some(a) => a, 60 | }; 61 | 62 | for (k, ri) in r_items.iter().enumerate() { 63 | if name.eq(&ri.name.to_lowercase()) { 64 | return Some(FindRegType::RegItem(r, k)); 65 | } 66 | } 67 | } 68 | 69 | // search register item alias 70 | for r in reg_defines { 71 | let r_items = match r.items { 72 | None => continue, 73 | Some(a) => a, 74 | }; 75 | 76 | for (k, ri) in r_items.iter().enumerate() { 77 | for &ria in ri.alias { 78 | if name.eq(&ria.to_lowercase()) { 79 | return Some(FindRegType::RegItem(r, k)); 80 | } 81 | } 82 | } 83 | } 84 | 85 | None 86 | } 87 | 88 | pub fn parse_cli_arg_number(input: &str, is_bool: bool) -> Result> { 89 | let input = input.to_lowercase(); 90 | 91 | if is_bool { 92 | if input.eq("true") || input.eq("t") { 93 | return Ok(1); 94 | } else if input.eq("false") || input.eq("f") { 95 | return Ok(0); 96 | } 97 | } 98 | 99 | let ret = if input.starts_with("0b") { 100 | let input_str = input.trim_start_matches("0b"); 101 | let input_str = input_str.replace("-", ""); 102 | 103 | u8::from_str_radix(&input_str, 2)? 104 | } else if input.starts_with("0x") { 105 | let input_str = input.trim_start_matches("0x"); 106 | 107 | u8::from_str_radix(input_str, 16)? 108 | } else { 109 | return Err("Cannot parse input value".into()); 110 | }; 111 | 112 | Ok(ret) 113 | } 114 | 115 | pub fn display_bool_with_color(v: bool) -> String { 116 | if v { 117 | console::style("True").green() 118 | } else { 119 | console::style("False").red() 120 | } 121 | .to_string() 122 | } 123 | 124 | pub fn display_u8_hex(a: u8) -> String { 125 | format!("0x{:02X}(0b{:04b}_{:04b})", a, a >> 4, a & 0x0F) 126 | } 127 | -------------------------------------------------------------------------------- /src/spi_flash/model/macronix.rs: -------------------------------------------------------------------------------- 1 | use super::{Capacity, Chip, RegReadRet, Register, RegisterAccess, RegisterItem, Vendor}; 2 | 3 | pub fn parse_jedec_id(vendor: &'static Vendor, data: (u8, u8)) -> Option { 4 | let memory_type = data.0; 5 | let capacity = data.1; 6 | // println!("{:02X} {:02X}", memory_type, capacity); 7 | 8 | match memory_type { 9 | 0x20 => match capacity { 10 | 0x17 => Some(Chip { 11 | name: "MX25L64".to_string(), 12 | vendor, 13 | capacity: Capacity::C64, 14 | }), 15 | 0x19 => Some(Chip { 16 | name: "MX25L256".to_string(), 17 | vendor, 18 | capacity: Capacity::C256, 19 | }), 20 | _ => None, 21 | }, 22 | _ => None, 23 | } 24 | } 25 | 26 | pub const REGISTER_DEFINES: [Register; 2] = [ 27 | Register { 28 | name: "status", 29 | addr: 0x05, 30 | reader: |spi_flash| -> Result { 31 | let mut buf: [u8; 2] = [0x05, 0x00]; 32 | 33 | spi_flash.drive.transfer(&mut buf)?; 34 | 35 | Ok(RegReadRet::One(buf[1])) 36 | }, 37 | writer: None, 38 | items: Some(&[ 39 | RegisterItem { 40 | name: "busy", 41 | alias: &["WIP"], 42 | describe: "write in progress bit", 43 | offset: 0, 44 | width: 1, 45 | access: RegisterAccess::ReadOnly, 46 | }, 47 | RegisterItem { 48 | name: "write_enable", 49 | alias: &["WE", "WEL"], 50 | describe: "write enable latch", 51 | offset: 1, 52 | width: 1, 53 | access: RegisterAccess::ReadOnly, 54 | }, 55 | RegisterItem { 56 | name: "block_protect", 57 | alias: &["BP"], 58 | describe: "level of protected block", 59 | offset: 2, 60 | width: 4, 61 | access: RegisterAccess::ReadOnly, 62 | }, 63 | RegisterItem { 64 | name: "quad_enable", 65 | alias: &["QE"], 66 | describe: "Quad Enable", 67 | offset: 6, 68 | width: 1, 69 | access: RegisterAccess::ReadOnly, 70 | }, 71 | RegisterItem { 72 | name: "sreg_w_prot", 73 | alias: &["SRWD"], 74 | describe: "status register write protect", 75 | offset: 7, 76 | width: 1, 77 | access: RegisterAccess::ReadOnly, 78 | }, 79 | ]), 80 | }, 81 | Register { 82 | name: "config", 83 | addr: 0x15, 84 | reader: |spi_flash| -> Result { 85 | let mut buf: [u8; 2] = [0x15, 0x00]; 86 | 87 | spi_flash.drive.transfer(&mut buf)?; 88 | 89 | Ok(RegReadRet::One(buf[1])) 90 | }, 91 | writer: None, 92 | items: Some(&[ 93 | RegisterItem { 94 | name: "ODS", 95 | alias: &["ODS"], 96 | describe: "output driver strength", 97 | offset: 0, 98 | width: 2, 99 | access: RegisterAccess::ReadOnly, 100 | }, 101 | RegisterItem { 102 | name: "tb", 103 | alias: &["TB"], 104 | describe: "top/bottom selected", 105 | offset: 3, 106 | width: 1, 107 | access: RegisterAccess::ReadOnly, 108 | }, 109 | RegisterItem { 110 | name: "tb_enable", 111 | alias: &["TBE"], 112 | describe: "Preamble bit Enable", 113 | offset: 4, 114 | width: 1, 115 | access: RegisterAccess::ReadOnly, 116 | }, 117 | RegisterItem { 118 | name: "addr_mode", 119 | alias: &["EN4B"], 120 | describe: "", 121 | offset: 5, 122 | width: 1, 123 | access: RegisterAccess::ReadOnly, 124 | }, 125 | ]), 126 | }, 127 | ]; 128 | -------------------------------------------------------------------------------- /src/spi_flash/model/mod.rs: -------------------------------------------------------------------------------- 1 | mod eon_silicon; 2 | mod gigadevice; 3 | mod macronix; 4 | mod winbond; 5 | 6 | use std::fmt; 7 | 8 | use super::{RegReadRet, Register, RegisterAccess, RegisterItem, SpiDrive, SpiFlash}; 9 | 10 | type JedecIdParser = fn(vendor: &'static Vendor, data: (u8, u8)) -> Option; 11 | 12 | // #[derive(Debug)] 13 | pub struct Vendor { 14 | pub name: &'static str, 15 | pub id: u8, 16 | pub parser: JedecIdParser, 17 | pub reg_defines: Option<&'static [Register]>, 18 | } 19 | 20 | impl Vendor { 21 | pub fn check_support_uid(&self) -> Result<&'static Register, &'static str> { 22 | if let None = self.reg_defines { 23 | return Err("Not define Registers"); 24 | } 25 | 26 | let result = self 27 | .reg_defines 28 | .unwrap() 29 | .iter() 30 | .find(|item| item.name.eq("unique_id")); 31 | 32 | let result = match result { 33 | None => return Err("Not support UniqueID"), 34 | Some(a) => a, 35 | }; 36 | 37 | Ok(result) 38 | } 39 | 40 | pub fn read_uid(&self, spi_flash: &SpiFlash) -> Result, &'static str> { 41 | let uid_reg = self.check_support_uid()?; 42 | 43 | let result = (uid_reg.reader)(spi_flash)?; 44 | 45 | if let RegReadRet::Muti(buf) = result { 46 | return Ok(buf); 47 | } 48 | 49 | panic!(); 50 | } 51 | } 52 | 53 | #[derive(Debug, PartialEq, Eq)] 54 | pub enum Capacity { 55 | C05, 56 | C10, 57 | C20, 58 | C40, 59 | C80, 60 | C16, 61 | C32, 62 | C64, 63 | C128, 64 | C256, 65 | } 66 | 67 | impl Into for Capacity { 68 | fn into(self) -> usize { 69 | match self { 70 | Capacity::C05 => 1024 * 64, 71 | Capacity::C10 => 1024 * 128, 72 | Capacity::C20 => 1024 * 256, 73 | Capacity::C40 => 1024 * 512, 74 | Capacity::C80 => 1024 * 1024 * 1, 75 | Capacity::C16 => 1024 * 1024 * 2, 76 | Capacity::C32 => 1024 * 1024 * 4, 77 | Capacity::C64 => 1024 * 1024 * 8, 78 | Capacity::C128 => 1024 * 1024 * 16, 79 | Capacity::C256 => 1024 * 1024 * 32, 80 | } 81 | } 82 | } 83 | 84 | impl fmt::Display for Capacity { 85 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 86 | write!( 87 | f, 88 | "{}", 89 | match self { 90 | Capacity::C05 => "64 KB", 91 | Capacity::C10 => "128 KB", 92 | Capacity::C20 => "256 KB", 93 | Capacity::C40 => "512 KB", 94 | Capacity::C80 => "1 MB", 95 | Capacity::C16 => "2 MB", 96 | Capacity::C32 => "4 MB", 97 | Capacity::C64 => "8 MB", 98 | Capacity::C128 => "16 MB", 99 | Capacity::C256 => "32 MB", 100 | } 101 | ) 102 | } 103 | } 104 | 105 | // #[derive(Debug)] 106 | pub struct Chip { 107 | pub name: String, 108 | pub vendor: &'static Vendor, 109 | pub capacity: Capacity, 110 | } 111 | 112 | const JEDEC_ID_LIST: [Vendor; 4] = [ 113 | Vendor { 114 | name: "Eon Silicon", 115 | id: 0x1C, 116 | parser: eon_silicon::parse_jedec_id, 117 | reg_defines: Some(&eon_silicon::REGISTER_DEFINES), 118 | }, 119 | Vendor { 120 | name: "GigaDevice", 121 | id: 0xC8, 122 | parser: gigadevice::parse_jedec_id, 123 | reg_defines: Some(&gigadevice::REGISTER_DEFINES), 124 | }, 125 | Vendor { 126 | name: "Macronix (MX)", 127 | id: 0xC2, 128 | parser: macronix::parse_jedec_id, 129 | reg_defines: Some(¯onix::REGISTER_DEFINES), 130 | }, 131 | Vendor { 132 | name: "Winbond (ex Nexcom)", 133 | id: 0xEF, 134 | parser: winbond::parse_jedec_id, 135 | reg_defines: Some(&winbond::REGISTER_DEFINES), 136 | }, 137 | ]; 138 | 139 | #[test] 140 | pub fn test_parse_jedec_id() { 141 | assert!(parse_jedec_id(&[0xFF, 0xFF, 0xFF]).is_none()); 142 | assert!(parse_jedec_id(&[0x00, 0x00, 0x00]).is_none()); 143 | assert!(parse_jedec_id(&[]).is_none()); 144 | } 145 | 146 | pub fn parse_jedec_id(buf: &[u8]) -> Option { 147 | if buf.len() < 3 { 148 | return None; 149 | } 150 | 151 | let vendor = JEDEC_ID_LIST.iter().find(|&i| i.id == buf[0])?; 152 | 153 | (vendor.parser)(vendor, (buf[1], buf[2])) 154 | } 155 | -------------------------------------------------------------------------------- /src/bin/ch347tool/gpio.rs: -------------------------------------------------------------------------------- 1 | use clap::{Parser, Subcommand}; 2 | use std::thread::sleep; 3 | use std::time::Duration; 4 | 5 | #[derive(Parser, Debug)] 6 | #[clap(about = "Operate gpio")] 7 | pub struct CmdGpio { 8 | /// device number 9 | #[clap(value_parser)] 10 | index: u32, 11 | 12 | /// gpio mask, eg. hex: 0xFF or FFH dec:64 bin:0b0000_0011 13 | #[clap(value_parser)] 14 | gpio_mask: String, 15 | 16 | #[clap(subcommand, value_enum)] 17 | command: Commands, 18 | } 19 | 20 | #[derive(Parser, Clone, Debug)] 21 | #[clap(about = "output pwm")] 22 | pub struct SubCmdPWM { 23 | /// unit: Hz 24 | #[clap(default_value_t = 50)] 25 | freq: u32, 26 | 27 | /// range 0~1, eg. 0.5(50%), 0.25(25%) 28 | #[clap(default_value_t = 0.5)] 29 | duty: f32, 30 | } 31 | 32 | // #[derive(ValueEnum, Subcommand, Clone, Debug)] 33 | #[derive(Subcommand, Clone, Debug)] 34 | pub enum Commands { 35 | Status, 36 | Pwm(SubCmdPWM), 37 | High, 38 | Low, 39 | Read, 40 | } 41 | 42 | fn parse_gpio_dir(a: u8, bit: u8) -> &'static str { 43 | if a & (1 << bit) != 0 { 44 | return "Out"; 45 | } 46 | "In" 47 | } 48 | 49 | fn parse_gpio_data(a: u8, bit: u8) -> &'static str { 50 | if a & (1 << bit) != 0 { 51 | return "High"; 52 | } 53 | "Low" 54 | } 55 | 56 | pub fn cli_operator_gpio(args: &CmdGpio) { 57 | println!("Select device index: {}", args.index); 58 | println!("Select gpio mask: {}", args.gpio_mask); 59 | match &args.command { 60 | Commands::Status => { 61 | let dev = ch347_rs::Ch347Device::new(args.index).expect("error opening device"); 62 | 63 | let (gpio_dir, gpio_data) = 64 | ch347_rs::gpio_get(dev.get_dev_index()).expect("GPIO status error"); 65 | println!("Dir: 0x{:02X} Data: 0x{:02X}", gpio_dir, gpio_data); 66 | 67 | for i in 0..=7 { 68 | println!( 69 | "GPIO{} {} {}", 70 | i, 71 | parse_gpio_dir(gpio_dir, i), 72 | parse_gpio_data(gpio_data, i), 73 | ); 74 | } 75 | } 76 | Commands::Pwm(sub_cmd) => { 77 | let mask = args.gpio_mask.parse::().unwrap(); 78 | let dev = ch347_rs::Ch347Device::new(args.index).expect("error opening device"); 79 | let (curr_dir, curr_data) = ch347_rs::gpio_get(dev.get_dev_index()).unwrap(); 80 | let dir = curr_dir | mask; 81 | 82 | let freq = sub_cmd.freq as f64; 83 | let duty = sub_cmd.duty as f64; 84 | let on_period = Duration::from_micros((duty * 1_000_000.0 / freq) as u64); 85 | let off_period = Duration::from_micros(((1.0 - duty) * 1_000_000.0 / freq) as u64); 86 | 87 | println!("on_period: {:?}, off_period: {:?}", &on_period, &off_period); 88 | 89 | loop { 90 | let data = curr_data | mask; 91 | let _ = ch347_rs::gpio_set(dev.get_dev_index(), mask, dir, data); 92 | sleep(on_period); 93 | let data = curr_data & !mask; 94 | let _ = ch347_rs::gpio_set(dev.get_dev_index(), mask, dir, data); 95 | sleep(off_period); 96 | } 97 | } 98 | Commands::High => { 99 | let mask = args.gpio_mask.parse::().unwrap(); 100 | let dev = ch347_rs::Ch347Device::new(args.index).expect("error opening device"); 101 | let (curr_dir, curr_data) = ch347_rs::gpio_get(dev.get_dev_index()).unwrap(); 102 | let dir = curr_dir | mask; 103 | let data = curr_data | mask; 104 | let res = ch347_rs::gpio_set(dev.get_dev_index(), mask, dir, data); 105 | println!("gpio set result {:?}", res); 106 | ch347_rs::close_device(args.index); 107 | } 108 | Commands::Low => { 109 | let mask = args.gpio_mask.parse::().unwrap(); 110 | let dev = ch347_rs::Ch347Device::new(args.index).expect("error opening device"); 111 | let (curr_dir, curr_data) = ch347_rs::gpio_get(dev.get_dev_index()).unwrap(); 112 | let dir = curr_dir | mask; 113 | let data = curr_data & !mask; 114 | let res = ch347_rs::gpio_set(dev.get_dev_index(), mask, dir, data); 115 | println!("gpio set result {:?}", res); 116 | ch347_rs::close_device(args.index); 117 | } 118 | Commands::Read => { 119 | let dev = ch347_rs::Ch347Device::new(args.index).expect("error opening device"); 120 | let res = ch347_rs::gpio_get(dev.get_dev_index()); 121 | println!("gpio get {:?}", res); 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/bin/ch347tool/i2c.rs: -------------------------------------------------------------------------------- 1 | use ch347_rs::I2cSpeed; 2 | use clap::{Parser, ValueEnum}; 3 | 4 | #[derive(Parser, Debug)] 5 | #[clap(about = "Detects all device address on the I2C bus")] 6 | pub struct CmdI2cDetect { 7 | /// device number 8 | #[clap(value_parser)] 9 | index: u32, 10 | 11 | /// 20kHz, 100kHz, 400kHz, 750kHz 12 | #[clap(value_parser)] 13 | #[clap(default_value_t = I2cSpeed::Std, value_enum)] 14 | speed_level: I2cSpeed, 15 | } 16 | 17 | pub fn cli_i2c_detect(args: &CmdI2cDetect) { 18 | println!("speed: {}", args.speed_level); 19 | 20 | let dev = match ch347_rs::Ch347Device::new(args.index) { 21 | Ok(a) => a, 22 | Err(e) => { 23 | println!("{}", e); 24 | return; 25 | } 26 | }; 27 | 28 | dev.i2c_set(args.speed_level); 29 | 30 | println!(" 0 1 2 3 4 5 6 7 8 9 A B C D E F"); 31 | 32 | for y in 0..8 { 33 | let mut s = format!("{:02X}:", y * 0x10); 34 | for x in 0..16 { 35 | // skip reserved addresses 36 | if ((y == 0x00) && (x <= 0x2)) || ((y == 0x07) && (x >= 0x08)) { 37 | s.push_str(" "); 38 | continue; 39 | } 40 | 41 | if dev.i2c_device_detect(y * 0x10 + x) { 42 | s.push_str(&format!(" {:02X}", y * 0x10 + x)); 43 | continue; 44 | } 45 | 46 | s.push_str(" --"); 47 | } 48 | println!("{}", s); 49 | } 50 | } 51 | 52 | #[derive(ValueEnum, Clone, Debug)] 53 | enum DumpPage { 54 | Byte, 55 | Word, 56 | Full, 57 | } 58 | 59 | #[derive(Parser, Debug)] 60 | pub struct CmdI2cDump { 61 | /// device number 62 | #[clap(value_parser)] 63 | index: u32, 64 | 65 | /// i2c device addr(shifted), eg. 0x3C 66 | #[clap(value_parser)] 67 | addr: String, 68 | 69 | /// 20kHz, 100kHz, 400kHz, 750kHz 70 | #[clap(value_parser)] 71 | #[clap(default_value_t = I2cSpeed::Std, value_enum)] 72 | speed_level: I2cSpeed, 73 | 74 | #[clap(value_parser)] 75 | #[clap(default_value_t = DumpPage::Byte, value_enum)] 76 | page: DumpPage, 77 | } 78 | 79 | pub fn cli_i2c_dump(args: &CmdI2cDump) { 80 | println!("speed: {}", args.speed_level); 81 | 82 | let device_addr: u8; 83 | if args.addr.starts_with("0x") { 84 | match u8::from_str_radix(args.addr.trim_start_matches("0x"), 16) { 85 | Ok(addr) => device_addr = addr, 86 | Err(err) => { 87 | println!("parse device_addr error: {}", err); 88 | return; 89 | } 90 | } 91 | } else if args.addr.ends_with('H') { 92 | match u8::from_str_radix(args.addr.trim_end_matches('H'), 16) { 93 | Ok(addr) => device_addr = addr, 94 | Err(err) => { 95 | println!("parse device_addr error: {}", err); 96 | return; 97 | } 98 | } 99 | } else { 100 | match str::parse(&args.addr) { 101 | Ok(addr) => device_addr = addr, 102 | Err(err) => { 103 | println!("parse device_addr error: {}", err); 104 | return; 105 | } 106 | } 107 | } 108 | 109 | println!( 110 | "device_addr: 0x{:02X}(w:0x{:02X}, r:0x{:02X})", 111 | device_addr, 112 | device_addr << 1, 113 | (device_addr << 1) + 1 114 | ); 115 | 116 | let dev = match ch347_rs::Ch347Device::new(args.index) { 117 | Ok(a) => a, 118 | Err(e) => { 119 | println!("{}", e); 120 | return; 121 | } 122 | }; 123 | 124 | dev.i2c_set(args.speed_level); 125 | 126 | match args.page { 127 | DumpPage::Byte => { 128 | println!(" 0 1 2 3 4 5 6 7 8 9 A B C D E F"); 129 | for y in 0..16 { 130 | let mut s = format!("{:02X}:", y * 0x10); 131 | for x in 0..16 { 132 | let mut wbuf: [u8; 2] = [ 133 | device_addr << 1, // device addr 134 | y * 0x10 + x, // register addr 135 | ]; 136 | 137 | if let Err(_) = ch347_rs::i2c_stream( 138 | dev.get_dev_index(), 139 | 2, 140 | wbuf.as_mut_ptr(), 141 | 0, 142 | std::ptr::null_mut::(), 143 | ) { 144 | s.push_str(" XX"); 145 | continue; 146 | } 147 | 148 | let mut wbuf: [u8; 1] = [(device_addr << 1) + 1]; 149 | let mut rbuf: [u8; 1] = [0]; 150 | 151 | if let Err(_) = ch347_rs::i2c_stream( 152 | dev.get_dev_index(), 153 | 1, 154 | wbuf.as_mut_ptr(), 155 | 1, 156 | rbuf.as_mut_ptr(), 157 | ) { 158 | s.push_str(" XX"); 159 | } 160 | 161 | s.push_str(&format!(" {:02X}", rbuf[0])); 162 | } 163 | println!("{}", s); 164 | } 165 | } 166 | 167 | DumpPage::Word => { 168 | println!("TODO: sorry"); 169 | } 170 | DumpPage::Full => { 171 | let mut wbuf: [u8; 2] = [device_addr << 1, 0x00]; 172 | 173 | if let Err(_) = ch347_rs::i2c_stream( 174 | dev.get_dev_index(), 175 | 2, 176 | wbuf.as_mut_ptr(), 177 | 0, 178 | std::ptr::null_mut::(), 179 | ) { 180 | println!("i2c device addr nack"); 181 | return; 182 | } 183 | 184 | let mut wbuf: [u8; 1] = [(device_addr << 1) + 1]; 185 | let mut rbuf: [u8; 256] = [0; 256]; 186 | let _ = ch347_rs::i2c_stream( 187 | dev.get_dev_index(), 188 | 1, 189 | wbuf.as_mut_ptr(), 190 | 256, 191 | rbuf.as_mut_ptr(), 192 | ); 193 | 194 | println!(" 0 1 2 3 4 5 6 7 8 9 A B C D E F"); 195 | for y in 0..16 { 196 | let mut s = format!("{:02X}:", y * 0x10); 197 | for x in 0..16 { 198 | s.push_str(&format!(" {:02X}", rbuf[y * 0x10 + x])); 199 | } 200 | println!("{}", s); 201 | } 202 | } 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /src/bin/ch347tool/spi_flash/write.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | cmp, 3 | error::Error, 4 | fmt::Write, 5 | fs, 6 | sync::{Arc, Mutex}, 7 | thread, 8 | time::{Duration, SystemTime}, 9 | }; 10 | 11 | use clap::Parser; 12 | use indicatif::{ProgressBar, ProgressState, ProgressStyle}; 13 | 14 | use super::utils::{format_byte_per_sec, format_byte_unit}; 15 | 16 | #[derive(Parser, Clone, Debug)] 17 | #[clap(about = "Write spi flash chip")] 18 | pub struct CmdSpiFlashWrite { 19 | /// Before earse chip 20 | #[clap(short, long, value_parser, action)] 21 | erase: bool, 22 | 23 | /// Check after writing each page 24 | #[clap(short, long, value_parser, action)] 25 | check: bool, 26 | 27 | /// After check 28 | #[clap(long, value_parser, action)] 29 | after_check: bool, 30 | 31 | /// output to file 32 | #[clap(value_parser)] 33 | file: String, 34 | } 35 | 36 | pub fn cli_spi_flash_write( 37 | flash_args: &super::CmdSpiFlash, 38 | args: &CmdSpiFlashWrite, 39 | ) -> Result<(), Box> { 40 | let mut setp_count = 1; 41 | let mut setp_cnt = 0; 42 | if args.erase { 43 | setp_count += 1; 44 | } 45 | if args.after_check { 46 | setp_count += 1; 47 | } 48 | 49 | let mut file_buf = fs::read(args.file.as_str())?; 50 | 51 | if args.file.to_lowercase().ends_with(".cap") && (file_buf.len() > 0x800) { 52 | println!( 53 | "{} Detect {} file, will be offset {} address write", 54 | console::style("Note:").green(), 55 | console::style("ASUS-CAP").green(), 56 | console::style("0x800").green(), 57 | ); 58 | 59 | file_buf = file_buf[0x800..file_buf.len()].to_vec(); 60 | } 61 | 62 | let (device, chip_info) = flash_args.init()?; 63 | 64 | let chip_capacity: usize = chip_info.capacity.into(); 65 | let wsize = cmp::min(file_buf.len(), chip_capacity); 66 | 67 | if file_buf.len() > chip_capacity { 68 | println!( 69 | "{} File size is too large, the last {} will be lost", 70 | console::style("Warn:").yellow(), 71 | console::style(format_byte_unit(file_buf.len() - chip_capacity)).yellow(), 72 | ); 73 | } 74 | 75 | if args.erase { 76 | setp_cnt += 1; 77 | 78 | let spinner_style = ProgressStyle::with_template( 79 | "{prefix} {spinner:.green} [{elapsed_precise}] {wide_msg}", 80 | ) 81 | .unwrap(); 82 | // .tick_chars("⠁⠂⠄⡀⢀⠠⠐⠈x"); 83 | 84 | let pb = ProgressBar::new(0); 85 | pb.set_style(spinner_style.clone()); 86 | pb.set_prefix(format!( 87 | "{} Erasing", 88 | console::style(format!("[{}/{}]", setp_cnt, setp_count)) 89 | .bold() 90 | .dim(), 91 | )); 92 | // pb.set_message(format!("Erasing ...")); 93 | 94 | let pb_finished = Arc::new(Mutex::new(pb)); 95 | let mux_pb_finished = Arc::clone(&pb_finished); 96 | 97 | thread::spawn(move || loop { 98 | let pb_finished = mux_pb_finished.lock().unwrap(); 99 | 100 | if (*pb_finished).is_finished() { 101 | break; 102 | } 103 | 104 | (*pb_finished).tick(); 105 | thread::sleep(Duration::from_millis(40)); 106 | }); 107 | 108 | device.erase_full()?; 109 | 110 | let pb_finished = pb_finished.lock().unwrap(); 111 | (*pb_finished).finish_and_clear(); 112 | 113 | let take_time = (*pb_finished).elapsed().as_millis(); 114 | let take_time = Duration::from_millis(take_time as u64); 115 | 116 | println!( 117 | "{} Erase done, Take_time: {}", 118 | console::style(format!("[{}/{}]", setp_cnt, setp_count)) 119 | .bold() 120 | .dim(), 121 | humantime::format_duration(take_time) 122 | ); 123 | } 124 | 125 | let pb = ProgressBar::new(wsize as u64); 126 | pb.set_style(ProgressStyle::with_template( 127 | "{prefix} {spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({binary_bytes_per_sec}) ({eta})") 128 | .unwrap() 129 | .with_key("eta", |state: &ProgressState, w: &mut dyn Write| write!(w, "{:.1}s", state.eta().as_secs_f64()).unwrap()) 130 | .progress_chars("#>-")); 131 | 132 | setp_cnt += 1; 133 | 134 | pb.set_prefix(format!( 135 | "{} {}", 136 | console::style(format!("[{}/{}]", setp_cnt, setp_count)) 137 | .bold() 138 | .dim(), 139 | if args.check { 140 | "Writing with Verifing" 141 | } else { 142 | "Writing only" 143 | }, 144 | )); 145 | 146 | let start_time = SystemTime::now(); 147 | pb.tick(); 148 | 149 | let a = |e| -> bool { 150 | match e { 151 | ch347_rs::WriteEvent::Block(addr, count) => { 152 | pb.inc(count as u64); 153 | 154 | if !args.check { 155 | return true; 156 | } 157 | 158 | if (addr + 0x100) % 4096 != 0 { 159 | return true; 160 | } 161 | 162 | const BLOCK_SIZE: usize = 4096; 163 | let block_addr = addr + 0x100 - BLOCK_SIZE; 164 | 165 | // println!( 166 | // "check block {:02X}_{:04X}", 167 | // block_addr >> 16, 168 | // block_addr & 0xFFFF 169 | // ); 170 | 171 | let mut rbuf: [u8; BLOCK_SIZE] = [0; BLOCK_SIZE]; 172 | device.read(block_addr as u32, &mut rbuf); 173 | 174 | let mut is_verify_pass = true; 175 | 176 | for x in 0..BLOCK_SIZE { 177 | if rbuf[x] != file_buf[block_addr + x] { 178 | pb.finish_and_clear(); 179 | println!( 180 | "diff 0x{:04X}_{:04X} {:02X} => {:02X}", 181 | (block_addr + x) >> 16, 182 | (block_addr + x) & 0xFFFF, 183 | file_buf[block_addr + x], 184 | rbuf[x] 185 | ); 186 | is_verify_pass = false; 187 | } 188 | } 189 | 190 | return is_verify_pass; 191 | } 192 | ch347_rs::WriteEvent::Finish(_) => true, 193 | } 194 | }; 195 | 196 | device.write_with_callback(a, 0, &file_buf[0..wsize])?; 197 | pb.finish_and_clear(); 198 | let take_time = start_time.elapsed().unwrap().as_millis(); 199 | let take_time = Duration::from_millis(take_time as u64); 200 | 201 | let speed = (wsize as f64) / take_time.as_secs_f64(); 202 | let speed_str = format_byte_per_sec(speed); 203 | 204 | println!( 205 | "{} Write done, Take time: {} Speed: {}", 206 | console::style(format!("[{}/{}]", setp_cnt, setp_count)) 207 | .bold() 208 | .dim(), 209 | humantime::format_duration(take_time), 210 | speed_str, 211 | ); 212 | 213 | if args.after_check { 214 | setp_cnt += 1; 215 | 216 | println!( 217 | "{} Verify done, Take time: {}, Speed: {}", 218 | console::style(format!("[{}/{}]", setp_cnt, setp_count)) 219 | .bold() 220 | .dim(), 221 | 0, 222 | 0, 223 | ); 224 | } 225 | 226 | Ok(()) 227 | } 228 | -------------------------------------------------------------------------------- /src/spi_flash/model/gigadevice.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | 3 | use super::{Capacity, Chip, RegReadRet, Register, RegisterAccess, RegisterItem, Vendor}; 4 | 5 | pub fn parse_jedec_id(vendor: &'static Vendor, data: (u8, u8)) -> Option { 6 | let memory_type = data.0; 7 | let capacity = data.1; 8 | 9 | let mut chip_name = String::new(); 10 | 11 | chip_name.push_str("GD25"); 12 | 13 | match memory_type { 14 | 0x40 => chip_name.push_str("Q"), 15 | 0x60 => chip_name.push_str("LQ"), 16 | _ => return None, 17 | } 18 | 19 | let (cap_str, chip_capacity) = match capacity { 20 | 0x10 => ("05", Capacity::C05), 21 | 0x11 => ("10", Capacity::C10), 22 | 0x12 => ("20", Capacity::C20), 23 | 0x13 => ("40", Capacity::C40), 24 | 0x14 => ("80", Capacity::C80), 25 | 0x15 => ("16", Capacity::C16), 26 | 0x16 => ("32", Capacity::C32), 27 | 0x17 => ("64", Capacity::C64), 28 | 0x18 => ("128", Capacity::C128), 29 | 0x19 => ("256", Capacity::C256), 30 | _ => return None, 31 | }; 32 | chip_name.push_str(cap_str); 33 | 34 | Some(Chip { 35 | name: chip_name, 36 | vendor, 37 | capacity: chip_capacity, 38 | }) 39 | } 40 | 41 | pub const REGISTER_DEFINES: [Register; 5] = [ 42 | Register { 43 | name: "status_1", 44 | addr: 0x05, 45 | reader: |spi_flash| -> Result { 46 | let mut buf: [u8; 2] = [0x05, 0x00]; 47 | 48 | if let Err(_) = spi_flash.drive.transfer(&mut buf) { 49 | return Err("transfer fail"); 50 | } 51 | 52 | Ok(RegReadRet::One(buf[1])) 53 | }, 54 | writer: Some(|spi_flash, wbuf| -> Result<(), Box> { 55 | let mut buf: [u8; 1] = [0x06]; 56 | spi_flash.drive.transfer(&mut buf)?; 57 | 58 | let mut buf: [u8; 2] = [0x01, wbuf[0]]; 59 | spi_flash.drive.transfer(&mut buf)?; 60 | 61 | loop { 62 | let mut buf: [u8; 2] = [0x05, 0x00]; 63 | spi_flash.drive.transfer(&mut buf)?; 64 | 65 | if buf[1] & 0x01 != 0x01 { 66 | break; 67 | } 68 | } 69 | 70 | let mut buf: [u8; 1] = [0x04]; 71 | spi_flash.drive.transfer(&mut buf)?; 72 | 73 | Ok(()) 74 | }), 75 | items: Some(&[ 76 | RegisterItem { 77 | name: "busy", 78 | alias: &["WIP"], 79 | describe: "Erase/Write In Progress", 80 | offset: 0, 81 | width: 1, 82 | access: RegisterAccess::ReadOnly, 83 | }, 84 | RegisterItem { 85 | name: "write_enable", 86 | alias: &["WE", "WEL"], 87 | describe: "Write Enable Latch", 88 | offset: 1, 89 | width: 1, 90 | access: RegisterAccess::ReadOnly, 91 | }, 92 | RegisterItem { 93 | name: "block_protect", 94 | alias: &["BP"], 95 | describe: "Block Protect Bits", 96 | offset: 2, 97 | width: 5, 98 | access: RegisterAccess::ReadWrite, 99 | }, 100 | RegisterItem { 101 | name: "sreg_protect", 102 | alias: &["SRP0"], 103 | describe: "Status Register Protect", 104 | offset: 7, 105 | width: 1, 106 | access: RegisterAccess::ReadWrite, 107 | }, 108 | ]), 109 | }, 110 | Register { 111 | name: "status_2", 112 | addr: 0x35, 113 | reader: |spi_flash| -> Result { 114 | let mut buf: [u8; 2] = [0x35, 0x00]; 115 | 116 | if let Err(_) = spi_flash.drive.transfer(&mut buf) { 117 | return Err("transfer fail"); 118 | } 119 | 120 | Ok(RegReadRet::One(buf[1])) 121 | }, 122 | writer: Some(|spi_flash, wbuf| -> Result<(), Box> { 123 | let mut buf: [u8; 1] = [0x06]; 124 | spi_flash.drive.transfer(&mut buf)?; 125 | 126 | let mut buf: [u8; 2] = [0x31, wbuf[0]]; 127 | spi_flash.drive.transfer(&mut buf)?; 128 | 129 | loop { 130 | let mut buf: [u8; 2] = [0x05, 0x00]; 131 | spi_flash.drive.transfer(&mut buf)?; 132 | 133 | if buf[1] & 0x01 != 0x01 { 134 | break; 135 | } 136 | } 137 | 138 | let mut buf: [u8; 1] = [0x04]; 139 | spi_flash.drive.transfer(&mut buf)?; 140 | 141 | Ok(()) 142 | }), 143 | items: Some(&[ 144 | RegisterItem { 145 | name: "cur_addr_mode", 146 | alias: &["ADS"], 147 | offset: 0, 148 | width: 1, 149 | access: RegisterAccess::ReadOnly, 150 | describe: "Current Address Mode", 151 | }, 152 | RegisterItem { 153 | name: "quad_enable", 154 | alias: &["QE"], 155 | offset: 1, 156 | width: 1, 157 | access: RegisterAccess::ReadWrite, 158 | describe: "Quad Enable", 159 | }, 160 | RegisterItem { 161 | name: "suspend", 162 | alias: &["SUS2"], 163 | offset: 2, 164 | width: 1, 165 | access: RegisterAccess::ReadOnly, 166 | describe: "Program Suspend", 167 | }, 168 | RegisterItem { 169 | name: "lock", 170 | alias: &["LB"], 171 | offset: 3, 172 | width: 3, 173 | access: RegisterAccess::ReadWriteOTP, 174 | describe: "Security Register Lock Bits", 175 | }, 176 | RegisterItem { 177 | name: "sreg_protect", 178 | alias: &["SRP1"], 179 | describe: "Status Register Protection", 180 | offset: 6, 181 | width: 1, 182 | access: RegisterAccess::ReadWrite, 183 | }, 184 | RegisterItem { 185 | name: "suspend", 186 | alias: &["SUS1"], 187 | offset: 7, 188 | width: 1, 189 | access: RegisterAccess::ReadOnly, 190 | describe: "Erase Suspend", 191 | }, 192 | ]), 193 | }, 194 | Register { 195 | name: "status_3", 196 | addr: 0x15, 197 | reader: |spi_flash| -> Result { 198 | let mut buf: [u8; 2] = [0x15, 0x00]; 199 | 200 | if let Err(_) = spi_flash.drive.transfer(&mut buf) { 201 | return Err("transfer fail"); 202 | } 203 | 204 | Ok(RegReadRet::One(buf[1])) 205 | }, 206 | writer: Some(|spi_flash, wbuf| -> Result<(), Box> { 207 | let mut buf: [u8; 1] = [0x06]; 208 | spi_flash.drive.transfer(&mut buf)?; 209 | 210 | let mut buf: [u8; 2] = [0x11, wbuf[0]]; 211 | spi_flash.drive.transfer(&mut buf)?; 212 | 213 | loop { 214 | let mut buf: [u8; 2] = [0x05, 0x00]; 215 | spi_flash.drive.transfer(&mut buf)?; 216 | 217 | if buf[1] & 0x01 != 0x01 { 218 | break; 219 | } 220 | } 221 | 222 | let mut buf: [u8; 1] = [0x04]; 223 | spi_flash.drive.transfer(&mut buf)?; 224 | 225 | Ok(()) 226 | }), 227 | items: Some(&[ 228 | RegisterItem { 229 | name: "dummy_cfg", 230 | alias: &["DC"], 231 | offset: 0, 232 | width: 2, 233 | access: RegisterAccess::ReadWrite, 234 | describe: "Dummy Configuration", 235 | }, 236 | RegisterItem { 237 | name: "program_err", 238 | alias: &["PE"], 239 | offset: 2, 240 | width: 1, 241 | access: RegisterAccess::ReadOnly, 242 | describe: "Program Error", 243 | }, 244 | RegisterItem { 245 | name: "erase_err", 246 | alias: &["EE"], 247 | offset: 3, 248 | width: 1, 249 | access: RegisterAccess::ReadOnly, 250 | describe: "Erase Error", 251 | }, 252 | RegisterItem { 253 | name: "powerup_addr_mode", 254 | alias: &["ADP"], 255 | offset: 4, 256 | width: 1, 257 | access: RegisterAccess::ReadWrite, 258 | describe: "Power Up Address Mode", 259 | }, 260 | RegisterItem { 261 | name: "DRV", 262 | alias: &["DRV"], 263 | offset: 5, 264 | width: 2, 265 | access: RegisterAccess::ReadWrite, 266 | describe: "Output Driver Strength", 267 | }, 268 | RegisterItem { 269 | name: "hold_rst_func", 270 | alias: &["HOLD/RST"], 271 | offset: 7, 272 | width: 1, 273 | access: RegisterAccess::ReadWrite, 274 | describe: "HOLD# or RESET# Function", 275 | }, 276 | ]), 277 | }, 278 | Register { 279 | name: "unique_id", 280 | addr: 0x4B, 281 | reader: |spi_flash| -> Result { 282 | let mut wbuf: [u8; 21] = [0; 21]; 283 | wbuf[0] = 0x4B; 284 | 285 | if let Err(e) = spi_flash.drive.transfer(&mut wbuf) { 286 | return Err(e); 287 | } 288 | 289 | Ok(RegReadRet::Muti(wbuf[5..wbuf.len()].to_vec())) 290 | }, 291 | writer: None, 292 | items: None, 293 | }, 294 | Register { 295 | name: "ext_addr", 296 | addr: 0xC8, 297 | reader: |spi_flash| -> Result { 298 | let mut buf: [u8; 2] = [0xC8, 0x00]; 299 | 300 | spi_flash.drive.transfer(&mut buf)?; 301 | Ok(RegReadRet::One(buf[1])) 302 | }, 303 | writer: Some(|spi_flash, wbuf| -> Result<(), Box> { 304 | let mut buf: [u8; 1] = [0x06]; 305 | spi_flash.drive.transfer(&mut buf)?; 306 | 307 | let mut buf: [u8; 2] = [0xC5, wbuf[0]]; 308 | spi_flash.drive.transfer(&mut buf)?; 309 | 310 | let mut buf: [u8; 1] = [0x04]; 311 | spi_flash.drive.transfer(&mut buf)?; 312 | 313 | Ok(()) 314 | }), 315 | items: None, 316 | }, 317 | ]; 318 | -------------------------------------------------------------------------------- /src/spi_flash/model/winbond.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | 3 | use super::{Capacity, Chip, RegReadRet, Register, RegisterAccess, RegisterItem, Vendor}; 4 | 5 | #[test] 6 | pub fn test_parse_jedec_id() { 7 | for (id, name, cap) in vec![ 8 | (&[0xEF, 0x40, 0x10], "W25Q05", Capacity::C05), 9 | (&[0xEF, 0x40, 0x11], "W25Q10", Capacity::C10), 10 | (&[0xEF, 0x40, 0x12], "W25Q20", Capacity::C20), 11 | (&[0xEF, 0x40, 0x13], "W25Q40", Capacity::C40), 12 | (&[0xEF, 0x40, 0x14], "W25Q80", Capacity::C80), 13 | (&[0xEF, 0x40, 0x15], "W25Q16", Capacity::C16), 14 | (&[0xEF, 0x40, 0x16], "W25Q32", Capacity::C32), 15 | ] { 16 | assert!(super::parse_jedec_id(id).is_some()); 17 | 18 | let chip = super::parse_jedec_id(id).unwrap(); 19 | 20 | assert_eq!(name, &chip.name); 21 | assert_eq!(cap, chip.capacity); 22 | } 23 | } 24 | 25 | pub fn parse_jedec_id(vendor: &'static Vendor, data: (u8, u8)) -> Option { 26 | let memory_type = data.0; 27 | let capacity = data.1; 28 | 29 | let mut chip_name = String::new(); 30 | 31 | chip_name.push_str("W25Q"); 32 | 33 | let (cap_str, chip_capacity) = match capacity { 34 | 0x10 => ("05", Capacity::C05), 35 | 0x11 => ("10", Capacity::C10), 36 | 0x12 => ("20", Capacity::C20), 37 | 0x13 => ("40", Capacity::C40), 38 | 0x14 => ("80", Capacity::C80), 39 | 0x15 => ("16", Capacity::C16), 40 | 0x16 => ("32", Capacity::C32), 41 | 0x17 => ("64", Capacity::C64), 42 | 0x18 => ("128", Capacity::C128), 43 | 0x19 => ("256", Capacity::C256), 44 | _ => return None, 45 | }; 46 | chip_name.push_str(cap_str); 47 | 48 | match memory_type { 49 | 0x40 => {} 50 | 0x60 => {} 51 | _ => return None, 52 | } 53 | 54 | Some(Chip { 55 | name: chip_name, 56 | vendor, 57 | capacity: chip_capacity, 58 | }) 59 | } 60 | 61 | pub const REGISTER_DEFINES: [Register; 5] = [ 62 | Register { 63 | name: "status_1", 64 | addr: 0x05, 65 | reader: |spi_flash| -> Result { 66 | let mut buf: [u8; 2] = [0x05, 0x00]; 67 | 68 | if let Err(_) = spi_flash.drive.transfer(&mut buf) { 69 | return Err("transfer fail"); 70 | } 71 | 72 | Ok(RegReadRet::One(buf[1])) 73 | }, 74 | writer: Some(|spi_flash, wbuf| -> Result<(), Box> { 75 | let mut buf: [u8; 1] = [0x06]; 76 | spi_flash.drive.transfer(&mut buf)?; 77 | 78 | let mut buf: [u8; 2] = [0x01, wbuf[0]]; 79 | spi_flash.drive.transfer(&mut buf)?; 80 | 81 | loop { 82 | let mut buf: [u8; 2] = [0x05, 0x00]; 83 | spi_flash.drive.transfer(&mut buf)?; 84 | 85 | if buf[1] & 0x01 != 0x01 { 86 | break; 87 | } 88 | } 89 | 90 | let mut buf: [u8; 1] = [0x04]; 91 | spi_flash.drive.transfer(&mut buf)?; 92 | 93 | Ok(()) 94 | }), 95 | items: Some(&[ 96 | RegisterItem { 97 | name: "busy", 98 | alias: &["WIP"], 99 | describe: "Erase/Write In Progress", 100 | offset: 0, 101 | width: 1, 102 | access: RegisterAccess::ReadOnly, 103 | }, 104 | RegisterItem { 105 | name: "write_enable", 106 | alias: &["WE", "WEL"], 107 | describe: "Write Enable Latch", 108 | offset: 1, 109 | width: 1, 110 | access: RegisterAccess::ReadOnly, 111 | }, 112 | RegisterItem { 113 | name: "block_protect", 114 | alias: &["BP"], 115 | describe: "Block Protect Bits", 116 | offset: 2, 117 | width: 4, 118 | access: RegisterAccess::ReadWrite, 119 | }, 120 | RegisterItem { 121 | name: "tb_protect", 122 | alias: &["TB"], 123 | describe: "Top/Bottom Protect Bit", 124 | offset: 6, 125 | width: 1, 126 | access: RegisterAccess::ReadWrite, 127 | }, 128 | RegisterItem { 129 | name: "sreg_protect", 130 | alias: &["SRP"], 131 | describe: "Status Register Protect", 132 | offset: 7, 133 | width: 1, 134 | access: RegisterAccess::ReadWrite, 135 | }, 136 | ]), 137 | }, 138 | Register { 139 | name: "status_2", 140 | addr: 0x35, 141 | reader: |spi_flash| -> Result { 142 | let mut buf: [u8; 2] = [0x35, 0x00]; 143 | 144 | if let Err(_) = spi_flash.drive.transfer(&mut buf) { 145 | return Err("transfer fail"); 146 | } 147 | 148 | Ok(RegReadRet::One(buf[1])) 149 | }, 150 | writer: Some(|spi_flash, wbuf| -> Result<(), Box> { 151 | let mut buf: [u8; 1] = [0x06]; 152 | spi_flash.drive.transfer(&mut buf)?; 153 | 154 | let mut buf: [u8; 2] = [0x31, wbuf[0]]; 155 | spi_flash.drive.transfer(&mut buf)?; 156 | 157 | loop { 158 | let mut buf: [u8; 2] = [0x05, 0x00]; 159 | spi_flash.drive.transfer(&mut buf)?; 160 | 161 | if buf[1] & 0x01 != 0x01 { 162 | break; 163 | } 164 | } 165 | 166 | let mut buf: [u8; 1] = [0x04]; 167 | spi_flash.drive.transfer(&mut buf)?; 168 | 169 | Ok(()) 170 | }), 171 | items: Some(&[ 172 | RegisterItem { 173 | name: "sreg_protect_1", 174 | alias: &["SRL"], 175 | offset: 0, 176 | width: 1, 177 | access: RegisterAccess::ReadWrite, 178 | describe: "Status Register Protect 1", 179 | }, 180 | RegisterItem { 181 | name: "quad_enable", 182 | alias: &["QE"], 183 | offset: 1, 184 | width: 1, 185 | access: RegisterAccess::ReadWrite, 186 | describe: "Quad Enable", 187 | }, 188 | RegisterItem { 189 | name: "resv", 190 | alias: &["R"], 191 | offset: 2, 192 | width: 1, 193 | access: RegisterAccess::ReadOnly, 194 | describe: "Reserved", 195 | }, 196 | RegisterItem { 197 | name: "lock", 198 | alias: &["LB"], 199 | offset: 3, 200 | width: 3, 201 | access: RegisterAccess::ReadWriteOTP, 202 | describe: "Security Register Lock Bits", 203 | }, 204 | RegisterItem { 205 | name: "lock", 206 | alias: &["CMP"], 207 | offset: 6, 208 | width: 1, 209 | access: RegisterAccess::ReadWrite, 210 | describe: "Complement Protect", 211 | }, 212 | RegisterItem { 213 | name: "suspend", 214 | alias: &["SUS"], 215 | offset: 7, 216 | width: 1, 217 | access: RegisterAccess::ReadOnly, 218 | describe: "Suspend Status", 219 | }, 220 | ]), 221 | }, 222 | Register { 223 | name: "status_3", 224 | addr: 0x15, 225 | reader: |spi_flash| -> Result { 226 | let mut buf: [u8; 2] = [0x15, 0x00]; 227 | 228 | if let Err(_) = spi_flash.drive.transfer(&mut buf) { 229 | return Err("transfer fail"); 230 | } 231 | 232 | Ok(RegReadRet::One(buf[1])) 233 | }, 234 | writer: Some(|spi_flash, wbuf| -> Result<(), Box> { 235 | let mut buf: [u8; 1] = [0x06]; 236 | spi_flash.drive.transfer(&mut buf)?; 237 | 238 | let mut buf: [u8; 2] = [0x11, wbuf[0]]; 239 | spi_flash.drive.transfer(&mut buf)?; 240 | 241 | loop { 242 | let mut buf: [u8; 2] = [0x05, 0x00]; 243 | spi_flash.drive.transfer(&mut buf)?; 244 | 245 | if buf[1] & 0x01 != 0x01 { 246 | break; 247 | } 248 | } 249 | 250 | let mut buf: [u8; 1] = [0x04]; 251 | spi_flash.drive.transfer(&mut buf)?; 252 | 253 | Ok(()) 254 | }), 255 | items: Some(&[ 256 | RegisterItem { 257 | name: "cur_addr_mode", 258 | alias: &["ADS"], 259 | offset: 0, 260 | width: 1, 261 | access: RegisterAccess::ReadOnly, 262 | describe: "Current Address Mode", 263 | }, 264 | RegisterItem { 265 | name: "powerup_addr_mode", 266 | alias: &["ADP"], 267 | offset: 1, 268 | width: 1, 269 | access: RegisterAccess::ReadWrite, 270 | describe: "Power Up Address Mode", 271 | }, 272 | RegisterItem { 273 | name: "resv", 274 | alias: &["R"], 275 | offset: 2, 276 | width: 2, 277 | access: RegisterAccess::ReadOnly, 278 | describe: "Reserved", 279 | }, 280 | RegisterItem { 281 | name: "DRV", 282 | alias: &["DRV"], 283 | offset: 5, 284 | width: 2, 285 | access: RegisterAccess::ReadWrite, 286 | describe: "Output Driver Strength", 287 | }, 288 | RegisterItem { 289 | name: "resv", 290 | alias: &["R"], 291 | offset: 7, 292 | width: 1, 293 | access: RegisterAccess::ReadOnly, 294 | describe: "Reserved", 295 | }, 296 | ]), 297 | }, 298 | Register { 299 | name: "unique_id", 300 | addr: 0x4B, 301 | reader: |spi_flash| -> Result { 302 | let mut wbuf: [u8; 13] = [0; 13]; 303 | wbuf[0] = 0x4B; 304 | 305 | if let Err(e) = spi_flash.drive.transfer(&mut wbuf) { 306 | return Err(e); 307 | } 308 | 309 | Ok(RegReadRet::Muti(wbuf[5..wbuf.len()].to_vec())) 310 | }, 311 | writer: None, 312 | items: None, 313 | }, 314 | Register { 315 | name: "ext_addr", 316 | addr: 0xC8, 317 | reader: |spi_flash| -> Result { 318 | let mut buf: [u8; 2] = [0xC8, 0x00]; 319 | 320 | spi_flash.drive.transfer(&mut buf)?; 321 | Ok(RegReadRet::One(buf[1])) 322 | }, 323 | writer: Some(|spi_flash, wbuf| -> Result<(), Box> { 324 | let mut buf: [u8; 1] = [0x06]; 325 | spi_flash.drive.transfer(&mut buf)?; 326 | 327 | let mut buf: [u8; 2] = [0xC5, wbuf[0]]; 328 | spi_flash.drive.transfer(&mut buf)?; 329 | 330 | let mut buf: [u8; 1] = [0x04]; 331 | spi_flash.drive.transfer(&mut buf)?; 332 | 333 | Ok(()) 334 | }), 335 | items: None, 336 | }, 337 | ]; 338 | -------------------------------------------------------------------------------- /src/spi_flash/spi_flash.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use std::{error::Error, fmt, ops::Range}; 3 | 4 | pub enum SpiFlashCmd { 5 | JedecId, 6 | WriteEnable, 7 | WriteDisable, 8 | // status 9 | ReadStatus, 10 | // erase 11 | ChipErase, 12 | Erase4K, 13 | Erase32K, 14 | Erase64K, 15 | // write 16 | PageProgram, 17 | // read 18 | ReadData, 19 | } 20 | 21 | impl Into for SpiFlashCmd { 22 | fn into(self) -> u8 { 23 | match self { 24 | SpiFlashCmd::JedecId => 0x9F, 25 | SpiFlashCmd::WriteEnable => 0x06, 26 | SpiFlashCmd::WriteDisable => 0x04, 27 | // status 28 | SpiFlashCmd::ReadStatus => 0x05, 29 | // erase 30 | SpiFlashCmd::ChipErase => 0xC7, 31 | SpiFlashCmd::Erase4K => 0x20, 32 | SpiFlashCmd::Erase32K => 0x52, 33 | SpiFlashCmd::Erase64K => 0xD8, 34 | // write 35 | SpiFlashCmd::PageProgram => 0x02, 36 | // read 37 | SpiFlashCmd::ReadData => 0x03, 38 | } 39 | } 40 | } 41 | 42 | pub enum WriteEvent { 43 | Block(usize, usize), 44 | Finish(usize), 45 | } 46 | 47 | pub type WriteEventFn = fn(e: WriteEvent); 48 | 49 | #[derive(Debug)] 50 | pub struct StatusRes { 51 | pub busy: bool, 52 | pub wtite_enable: bool, 53 | } 54 | 55 | impl From for StatusRes { 56 | fn from(data: u8) -> StatusRes { 57 | let s = StatusRes { 58 | busy: (data & 0x01) != 0, 59 | wtite_enable: (data & 0x02) != 0, 60 | }; 61 | return s; 62 | } 63 | } 64 | 65 | pub struct SpiFlash { 66 | pub drive: T, 67 | } 68 | 69 | #[derive(Debug)] 70 | pub enum DetectErr { 71 | UnknowManufacturerID([u8; 3]), 72 | Other(String), 73 | } 74 | 75 | impl fmt::Display for DetectErr { 76 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 77 | match self { 78 | DetectErr::UnknowManufacturerID(buf) => write!(f, "Unknow JedecID {:02X?}", buf), 79 | DetectErr::Other(s) => write!(f, "{}", s), 80 | } 81 | } 82 | } 83 | 84 | impl Into> for DetectErr { 85 | fn into(self) -> Box { 86 | self.to_string().into() 87 | } 88 | } 89 | 90 | impl SpiFlash { 91 | pub fn new(drive: T) -> SpiFlash { 92 | SpiFlash { drive } 93 | } 94 | 95 | pub fn detect(&self) -> Result { 96 | let mut wbuf: [u8; 4] = [SpiFlashCmd::JedecId.into(), 0x00, 0x00, 0x00]; 97 | 98 | if let Err(e) = self.drive.transfer(&mut wbuf) { 99 | return Err(DetectErr::Other(format!("{}", e))); 100 | } 101 | 102 | let jedec_id = &wbuf[1..4]; 103 | // println!("JEDEC_ID: {:02X?} ", jedec_id); 104 | 105 | // let manufacturer_id = jedec_id[0]; 106 | 107 | let chip_info = match parse_jedec_id(jedec_id) { 108 | None => { 109 | return Err(DetectErr::UnknowManufacturerID( 110 | jedec_id.try_into().unwrap(), 111 | )); 112 | // return Err(DetectErr::Other(format!( 113 | // "Unknow JedecID: {:02X?}", 114 | // jedec_id 115 | // ))); 116 | } 117 | Some(chip_info) => chip_info, 118 | }; 119 | 120 | return Ok(chip_info); 121 | } 122 | 123 | pub fn read_uuid(&self, vendor: &Vendor) -> Result, &'static str> { 124 | return vendor.read_uid(self); 125 | } 126 | 127 | pub fn read_status_register( 128 | &self, 129 | _vendor: &Vendor, 130 | ) -> Result, &'static str> { 131 | return Err("Not supported"); 132 | } 133 | 134 | pub fn detect_and_print(&self) -> Result { 135 | let chip_info = self.detect()?; 136 | 137 | println!("ChipInfo:"); 138 | println!(" Manufacturer: {}", chip_info.vendor.name); 139 | println!(" Name: {}", chip_info.name); 140 | println!(" Capacity: {}", chip_info.capacity); 141 | 142 | return Ok(chip_info); 143 | } 144 | 145 | pub fn read(&self, addr: u32, buf: &mut [u8]) { 146 | buf[0] = SpiFlashCmd::ReadData.into(); 147 | buf[1] = (addr >> 16) as u8; 148 | buf[2] = (addr >> 8) as u8; 149 | buf[3] = (addr) as u8; 150 | 151 | if let Err(_) = self.drive.write_after_read(4, buf.len() as u32, buf) { 152 | return; 153 | } 154 | } 155 | 156 | pub fn read_status(&self) -> Result { 157 | let mut buf: [u8; 2] = [SpiFlashCmd::ReadStatus.into(), 0x00]; 158 | 159 | if let Err(_) = self.drive.transfer(&mut buf) { 160 | return Err("transfer fail"); 161 | } 162 | 163 | Ok(StatusRes::from(buf[1])) 164 | } 165 | 166 | pub fn wait_not_busy(&self) -> Result { 167 | loop { 168 | let status = match self.read_status() { 169 | Err(e) => { 170 | println!("{:X?}", e); 171 | return Err("read_status fail"); 172 | } 173 | Ok(s) => s, 174 | }; 175 | 176 | if status.busy { 177 | continue; 178 | } 179 | 180 | return Ok(status); 181 | } 182 | } 183 | 184 | pub fn erase_full(&self) -> Result<(), &'static str> { 185 | self.wait_not_busy()?; 186 | 187 | let mut buf: [u8; 1] = [SpiFlashCmd::WriteEnable.into()]; 188 | self.drive.transfer(&mut buf)?; 189 | 190 | let mut buf: [u8; 1] = [SpiFlashCmd::ChipErase.into()]; 191 | self.drive.transfer(&mut buf)?; 192 | 193 | self.wait_not_busy()?; 194 | 195 | return Ok(()); 196 | } 197 | 198 | pub fn write(&self, addr: u32, buf: &[u8]) -> Result<(), &'static str> { 199 | self.write_with_callback(|_| true, addr, buf) 200 | } 201 | 202 | pub fn write_with_callback( 203 | &self, 204 | mut cbk: F, 205 | addr: u32, 206 | buf: &[u8], 207 | ) -> Result<(), &'static str> 208 | where 209 | F: FnMut(WriteEvent) -> bool, 210 | { 211 | self.wait_not_busy()?; 212 | 213 | const BLOCK_SIZE: usize = 0x100; 214 | 215 | for i in (0..buf.len()).step_by(BLOCK_SIZE) { 216 | // write enable 217 | let mut wbuf: [u8; 1] = [SpiFlashCmd::WriteEnable.into()]; 218 | self.drive.transfer(&mut wbuf)?; 219 | 220 | let addr_offset = addr + i as u32; 221 | 222 | if (buf.len() - i) >= BLOCK_SIZE { 223 | let mut wbuf: [u8; 4 + BLOCK_SIZE] = [0; 4 + BLOCK_SIZE]; 224 | 225 | wbuf[0] = SpiFlashCmd::PageProgram.into(); 226 | wbuf[1] = (addr_offset >> 16) as u8; 227 | wbuf[2] = (addr_offset >> 8) as u8; 228 | wbuf[3] = addr_offset as u8; 229 | wbuf[4..(4 + BLOCK_SIZE)].copy_from_slice(&buf[i..(i + BLOCK_SIZE)]); 230 | self.drive.transfer(&mut wbuf)?; 231 | self.wait_not_busy()?; 232 | 233 | if !cbk(WriteEvent::Block(i, BLOCK_SIZE)) { 234 | return Ok(()); 235 | } 236 | } else { 237 | let mut wbuf: Vec = Vec::new(); 238 | wbuf.extend_from_slice(&[ 239 | SpiFlashCmd::PageProgram.into(), 240 | (addr_offset >> 16) as u8, 241 | (addr_offset >> 8) as u8, 242 | addr_offset as u8, 243 | ]); 244 | wbuf.extend_from_slice(&buf[i..buf.len()]); 245 | self.drive.transfer(&mut wbuf)?; 246 | self.wait_not_busy()?; 247 | 248 | if cbk(WriteEvent::Block(i, buf.len() - i)) { 249 | return Ok(()); 250 | } 251 | } 252 | } 253 | 254 | cbk(WriteEvent::Finish(buf.len())); 255 | return Ok(()); 256 | } 257 | } 258 | 259 | pub type RegReader = fn(spi_flash: &SpiFlash) -> Result; 260 | pub type RegWriter = 261 | fn(spi_flash: &SpiFlash, buf: &[u8]) -> Result<(), Box>; 262 | 263 | pub enum RegLen { 264 | One, 265 | Muti(usize), 266 | } 267 | 268 | #[derive(Debug)] 269 | pub enum RegReadRet { 270 | One(u8), 271 | Muti(Vec), 272 | } 273 | 274 | impl fmt::Display for RegReadRet { 275 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 276 | match self { 277 | RegReadRet::One(a) => write!(f, "0x{:02X}({:04b}_{:04b})", a, a >> 4, a & 0x0F), 278 | RegReadRet::Muti(a) => write!(f, "{:02X?}", a), 279 | } 280 | } 281 | } 282 | 283 | pub struct Register { 284 | // pub bits: Range, 285 | pub name: &'static str, 286 | pub addr: u8, 287 | pub items: Option<&'static [RegisterItem]>, 288 | pub reader: RegReader, 289 | pub writer: Option, 290 | } 291 | 292 | pub struct RegisterItem { 293 | pub name: &'static str, 294 | pub alias: &'static [&'static str], 295 | pub describe: &'static str, 296 | pub offset: u8, 297 | pub width: u8, 298 | pub access: RegisterAccess, 299 | } 300 | 301 | #[derive(Debug)] 302 | pub enum RegisterAccess { 303 | ReadOnly, 304 | ReadWrite, 305 | ReadWriteOTP, 306 | } 307 | 308 | impl fmt::Display for RegisterAccess { 309 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 310 | write!( 311 | f, 312 | "{}", 313 | match self { 314 | RegisterAccess::ReadOnly => "Volatile, read only", 315 | RegisterAccess::ReadWrite => "Non-volatile writable", 316 | RegisterAccess::ReadWriteOTP => "Non-volatile writable (OTP)", 317 | } 318 | ) 319 | } 320 | } 321 | 322 | // impl Register { 323 | // pub fn new(bits: Range) -> Register { 324 | // Register { bits } 325 | // } 326 | 327 | // pub fn new_bit(bit: usize) -> Register { 328 | // Register { bits: bit..bit + 1 } 329 | // } 330 | 331 | // pub fn read(self, buf: &[u8]) -> u32 { 332 | // read_reg_bits(buf, self.bits) 333 | // } 334 | 335 | // pub fn bit_width(&self) -> usize { 336 | // self.bits.len() 337 | // } 338 | // } 339 | 340 | pub struct RegisterRead<'a> { 341 | buf: &'a [u8], 342 | } 343 | 344 | impl<'a> RegisterRead<'_> { 345 | pub fn new(buf: &'a [u8]) -> RegisterRead { 346 | RegisterRead { buf } 347 | } 348 | 349 | pub fn read_bit(&self, bit: usize) -> Result { 350 | let buf_index = bit / 8; 351 | let bit_index = bit % 8; 352 | 353 | let ret = if self.buf[buf_index] & (1 << bit_index) != 0 { 354 | true 355 | } else { 356 | false 357 | }; 358 | 359 | return Ok(ret); 360 | } 361 | 362 | pub fn read_bits(&self, bits: Range) -> Result, &'static str> { 363 | let mut ret = Vec::new(); 364 | 365 | for i in bits { 366 | let buf_index = i / 8; 367 | let bit_index = i % 8; 368 | 369 | let b = if self.buf[buf_index] & (1 << bit_index) != 0 { 370 | true 371 | } else { 372 | false 373 | }; 374 | 375 | ret.push(b); 376 | } 377 | 378 | Ok(ret) 379 | } 380 | 381 | pub fn read_bytes(&self, bits: Range) -> Result, &'static str> { 382 | let mut ret = Vec::new(); 383 | let mut b: u8 = 0; 384 | 385 | let bit_width = if bits.start > bits.end { 386 | bits.start - bits.end 387 | } else { 388 | bits.end - bits.start 389 | }; 390 | 391 | for (k, i) in bits.enumerate() { 392 | let buf_index = i / 8; 393 | let bit_index = i % 8; 394 | 395 | b <<= 1; 396 | if self.buf[buf_index] & (1 << bit_index) != 0 { 397 | b |= 1; 398 | } 399 | 400 | if (k != 0) && (k % 8 == 0) { 401 | ret.push(b); 402 | b = 0; 403 | } 404 | } 405 | 406 | if (bit_width % 8) != 0 { 407 | ret.push(b); 408 | } 409 | 410 | Ok(ret) 411 | } 412 | } 413 | -------------------------------------------------------------------------------- /src/ch347lib/ch347lib.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | use std::{fmt, string}; 3 | 4 | use super::ch347dll::*; 5 | use crate::spi_flash::{SpiDrive, SpiFlash}; 6 | use crate::windows::basetsd::*; 7 | use clap::ValueEnum; 8 | 9 | /// 枚举设备列表 10 | /// 11 | /// # Arguments 12 | /// 13 | /// list of device_info 14 | /// 15 | /// # Examples 16 | /// 17 | /// ```rust 18 | /// println!("enum_device: {:?}", ch347_rs::enum_device()); 19 | /// ``` 20 | pub fn enum_device() -> Vec { 21 | let mut device_info_list = Vec::new(); 22 | 23 | for i in 0..16 { 24 | if let Ok(dev) = Ch347Device::new(i) { 25 | device_info_list.push(dev); 26 | } 27 | } 28 | 29 | device_info_list 30 | } 31 | 32 | #[cfg(target_os = "windows")] 33 | pub fn enum_uart_device() -> Vec { 34 | let mut device_info_list = Vec::new(); 35 | 36 | for i in 0..16 { 37 | if let Some(dev) = Ch347Device::new_serial(i) { 38 | device_info_list.push(dev); 39 | } 40 | } 41 | 42 | device_info_list 43 | } 44 | 45 | #[cfg(target_os = "windows")] 46 | pub fn open_device(index: u32) -> HANDLE { 47 | unsafe { CH347OpenDevice(index as ULONG) } 48 | } 49 | #[cfg(target_os = "linux")] 50 | pub fn open_device(index: u32) -> i32 { 51 | unsafe { CH347OpenDevice(index as ULONG) } 52 | } 53 | 54 | pub fn close_device(index: u32) { 55 | unsafe { 56 | CH347CloseDevice(index as ULONG); 57 | } 58 | } 59 | 60 | /// Returns a person with the name given them 61 | /// 62 | /// # Arguments 63 | /// 64 | /// * `device_index` - A string slice that holds the name of the person 65 | /// 66 | pub fn get_version(device_index: u32) -> (BOOL, u8, u8, u8, u8) { 67 | let mut i_driver_ver: u8 = 0; 68 | let mut i_dllver: u8 = 0; 69 | let mut ibcd_device: u8 = 0; 70 | let mut i_chip_type: u8 = 0; 71 | let ret: BOOL; 72 | 73 | unsafe { 74 | ret = CH347GetVersion( 75 | device_index as libc::c_ulong, 76 | &mut i_driver_ver, 77 | &mut i_dllver, 78 | &mut ibcd_device, 79 | &mut i_chip_type, 80 | ); 81 | } 82 | 83 | (ret, i_driver_ver, i_dllver, ibcd_device, i_chip_type) 84 | } 85 | 86 | pub fn get_device_info(device_index: u64) -> Option { 87 | let device_info = DeviceInfo::default(); 88 | let ret: BOOL; 89 | unsafe { 90 | ret = CH347GetDeviceInfor(device_index as libc::c_ulong, &device_info as *const _); 91 | } 92 | 93 | if ret == 0 { 94 | return None; 95 | } 96 | 97 | Some(device_info) 98 | } 99 | 100 | pub fn get_uart_device_info(device_index: u64) -> Option { 101 | let device_info = DeviceInfo::default(); 102 | let ret: BOOL; 103 | unsafe { 104 | ret = CH347Uart_GetDeviceInfor(device_index as libc::c_ulong, &device_info as *const _); 105 | } 106 | 107 | if ret == 0 { 108 | return None; 109 | } 110 | 111 | Some(device_info) 112 | } 113 | 114 | pub fn set_notify_callback( 115 | device_index: u32, 116 | device_id: &str, 117 | callback: fn(s: NotifyiEventStatus), 118 | ) { 119 | unsafe { 120 | let mut cbk_fn = |state: ULONG| { 121 | println!("Ch347NotifyRoutine"); 122 | callback(match state { 123 | 0 => NotifyiEventStatus::Inserted, 124 | 3 => NotifyiEventStatus::Removed, 125 | _ => NotifyiEventStatus::Unknow(state), 126 | }); 127 | }; 128 | 129 | CH347SetDeviceNotify( 130 | device_index as ULONG, 131 | // device_id.clone().as_mut_ptr(), 132 | device_id.as_ptr(), 133 | &mut cbk_fn as *mut _ as *mut libc::c_void, 134 | ); 135 | } 136 | } 137 | 138 | #[derive(Clone, Copy, Debug, ValueEnum)] 139 | pub enum I2cSpeed { 140 | Low = 0x00, // 20kHz 141 | Std = 0x01, // 100kHz 142 | Fast = 0x02, // 400kHz 143 | High = 0x03, // 750kHz 144 | } 145 | 146 | impl fmt::Display for I2cSpeed { 147 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 148 | write!( 149 | f, 150 | "{:?}-{}", 151 | self, 152 | match self { 153 | I2cSpeed::Low => "20kHz", 154 | I2cSpeed::Std => "100kHz", 155 | I2cSpeed::Fast => "400kHz", 156 | I2cSpeed::High => "750kHz", 157 | } 158 | ) 159 | } 160 | } 161 | 162 | pub fn i2c_stream( 163 | index: ULONG, 164 | wsize: u32, 165 | wbuf: *const u8, 166 | rsize: u32, 167 | rbuf: *mut u8, 168 | ) -> Result<(), ()> { 169 | unsafe { 170 | if CH347StreamI2C( 171 | index as ULONG, 172 | wsize as ULONG, 173 | wbuf as *mut libc::c_void, 174 | rsize as ULONG, 175 | rbuf as *mut libc::c_void, 176 | ) == 0 177 | { 178 | return Err(()); 179 | } 180 | Ok(()) 181 | } 182 | } 183 | 184 | pub fn gpio_get(index: ULONG) -> Result<(u8, u8), string::String> { 185 | let mut dir: u8 = 0; 186 | let gpio_dir: *mut u8 = &mut dir; 187 | let mut data: u8 = 0; 188 | let gpio_data: *mut u8 = &mut data; 189 | 190 | unsafe { 191 | match CH347GPIO_Get(index as ULONG, gpio_dir, gpio_data) { 192 | 0 => Err("fail".to_string()), 193 | _ => Ok((dir, data)), 194 | } 195 | } 196 | } 197 | 198 | /** 199 | * Enable flag: corresponding to bit 0-7, corresponding to GPIO0-7. 200 | * 201 | * Set the I/O direction. If a certain bit is cleared to 0, the 202 | * corresponding pin is input, and if a certain position is set to 203 | * 1, the corresponding pin is output. GPIO0-7 corresponds to bits 204 | * 0-7. 205 | * 206 | * Output data, if the I/O direction is output, then the 207 | * corresponding pin outputs low level when a certain bit is cleared 208 | * to 0, and the corresponding pin outputs high level when a certain 209 | * position is 1. 210 | */ 211 | pub fn gpio_set( 212 | index: ULONG, 213 | gpio_enable: u8, 214 | gpio_dir: u8, 215 | gpio_data: u8, 216 | ) -> Result<(), string::String> { 217 | unsafe { 218 | match CH347GPIO_Set(index as ULONG, gpio_enable, gpio_dir, gpio_data) { 219 | 0 => Err("".to_string()), 220 | _ => Ok(()), 221 | } 222 | } 223 | } 224 | 225 | pub enum CH347TransType { 226 | Parallel, 227 | Serial, 228 | } 229 | 230 | pub struct Ch347Device { 231 | #[cfg(target_os = "windows")] 232 | index: ULONG, 233 | 234 | #[cfg(target_os = "linux")] 235 | fd: i32, 236 | 237 | ts_type: CH347TransType, 238 | spi_cfg: SpiConfig, 239 | } 240 | 241 | pub fn enum_ch347_device() -> Vec { 242 | let mut device_list: Vec = Vec::new(); 243 | 244 | for i in enum_device() { 245 | device_list.push(i); 246 | } 247 | 248 | #[cfg(target_os = "windows")] 249 | { 250 | for i in enum_uart_device() { 251 | device_list.push(i); 252 | } 253 | } 254 | 255 | device_list 256 | } 257 | 258 | impl Ch347Device { 259 | #[cfg(target_os = "windows")] 260 | pub fn new(index: u32) -> Result { 261 | unsafe { 262 | if CH347OpenDevice(index as ULONG) == INVALID_HANDLE_VALUE { 263 | return Err("CH347OpenDevice Fail"); 264 | } 265 | } 266 | 267 | Ok(Ch347Device { 268 | index: index as ULONG, 269 | ts_type: CH347TransType::Parallel, 270 | spi_cfg: SpiConfig::default(), 271 | }) 272 | } 273 | 274 | #[cfg(target_os = "linux")] 275 | pub fn new(index: u32) -> Result { 276 | let fd = unsafe { CH347OpenDevice(index as ULONG) }; 277 | if fd <= 0 { 278 | return Err("CH347OpenDevice Fail"); 279 | } 280 | 281 | Ok(Ch347Device { 282 | fd, 283 | ts_type: CH347TransType::Parallel, 284 | spi_cfg: SpiConfig::default(), 285 | }) 286 | } 287 | 288 | #[cfg(target_os = "windows")] 289 | pub fn new_serial(index: u32) -> Option { 290 | unsafe { 291 | if CH347Uart_Open(index as ULONG) == INVALID_HANDLE_VALUE { 292 | return None; 293 | } 294 | } 295 | 296 | Some(Ch347Device { 297 | index: index as ULONG, 298 | ts_type: CH347TransType::Serial, 299 | spi_cfg: SpiConfig::default(), 300 | }) 301 | } 302 | 303 | #[cfg(target_os = "windows")] 304 | pub fn get_dev_index(&self) -> ULONG { 305 | self.index 306 | } 307 | 308 | #[cfg(target_os = "linux")] 309 | pub fn get_dev_index(&self) -> ULONG { 310 | self.fd as ULONG 311 | } 312 | 313 | pub fn spi_flash(mut self) -> Result, Box> { 314 | self.spi_cfg = self.get_raw_spi_config()?; 315 | Ok(SpiFlash::new(self)) 316 | } 317 | 318 | pub fn get_raw_info(&self) -> Option { 319 | let device_info = DeviceInfo::default(); 320 | 321 | match self.ts_type { 322 | CH347TransType::Parallel => { 323 | unsafe { 324 | if CH347GetDeviceInfor( 325 | self.get_dev_index() as libc::c_ulong, 326 | &device_info as *const _, 327 | ) == 0 328 | { 329 | return None; 330 | } 331 | } 332 | Some(device_info) 333 | } 334 | CH347TransType::Serial => { 335 | unsafe { 336 | if CH347Uart_GetDeviceInfor(self.get_dev_index(), &device_info as *const _) == 0 337 | { 338 | return None; 339 | } 340 | } 341 | Some(device_info) 342 | } 343 | } 344 | } 345 | 346 | pub fn get_raw_spi_config(&self) -> Result { 347 | let mut spicfg = SpiConfig::default(); 348 | unsafe { 349 | if CH347SPI_GetCfg(self.get_dev_index(), &mut spicfg) == 0 { 350 | return Err("CH347SPI_GetCfg Fail"); 351 | } 352 | } 353 | 354 | Ok(spicfg) 355 | } 356 | 357 | pub fn apply_spi_config(&mut self) -> Result<(), &'static str> { 358 | unsafe { 359 | if CH347SPI_Init(self.get_dev_index(), &mut self.spi_cfg) == 0 { 360 | return Err("CH347SPI_Init Fail"); 361 | } 362 | } 363 | 364 | Ok(()) 365 | } 366 | 367 | pub fn change_spi_raw_config(&mut self, f: F) -> Result<(), &'static str> 368 | where 369 | F: Fn(&mut SpiConfig), 370 | { 371 | f(&mut self.spi_cfg); 372 | self.apply_spi_config()?; 373 | 374 | Ok(()) 375 | } 376 | 377 | pub fn i2c_set(&self, speed: I2cSpeed) { 378 | unsafe { 379 | CH347I2C_Set(self.get_dev_index(), speed as ULONG); 380 | } 381 | } 382 | 383 | pub fn i2c_device_detect(&self, addr: u8) -> bool { 384 | unsafe { 385 | let mut wbuf: [u8; 1] = [addr << 1]; 386 | if CH347StreamI2C( 387 | self.get_dev_index(), 388 | 1, 389 | wbuf.as_mut_ptr() as *mut libc::c_void, 390 | 0, 391 | std::ptr::null_mut::(), 392 | ) == 0 393 | { 394 | return false; 395 | } 396 | } 397 | true 398 | } 399 | 400 | pub fn i2c_stream(&self, wbuf: &[u8], rbuf: &mut [u8]) -> Result<(), ()> { 401 | i2c_stream( 402 | self.get_dev_index(), 403 | wbuf.len() as u32, 404 | wbuf.as_ptr(), 405 | rbuf.len() as u32, 406 | rbuf.as_mut_ptr(), 407 | ) 408 | } 409 | } 410 | 411 | impl SpiDrive for Ch347Device { 412 | fn transfer(&self, iobuf: &mut [u8]) -> Result<(), &'static str> { 413 | unsafe { 414 | if CH347StreamSPI4( 415 | self.get_dev_index(), 416 | 0x80, 417 | iobuf.len() as ULONG, 418 | iobuf.as_mut_ptr() as *mut libc::c_void, 419 | ) == 0 420 | { 421 | return Err("ch347 transfer failed"); 422 | } 423 | } 424 | 425 | Ok(()) 426 | } 427 | 428 | fn write_after_read( 429 | &self, 430 | write_len: u32, 431 | read_len: u32, 432 | iobuf: &mut [u8], 433 | ) -> Result<(), &'static str> { 434 | unsafe { 435 | if CH347SPI_Read( 436 | self.get_dev_index(), 437 | 0x80, 438 | write_len as ULONG, 439 | &mut (read_len as ULONG), 440 | iobuf.as_mut_ptr() as *mut libc::c_void, 441 | ) == 0 442 | { 443 | return Err("ch347 transfer failed"); 444 | } 445 | } 446 | 447 | Ok(()) 448 | } 449 | } 450 | 451 | impl Drop for Ch347Device { 452 | fn drop(&mut self) { 453 | unsafe { 454 | CH347CloseDevice(self.get_dev_index()); 455 | } 456 | } 457 | } 458 | -------------------------------------------------------------------------------- /lib/Arm/CH347SPILIB.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ch34x_lib.h for ch341 in Epp/MEM/I2C/SPI/GPIO 3 | * Copyright (C) WCH 2019 4 | * Running Environment: Linux 5 | * Version:2.3 6 | */ 7 | 8 | #ifndef _CH34X_LIB_H 9 | #define _CH34X_LIB_H 10 | 11 | #ifndef CHAR 12 | #define CHAR char 13 | #endif 14 | 15 | #ifndef UCHAR 16 | #define UCHAR unsigned char 17 | #endif 18 | 19 | #ifndef USHORT 20 | #define USHORT unsigned short 21 | #endif 22 | 23 | #ifndef ULONG 24 | #define ULONG unsigned long 25 | #endif 26 | 27 | #ifndef LONGLONG 28 | #define LONGLONG unsigned long long 29 | #endif 30 | 31 | #ifndef PUCHAR 32 | #define PUCHAR unsigned char * 33 | #endif 34 | 35 | #ifndef PCHAR 36 | #define PCHAR char * 37 | #endif 38 | 39 | #ifndef PUSHORT 40 | #define PUSHORT unsigned short * 41 | #endif 42 | 43 | #ifndef PULONG 44 | #define PULONG unsigned long * 45 | #endif 46 | 47 | #ifndef VOID 48 | #define VOID void 49 | #endif 50 | 51 | #ifndef PVOID 52 | #define PVOID void * 53 | #endif 54 | 55 | #define true 1 56 | #define false 0 57 | 58 | #define TRUE true 59 | #define FALSE false 60 | 61 | #ifndef min 62 | #define min(x, y) (((x) < (y)) ? (x) : (y)) 63 | #endif 64 | 65 | #ifndef max 66 | #define max(x, y) (((x) < (y)) ? (y) : (x)) 67 | #endif 68 | 69 | typedef enum 70 | { 71 | FALSE_H = 0, 72 | TRUE_H = !FALSE_H 73 | } BOOL; 74 | 75 | #define MAX_PATH 512 76 | 77 | #define CH341_PACKET_LENGTH 32 78 | #define CH341_PKT_LEN_SHORT 8 79 | 80 | #define CH341_MAX_NUMBER 16 81 | #define MAX_BUFFER_LENGTH 0x1000 82 | #define DEFAULT_BUFFER_LEN 0x0400 83 | 84 | #define mCH341_PACKET_LENGTH 32 85 | #define mCH341_MAX_NUMBER 16 86 | 87 | #define CH347_USB_VENDOR 0 88 | #define CH347_USB_HID 2 89 | #define CH347_USB_VCP 3 90 | 91 | // USB to SPI CMD 92 | #define SET_CS 0 93 | #define CLR_CS 1 94 | 95 | #define USB20_CMD_SPI_INIT 0xC0 // 用于初始化SPI接口,设置SPI接口的数据位、时钟分频、高低字节顺序等等参数。 96 | #define USB20_CMD_SPI_CONTROL 0xC1 // SPI接口控制命令,用于控制SPI接口片选引脚输出高低电平以及电平延时时间。 97 | #define USB20_CMD_SPI_RD_WR 0xC2 // SPI接口常规读取写入数据命令,用于SPI接口通用读取写入操作,一般用于简短常规命令操作。该命令写N个字节数据的同时会回读N个字节数据。 98 | #define USB20_CMD_SPI_BLCK_RD 0xC3 // SPI接口批量读取数据命令,用于SPI接口批量读取数据,一般用于批量数据的读取操作。启用该命令读取数据后,读取的数据会按最大包大小进行打包上传,直到所有数据读取返回完毕。 99 | #define USB20_CMD_SPI_BLCK_WR 0xC4 // SPI接口批量写入数据命令,用于SPI接口批量写入数据,一般用于批量数据的写入操作。 100 | #define USB20_CMD_INFO_RD 0xCA // 参数获取,用于获取SPI接口相关参数等 101 | 102 | #define mMAX_BUFFER_LENGTH 0x1000 103 | #define USB20_CMD_HEADER 3 104 | 105 | #define SPI_CS_ACTIVE 0x00 106 | #define SPI_CS_DEACTIVE 0x01 107 | 108 | /* SPI_data_direction */ 109 | #define SPI_Direction_2Lines_FullDuplex ((USHORT)0x0000) 110 | #define SPI_Direction_2Lines_RxOnly ((USHORT)0x0400) 111 | #define SPI_Direction_1Line_Rx ((USHORT)0x8000) 112 | #define SPI_Direction_1Line_Tx ((USHORT)0xC000) 113 | 114 | /* SPI_mode */ 115 | #define SPI_Mode_Master ((USHORT)0x0104) 116 | #define SPI_Mode_Slave ((USHORT)0x0000) 117 | 118 | /* SPI_data_size */ 119 | #define SPI_DataSize_16b ((USHORT)0x0800) 120 | #define SPI_DataSize_8b ((USHORT)0x0000) 121 | 122 | /* SPI_Clock_Polarity */ 123 | #define SPI_CPOL_Low ((USHORT)0x0000) 124 | #define SPI_CPOL_High ((USHORT)0x0002) 125 | 126 | /* SPI_Clock_Phase */ 127 | #define SPI_CPHA_1Edge ((USHORT)0x0000) 128 | #define SPI_CPHA_2Edge ((USHORT)0x0001) 129 | 130 | /* SPI_Slave_Select_management */ 131 | #define SPI_NSS_Soft ((USHORT)0x0200) 132 | #define SPI_NSS_Hard ((USHORT)0x0000) 133 | 134 | #define mWIN32_COMMAND_HEAD 32 // WIN32命令接口的头长度 135 | 136 | /* SPI_MSB_LSB_transmission */ 137 | #define SPI_FirstBit_MSB ((USHORT)0x0000) 138 | #define SPI_FirstBit_LSB ((USHORT)0x0080) 139 | 140 | #define mMAX_BUFFER_LENGTH 0x1000 // 数据缓冲区最大长度4096 141 | 142 | #define mMAX_COMMAND_LENGTH (mWIN32_COMMAND_HEAD + mMAX_BUFFER_LENGTH) // 最大数据长度加上命令结构头的长度 143 | 144 | #define mDEFAULT_BUFFER_LEN 0x0400 // 数据缓冲区默认长度1024 145 | 146 | #define mDEFAULT_COMMAND_LEN (mWIN32_COMMAND_HEAD + mDEFAULT_BUFFER_LEN) // 默认数据长度加上命令结构头的长度 147 | 148 | /* SPI Init structure definition */ 149 | typedef struct _SPI_InitTypeDef 150 | { 151 | USHORT SPI_Direction; /* Specifies th e SPI unidirectional or bidirectional data mode. 152 | This parameter can be a value of @ref SPI_data_direction */ 153 | 154 | USHORT SPI_Mode; /* Specifies the SPI operating mode. 155 | This parameter can be a value of @ref SPI_mode */ 156 | 157 | USHORT SPI_DataSize; /* Specifies the SPI data size. 158 | This parameter can be a value of @ref SPI_data_size */ 159 | 160 | USHORT SPI_CPOL; /* Specifies the serial clock steady state. 161 | This parameter can be a value of @ref SPI_Clock_Polarity */ 162 | 163 | USHORT SPI_CPHA; /* Specifies the clock active edge for the bit capture. 164 | This parameter can be a value of @ref SPI_Clock_Phase */ 165 | 166 | USHORT SPI_NSS; /* Specifies whether the NSS signal is managed by 167 | hardware (NSS pin) or by software using the SSI bit. 168 | This parameter can be a value of @ref SPI_Slave_Select_management */ 169 | 170 | USHORT SPI_BaudRatePrescaler; /* Specifies the Baud Rate prescaler value which will be 171 | used to configure the transmit and receive SCK clock. 172 | This parameter can be a value of @ref SPI_BaudRate_Prescaler. 173 | @note The communication clock is derived from the master 174 | clock. The slave clock does not need to be set. */ 175 | 176 | USHORT SPI_FirstBit; /* Specifies whether data transfers start from MSB or LSB bit. 177 | This parameter can be a value of @ref SPI_MSB_LSB_transmission */ 178 | 179 | USHORT SPI_CRCPolynomial; /* Specifies the polynomial used for the CRC calculation. */ 180 | } SPI_InitTypeDef; 181 | 182 | typedef struct _SpiUSBCFG 183 | { 184 | SPI_InitTypeDef SPIInitCfg; 185 | USHORT SpiWriteReadInterval; // SPI接口常规读取写入数据命令(DEF_CMD_SPI_RD_WR)),单位为uS 186 | UCHAR SpiOutDefaultData; // SPI读数据时默认输出数据 187 | UCHAR OtherCfg; // 1个字节杂项控制; 188 | // 位7:片选CS1极性控制:0:低电平有效;1:有电平有效; 189 | // 位6:片选CS2极性控制:0:低电平有效;1:有电平有效; 190 | // 位5:IIC时钟延展功能控制:0:禁止;1:使能; 191 | // 位4:IIC读取最后1个字节完成时生成或不生成NACK; 192 | // 位3-0:保留; 193 | UCHAR Reserved[4]; // 保留 194 | } SpiHwCfgS, *PSpiHwCfgS; 195 | 196 | typedef struct _CH347_USB_CMD_S 197 | { 198 | UCHAR mFunction; 199 | USHORT mLength; 200 | UCHAR mBuffer[512]; 201 | } CH347SPI_CMD, *mPCH347SPI_CMD; 202 | 203 | typedef struct _StreamUSBCFG 204 | { 205 | SPI_InitTypeDef SPIInitCfg; 206 | USHORT SpiWriteReadInterval; // SPI接口常规读取写入数据命令(DEF_CMD_SPI_RD_WR)),单位为uS 207 | UCHAR SpiOutDefaultData; // SPI读数据时默认输出数据 208 | UCHAR OtherCfg; // 1个字节杂项控制; 209 | // 位7:片选CS1极性控制:0:低电平有效;1:有电平有效; 210 | // 位6:片选CS2极性控制:0:低电平有效;1:有电平有效; 211 | // 位5:IIC时钟延展功能控制:0:禁止;1:使能; 212 | // 位4:IIC读取最后1个字节完成时生成或不生成NACK; 213 | // 位3-0:保留; 214 | UCHAR Reserved[4]; // 保留 215 | } StreamHwCfgS, *PStreamHwCfgS; 216 | 217 | #pragma pack(1) 218 | // SPI控制器配置 219 | typedef struct _SPI_CONFIG 220 | { 221 | UCHAR iMode; // 0-3:SPI Mode0/1/2/3 222 | UCHAR iClock; // 0=60MHz, 1=30MHz, 2=15MHz, 3=7.5MHz, 4=3.75MHz, 5=1.875MHz, 6=937.5KHz,7=468.75KHz 223 | UCHAR iByteOrder; // 0=低位在前(LSB), 1=高位在前(MSB) 224 | USHORT iSpiWriteReadInterval; // SPI接口常规读取写入数据命令,单位为uS 225 | UCHAR iSpiOutDefaultData; // SPI读数据时默认输出数据 226 | ULONG iChipSelect; // 片选控制, 位7为0则忽略片选控制, 位7为1则参数有效: 位1位0为00/01分别选择CS1/CS2引脚作为低电平有效片选 227 | UCHAR CS1Polarity; // 位0:片选CS1极性控制:0:低电平有效;1:高电平有效; 228 | UCHAR CS2Polarity; // 位0:片选CS2极性控制:0:低电平有效;1:高电平有效; 229 | USHORT iIsAutoDeativeCS; // 操作完成后是否自动撤消片选 230 | USHORT iActiveDelay; // 设置片选后执行读写操作的延时时间,单位us 231 | ULONG iDelayDeactive; // 撤消片选后执行读写操作的延时时间,单位us 232 | } mSpiCfgS, *mPSpiCfgS; 233 | 234 | //设备信息 235 | typedef struct _DEV_INFOR 236 | { 237 | UCHAR iIndex; // 当前打开序号 238 | UCHAR DevicePath[MAX_PATH]; // 设备链接名,用于CreateFile 239 | UCHAR UsbClass; // 0:CH347_USB_CH341, 2:CH347_USB_HID,3:CH347_USB_VCP 240 | UCHAR FuncType; // 0:CH347_FUNC_UART,1:CH347_FUNC_SPI_I2C,2:CH347_FUNC_JTAG_I2C 241 | CHAR DeviceID[64]; // USB\VID_xxxx&PID_xxxx 242 | UCHAR ChipMode; // 芯片模式,0:Mode0(UART0/1); 1:Mode1(Uart1+SPI+I2C); 2:Mode2(HID Uart1+SPI+I2C) 3:Mode3(Uart1+Jtag+IIC) 243 | int DevHandle; // 设备句柄 244 | USHORT BulkOutEndpMaxSize; // 上传端点大小 245 | USHORT BulkInEndpMaxSize; // 下传端点大小 246 | UCHAR UsbSpeedType; // USB速度类型,0:FS,1:HS,2:SS 247 | UCHAR CH347IfNum; // 设备接口号: 0:UART,1:SPI/IIC/JTAG/GPIO 248 | UCHAR DataUpEndp; // 端点地址 249 | UCHAR DataDnEndp; // 端点地址 250 | CHAR ProductString[64]; // USB产品字符串 251 | CHAR ManufacturerString[64]; // USB厂商字符串 252 | ULONG WriteTimeout; // USB写超时 253 | ULONG ReadTimeout; // USB读超时 254 | CHAR FuncDescStr[64]; // 接口功能描述符 255 | UCHAR FirewareVer; // 固件版本 256 | } mDeviceInforS, *mPDeviceInforS; 257 | 258 | typedef struct _DevObj 259 | { 260 | UCHAR iIndex; //当前打开序号 261 | UCHAR DevicePath[MAX_PATH]; 262 | UCHAR UsbClass; // 0:CH341 Vendor; 1:CH347 Vendor; 2:HID 263 | UCHAR FuncType; // 0:UART1; 1:SPI+IIC; 2:JTAG+IIC 264 | CHAR DeviceID[64]; // USB\VID_xxxx&PID_xxxx 265 | UCHAR Mode; //芯片模式,0:Mode0(UART*2); 1:Mode1(Uart1+SPI+IIC); 2:Mode2(HID Uart1+SPI+IIC) 3:Mode3(Uart1+Jtag+IIC) 266 | USHORT BulkOutEndpMaxSize; //上传端点大小 267 | USHORT BulkInEndpMaxSize; //下传端点大小 268 | UCHAR UsbSpeedType; // USB速度类型,0:FS,1:HS,2:SS 269 | UCHAR CH347IfNum; // USB接口号 270 | UCHAR DataUpEndp; //端点地址 271 | UCHAR DataDnEndp; //端点地址 272 | CHAR ProductString[64]; // USB产品字符串 273 | CHAR ManufacturerString[64]; // USB厂商字符串 274 | ULONG WriteTimeout; // USB写超时 275 | ULONG ReadTimeout; // USB读超时 276 | CHAR FuncDescStr[64]; 277 | UCHAR FirewareVer; //固件版本 278 | ULONG CmdDataMaxSize; 279 | } mDevObjS, *mPDevObj; 280 | #pragma pack() 281 | 282 | // CH347模式公用函数,支持CH347所有模式下的打开、关闭、USB读、USB写,包含HID 283 | //打开USB设备 284 | int CH347OpenDevice(ULONG DevI); 285 | 286 | //关闭USB设备 287 | BOOL CH347CloseDevice(ULONG iIndex); 288 | 289 | // 读取USB数据块 290 | BOOL CH347ReadData(ULONG iIndex, // 指定设备序号 291 | PVOID oBuffer, // 指向一个足够大的缓冲区,用于保存读取的数据 292 | PULONG ioLength); // 指向长度单元,输入时为准备读取的长度,返回后为实际读取的长度 293 | 294 | // 写取USB数据块 295 | BOOL CH347WriteData(ULONG iIndex, // 指定设备序号 296 | PVOID iBuffer, // 指向一个缓冲区,放置准备写出的数据 297 | PULONG ioLength); // 指向长度单元,输入时为准备写出的长度,返回后为实际写出的长度 298 | 299 | /***************SPI********************/ 300 | // SPI控制器初始化 301 | BOOL CH347SPI_Init(ULONG iIndex, mSpiCfgS *SpiCfg); 302 | 303 | //获取SPI控制器配置信息 304 | BOOL CH347SPI_GetCfg(ULONG iIndex, mSpiCfgS *SpiCfg); 305 | 306 | //设置片选状态,使用前需先调用CH347SPI_Init对CS进行设置 307 | BOOL CH347SPI_ChangeCS(ULONG iIndex, // 指定设备序号 308 | UCHAR iStatus); // 0=撤消片选,1=设置片选 309 | 310 | //设置SPI片选 311 | BOOL CH347SPI_SetChipSelect(ULONG iIndex, // 指定设备序号 312 | USHORT iEnableSelect, // 低八位为CS1,高八位为CS2; 字节值为1=设置CS,为0=忽略此CS设置 313 | USHORT iChipSelect, // 低八位为CS1,高八位为CS2;片选输出,0=撤消片选,1=设置片选 314 | ULONG iIsAutoDeativeCS, // 低16位为CS1,高16位为CS2;操作完成后是否自动撤消片选 315 | ULONG iActiveDelay, // 低16位为CS1,高16位为CS2;设置片选后执行读写操作的延时时间,单位us 316 | ULONG iDelayDeactive); // 低16位为CS1,高16位为CS2;撤消片选后执行读写操作的延时时间,单位us 317 | 318 | // SPI4写数据 319 | BOOL CH347SPI_Write(ULONG iIndex, // 指定设备序号 320 | ULONG iChipSelect, // 片选控制, 位7为0则忽略片选控制, 位7为1进行片选操作 321 | ULONG iLength, // 准备传输的数据字节数 322 | ULONG iWriteStep, // 准备读取的单个块的长度 323 | PVOID ioBuffer); // 指向一个缓冲区,放置准备从MOSI写出的数据 324 | 325 | // SPI4读数据.无需先写数据,效率较CH347SPI_WriteRead高很多 326 | BOOL CH347SPI_Read(ULONG iIndex, // 指定设备序号 327 | ULONG iChipSelect, // 片选控制, 位7为0则忽略片选控制, 位7为1进行片选操作 328 | ULONG oLength, // 准备发出的字节数 329 | PULONG iLength, // 准备读入的数据字节数 330 | PVOID ioBuffer); // 指向一个缓冲区,放置准备从DOUT写出的数据,返回后是从DIN读入的数据 331 | 332 | // 处理SPI数据流,4线接口 333 | BOOL CH347SPI_WriteRead(ULONG iIndex, // 指定设备序号 334 | ULONG iChipSelect, // 片选控制, 位7为0则忽略片选控制, 位7为1则操作片选 335 | ULONG iLength, // 准备传输的数据字节数 336 | PVOID ioBuffer); // 指向一个缓冲区,放置准备从DOUT写出的数据,返回后是从DIN读入的数据 337 | 338 | // 处理SPI数据流,4线接口 339 | BOOL CH347StreamSPI4(ULONG iIndex, // 指定设备序号 340 | ULONG iChipSelect, // 片选控制, 位7为0则忽略片选控制, 位7为1则参数有效 341 | ULONG iLength, // 准备传输的数据字节数 342 | PVOID ioBuffer); // 指向一个缓冲区,放置准备从DOUT写出的数据,返回后是从DIN读入的数据 343 | 344 | /********IIC***********/ 345 | typedef enum _EEPROM_TYPE 346 | { // EEPROM型号 347 | ID_24C01, 348 | ID_24C02, 349 | ID_24C04, 350 | ID_24C08, 351 | ID_24C16, 352 | ID_24C32, 353 | ID_24C64, 354 | ID_24C128, 355 | ID_24C256, 356 | ID_24C512, 357 | ID_24C1024, 358 | ID_24C2048, 359 | ID_24C4096 360 | } EEPROM_TYPE; 361 | 362 | // 设置串口流模式 363 | BOOL CH347I2C_Set(ULONG iIndex, // 指定设备序号 364 | ULONG iMode); // 指定模式,见下行 365 | // 位1-位0: I2C接口速度/SCL频率, 00=低速/20KHz,01=标准/100KHz(默认值),10=快速/400KHz,11=高速/750KHz 366 | // 其它保留,必须为0 367 | 368 | // 设置硬件异步延时,调用后很快返回,而在下一个流操作之前延时指定毫秒数 369 | BOOL CH347I2C_SetDelaymS(ULONG iIndex, // 指定设备序号 370 | ULONG iDelay); // 指定延时的毫秒数 371 | 372 | // 处理I2C数据流,2线接口,时钟线为SCL引脚,数据线为SDA引脚 373 | BOOL CH347StreamI2C(ULONG iIndex, // 指定设备序号 374 | ULONG iWriteLength, // 准备写出的数据字节数 375 | PVOID iWriteBuffer, // 指向一个缓冲区,放置准备写出的数据,首字节通常是I2C设备地址及读写方向位 376 | ULONG iReadLength, // 准备读取的数据字节数 377 | PVOID oReadBuffer); // 指向一个缓冲区,返回后是读入的数据 378 | 379 | BOOL CH347ReadEEPROM( // 从EEPROM中读取数据块,速度约56K字节 380 | ULONG iIndex, // 指定CH341设备序号 381 | EEPROM_TYPE iEepromID, // 指定EEPROM型号 382 | ULONG iAddr, // 指定数据单元的地址 383 | ULONG iLength, // 准备读取的数据字节数 384 | PUCHAR oBuffer); // 指向一个缓冲区,返回后是读入的数据 385 | 386 | // 向EEPROM中写入数据块 387 | BOOL CH347WriteEEPROM(ULONG iIndex, // 指定设备序号 388 | EEPROM_TYPE iEepromID, // 指定EEPROM型号 389 | ULONG iAddr, // 指定数据单元的地址 390 | ULONG iLength, // 准备写出的数据字节数 391 | PUCHAR iBuffer); // 指向一个缓冲区,放置准备写出的数据 392 | #endif -------------------------------------------------------------------------------- /lib/Mips/CH347SPILIB.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ch34x_lib.h for ch341 in Epp/MEM/I2C/SPI/GPIO 3 | * Copyright (C) WCH 2019 4 | * Running Environment: Linux 5 | * Version:2.3 6 | */ 7 | 8 | #ifndef _CH34X_LIB_H 9 | #define _CH34X_LIB_H 10 | 11 | #ifndef CHAR 12 | #define CHAR char 13 | #endif 14 | 15 | #ifndef UCHAR 16 | #define UCHAR unsigned char 17 | #endif 18 | 19 | #ifndef USHORT 20 | #define USHORT unsigned short 21 | #endif 22 | 23 | #ifndef ULONG 24 | #define ULONG unsigned long 25 | #endif 26 | 27 | #ifndef LONGLONG 28 | #define LONGLONG unsigned long long 29 | #endif 30 | 31 | #ifndef PUCHAR 32 | #define PUCHAR unsigned char * 33 | #endif 34 | 35 | #ifndef PCHAR 36 | #define PCHAR char * 37 | #endif 38 | 39 | #ifndef PUSHORT 40 | #define PUSHORT unsigned short * 41 | #endif 42 | 43 | #ifndef PULONG 44 | #define PULONG unsigned long * 45 | #endif 46 | 47 | #ifndef VOID 48 | #define VOID void 49 | #endif 50 | 51 | #ifndef PVOID 52 | #define PVOID void * 53 | #endif 54 | 55 | #define true 1 56 | #define false 0 57 | 58 | #define TRUE true 59 | #define FALSE false 60 | 61 | #ifndef min 62 | #define min(x, y) (((x) < (y)) ? (x) : (y)) 63 | #endif 64 | 65 | #ifndef max 66 | #define max(x, y) (((x) < (y)) ? (y) : (x)) 67 | #endif 68 | 69 | typedef enum 70 | { 71 | FALSE_H = 0, 72 | TRUE_H = !FALSE_H 73 | } BOOL; 74 | 75 | #define MAX_PATH 512 76 | 77 | #define CH341_PACKET_LENGTH 32 78 | #define CH341_PKT_LEN_SHORT 8 79 | 80 | #define CH341_MAX_NUMBER 16 81 | #define MAX_BUFFER_LENGTH 0x1000 82 | #define DEFAULT_BUFFER_LEN 0x0400 83 | 84 | #define mCH341_PACKET_LENGTH 32 85 | #define mCH341_MAX_NUMBER 16 86 | 87 | #define CH347_USB_VENDOR 0 88 | #define CH347_USB_HID 2 89 | #define CH347_USB_VCP 3 90 | 91 | // USB to SPI CMD 92 | #define SET_CS 0 93 | #define CLR_CS 1 94 | 95 | #define USB20_CMD_SPI_INIT 0xC0 // 用于初始化SPI接口,设置SPI接口的数据位、时钟分频、高低字节顺序等等参数。 96 | #define USB20_CMD_SPI_CONTROL 0xC1 // SPI接口控制命令,用于控制SPI接口片选引脚输出高低电平以及电平延时时间。 97 | #define USB20_CMD_SPI_RD_WR 0xC2 // SPI接口常规读取写入数据命令,用于SPI接口通用读取写入操作,一般用于简短常规命令操作。该命令写N个字节数据的同时会回读N个字节数据。 98 | #define USB20_CMD_SPI_BLCK_RD 0xC3 // SPI接口批量读取数据命令,用于SPI接口批量读取数据,一般用于批量数据的读取操作。启用该命令读取数据后,读取的数据会按最大包大小进行打包上传,直到所有数据读取返回完毕。 99 | #define USB20_CMD_SPI_BLCK_WR 0xC4 // SPI接口批量写入数据命令,用于SPI接口批量写入数据,一般用于批量数据的写入操作。 100 | #define USB20_CMD_INFO_RD 0xCA // 参数获取,用于获取SPI接口相关参数等 101 | 102 | #define mMAX_BUFFER_LENGTH 0x1000 103 | #define USB20_CMD_HEADER 3 104 | 105 | #define SPI_CS_ACTIVE 0x00 106 | #define SPI_CS_DEACTIVE 0x01 107 | 108 | /* SPI_data_direction */ 109 | #define SPI_Direction_2Lines_FullDuplex ((USHORT)0x0000) 110 | #define SPI_Direction_2Lines_RxOnly ((USHORT)0x0400) 111 | #define SPI_Direction_1Line_Rx ((USHORT)0x8000) 112 | #define SPI_Direction_1Line_Tx ((USHORT)0xC000) 113 | 114 | /* SPI_mode */ 115 | #define SPI_Mode_Master ((USHORT)0x0104) 116 | #define SPI_Mode_Slave ((USHORT)0x0000) 117 | 118 | /* SPI_data_size */ 119 | #define SPI_DataSize_16b ((USHORT)0x0800) 120 | #define SPI_DataSize_8b ((USHORT)0x0000) 121 | 122 | /* SPI_Clock_Polarity */ 123 | #define SPI_CPOL_Low ((USHORT)0x0000) 124 | #define SPI_CPOL_High ((USHORT)0x0002) 125 | 126 | /* SPI_Clock_Phase */ 127 | #define SPI_CPHA_1Edge ((USHORT)0x0000) 128 | #define SPI_CPHA_2Edge ((USHORT)0x0001) 129 | 130 | /* SPI_Slave_Select_management */ 131 | #define SPI_NSS_Soft ((USHORT)0x0200) 132 | #define SPI_NSS_Hard ((USHORT)0x0000) 133 | 134 | #define mWIN32_COMMAND_HEAD 32 // WIN32命令接口的头长度 135 | 136 | /* SPI_MSB_LSB_transmission */ 137 | #define SPI_FirstBit_MSB ((USHORT)0x0000) 138 | #define SPI_FirstBit_LSB ((USHORT)0x0080) 139 | 140 | #define mMAX_BUFFER_LENGTH 0x1000 // 数据缓冲区最大长度4096 141 | 142 | #define mMAX_COMMAND_LENGTH (mWIN32_COMMAND_HEAD + mMAX_BUFFER_LENGTH) // 最大数据长度加上命令结构头的长度 143 | 144 | #define mDEFAULT_BUFFER_LEN 0x0400 // 数据缓冲区默认长度1024 145 | 146 | #define mDEFAULT_COMMAND_LEN (mWIN32_COMMAND_HEAD + mDEFAULT_BUFFER_LEN) // 默认数据长度加上命令结构头的长度 147 | 148 | /* SPI Init structure definition */ 149 | typedef struct _SPI_InitTypeDef 150 | { 151 | USHORT SPI_Direction; /* Specifies th e SPI unidirectional or bidirectional data mode. 152 | This parameter can be a value of @ref SPI_data_direction */ 153 | 154 | USHORT SPI_Mode; /* Specifies the SPI operating mode. 155 | This parameter can be a value of @ref SPI_mode */ 156 | 157 | USHORT SPI_DataSize; /* Specifies the SPI data size. 158 | This parameter can be a value of @ref SPI_data_size */ 159 | 160 | USHORT SPI_CPOL; /* Specifies the serial clock steady state. 161 | This parameter can be a value of @ref SPI_Clock_Polarity */ 162 | 163 | USHORT SPI_CPHA; /* Specifies the clock active edge for the bit capture. 164 | This parameter can be a value of @ref SPI_Clock_Phase */ 165 | 166 | USHORT SPI_NSS; /* Specifies whether the NSS signal is managed by 167 | hardware (NSS pin) or by software using the SSI bit. 168 | This parameter can be a value of @ref SPI_Slave_Select_management */ 169 | 170 | USHORT SPI_BaudRatePrescaler; /* Specifies the Baud Rate prescaler value which will be 171 | used to configure the transmit and receive SCK clock. 172 | This parameter can be a value of @ref SPI_BaudRate_Prescaler. 173 | @note The communication clock is derived from the master 174 | clock. The slave clock does not need to be set. */ 175 | 176 | USHORT SPI_FirstBit; /* Specifies whether data transfers start from MSB or LSB bit. 177 | This parameter can be a value of @ref SPI_MSB_LSB_transmission */ 178 | 179 | USHORT SPI_CRCPolynomial; /* Specifies the polynomial used for the CRC calculation. */ 180 | } SPI_InitTypeDef; 181 | 182 | typedef struct _SpiUSBCFG 183 | { 184 | SPI_InitTypeDef SPIInitCfg; 185 | USHORT SpiWriteReadInterval; // SPI接口常规读取写入数据命令(DEF_CMD_SPI_RD_WR)),单位为uS 186 | UCHAR SpiOutDefaultData; // SPI读数据时默认输出数据 187 | UCHAR OtherCfg; // 1个字节杂项控制; 188 | // 位7:片选CS1极性控制:0:低电平有效;1:有电平有效; 189 | // 位6:片选CS2极性控制:0:低电平有效;1:有电平有效; 190 | // 位5:IIC时钟延展功能控制:0:禁止;1:使能; 191 | // 位4:IIC读取最后1个字节完成时生成或不生成NACK; 192 | // 位3-0:保留; 193 | UCHAR Reserved[4]; // 保留 194 | } SpiHwCfgS, *PSpiHwCfgS; 195 | 196 | typedef struct _CH347_USB_CMD_S 197 | { 198 | UCHAR mFunction; 199 | USHORT mLength; 200 | UCHAR mBuffer[512]; 201 | } CH347SPI_CMD, *mPCH347SPI_CMD; 202 | 203 | typedef struct _StreamUSBCFG 204 | { 205 | SPI_InitTypeDef SPIInitCfg; 206 | USHORT SpiWriteReadInterval; // SPI接口常规读取写入数据命令(DEF_CMD_SPI_RD_WR)),单位为uS 207 | UCHAR SpiOutDefaultData; // SPI读数据时默认输出数据 208 | UCHAR OtherCfg; // 1个字节杂项控制; 209 | // 位7:片选CS1极性控制:0:低电平有效;1:有电平有效; 210 | // 位6:片选CS2极性控制:0:低电平有效;1:有电平有效; 211 | // 位5:IIC时钟延展功能控制:0:禁止;1:使能; 212 | // 位4:IIC读取最后1个字节完成时生成或不生成NACK; 213 | // 位3-0:保留; 214 | UCHAR Reserved[4]; // 保留 215 | } StreamHwCfgS, *PStreamHwCfgS; 216 | 217 | #pragma pack(1) 218 | // SPI控制器配置 219 | typedef struct _SPI_CONFIG 220 | { 221 | UCHAR iMode; // 0-3:SPI Mode0/1/2/3 222 | UCHAR iClock; // 0=60MHz, 1=30MHz, 2=15MHz, 3=7.5MHz, 4=3.75MHz, 5=1.875MHz, 6=937.5KHz,7=468.75KHz 223 | UCHAR iByteOrder; // 0=低位在前(LSB), 1=高位在前(MSB) 224 | USHORT iSpiWriteReadInterval; // SPI接口常规读取写入数据命令,单位为uS 225 | UCHAR iSpiOutDefaultData; // SPI读数据时默认输出数据 226 | ULONG iChipSelect; // 片选控制, 位7为0则忽略片选控制, 位7为1则参数有效: 位1位0为00/01分别选择CS1/CS2引脚作为低电平有效片选 227 | UCHAR CS1Polarity; // 位0:片选CS1极性控制:0:低电平有效;1:高电平有效; 228 | UCHAR CS2Polarity; // 位0:片选CS2极性控制:0:低电平有效;1:高电平有效; 229 | USHORT iIsAutoDeativeCS; // 操作完成后是否自动撤消片选 230 | USHORT iActiveDelay; // 设置片选后执行读写操作的延时时间,单位us 231 | ULONG iDelayDeactive; // 撤消片选后执行读写操作的延时时间,单位us 232 | } mSpiCfgS, *mPSpiCfgS; 233 | 234 | //设备信息 235 | typedef struct _DEV_INFOR 236 | { 237 | UCHAR iIndex; // 当前打开序号 238 | UCHAR DevicePath[MAX_PATH]; // 设备链接名,用于CreateFile 239 | UCHAR UsbClass; // 0:CH347_USB_CH341, 2:CH347_USB_HID,3:CH347_USB_VCP 240 | UCHAR FuncType; // 0:CH347_FUNC_UART,1:CH347_FUNC_SPI_I2C,2:CH347_FUNC_JTAG_I2C 241 | CHAR DeviceID[64]; // USB\VID_xxxx&PID_xxxx 242 | UCHAR ChipMode; // 芯片模式,0:Mode0(UART0/1); 1:Mode1(Uart1+SPI+I2C); 2:Mode2(HID Uart1+SPI+I2C) 3:Mode3(Uart1+Jtag+IIC) 243 | int DevHandle; // 设备句柄 244 | USHORT BulkOutEndpMaxSize; // 上传端点大小 245 | USHORT BulkInEndpMaxSize; // 下传端点大小 246 | UCHAR UsbSpeedType; // USB速度类型,0:FS,1:HS,2:SS 247 | UCHAR CH347IfNum; // 设备接口号: 0:UART,1:SPI/IIC/JTAG/GPIO 248 | UCHAR DataUpEndp; // 端点地址 249 | UCHAR DataDnEndp; // 端点地址 250 | CHAR ProductString[64]; // USB产品字符串 251 | CHAR ManufacturerString[64]; // USB厂商字符串 252 | ULONG WriteTimeout; // USB写超时 253 | ULONG ReadTimeout; // USB读超时 254 | CHAR FuncDescStr[64]; // 接口功能描述符 255 | UCHAR FirewareVer; // 固件版本 256 | } mDeviceInforS, *mPDeviceInforS; 257 | 258 | typedef struct _DevObj 259 | { 260 | UCHAR iIndex; //当前打开序号 261 | UCHAR DevicePath[MAX_PATH]; 262 | UCHAR UsbClass; // 0:CH341 Vendor; 1:CH347 Vendor; 2:HID 263 | UCHAR FuncType; // 0:UART1; 1:SPI+IIC; 2:JTAG+IIC 264 | CHAR DeviceID[64]; // USB\VID_xxxx&PID_xxxx 265 | UCHAR Mode; //芯片模式,0:Mode0(UART*2); 1:Mode1(Uart1+SPI+IIC); 2:Mode2(HID Uart1+SPI+IIC) 3:Mode3(Uart1+Jtag+IIC) 266 | USHORT BulkOutEndpMaxSize; //上传端点大小 267 | USHORT BulkInEndpMaxSize; //下传端点大小 268 | UCHAR UsbSpeedType; // USB速度类型,0:FS,1:HS,2:SS 269 | UCHAR CH347IfNum; // USB接口号 270 | UCHAR DataUpEndp; //端点地址 271 | UCHAR DataDnEndp; //端点地址 272 | CHAR ProductString[64]; // USB产品字符串 273 | CHAR ManufacturerString[64]; // USB厂商字符串 274 | ULONG WriteTimeout; // USB写超时 275 | ULONG ReadTimeout; // USB读超时 276 | CHAR FuncDescStr[64]; 277 | UCHAR FirewareVer; //固件版本 278 | ULONG CmdDataMaxSize; 279 | } mDevObjS, *mPDevObj; 280 | #pragma pack() 281 | 282 | // CH347模式公用函数,支持CH347所有模式下的打开、关闭、USB读、USB写,包含HID 283 | //打开USB设备 284 | int CH347OpenDevice(ULONG DevI); 285 | 286 | //关闭USB设备 287 | BOOL CH347CloseDevice(ULONG iIndex); 288 | 289 | // 读取USB数据块 290 | BOOL CH347ReadData(ULONG iIndex, // 指定设备序号 291 | PVOID oBuffer, // 指向一个足够大的缓冲区,用于保存读取的数据 292 | PULONG ioLength); // 指向长度单元,输入时为准备读取的长度,返回后为实际读取的长度 293 | 294 | // 写取USB数据块 295 | BOOL CH347WriteData(ULONG iIndex, // 指定设备序号 296 | PVOID iBuffer, // 指向一个缓冲区,放置准备写出的数据 297 | PULONG ioLength); // 指向长度单元,输入时为准备写出的长度,返回后为实际写出的长度 298 | 299 | /***************SPI********************/ 300 | // SPI控制器初始化 301 | BOOL CH347SPI_Init(ULONG iIndex, mSpiCfgS *SpiCfg); 302 | 303 | //获取SPI控制器配置信息 304 | BOOL CH347SPI_GetCfg(ULONG iIndex, mSpiCfgS *SpiCfg); 305 | 306 | //设置片选状态,使用前需先调用CH347SPI_Init对CS进行设置 307 | BOOL CH347SPI_ChangeCS(ULONG iIndex, // 指定设备序号 308 | UCHAR iStatus); // 0=撤消片选,1=设置片选 309 | 310 | //设置SPI片选 311 | BOOL CH347SPI_SetChipSelect(ULONG iIndex, // 指定设备序号 312 | USHORT iEnableSelect, // 低八位为CS1,高八位为CS2; 字节值为1=设置CS,为0=忽略此CS设置 313 | USHORT iChipSelect, // 低八位为CS1,高八位为CS2;片选输出,0=撤消片选,1=设置片选 314 | ULONG iIsAutoDeativeCS, // 低16位为CS1,高16位为CS2;操作完成后是否自动撤消片选 315 | ULONG iActiveDelay, // 低16位为CS1,高16位为CS2;设置片选后执行读写操作的延时时间,单位us 316 | ULONG iDelayDeactive); // 低16位为CS1,高16位为CS2;撤消片选后执行读写操作的延时时间,单位us 317 | 318 | // SPI4写数据 319 | BOOL CH347SPI_Write(ULONG iIndex, // 指定设备序号 320 | ULONG iChipSelect, // 片选控制, 位7为0则忽略片选控制, 位7为1进行片选操作 321 | ULONG iLength, // 准备传输的数据字节数 322 | ULONG iWriteStep, // 准备读取的单个块的长度 323 | PVOID ioBuffer); // 指向一个缓冲区,放置准备从MOSI写出的数据 324 | 325 | // SPI4读数据.无需先写数据,效率较CH347SPI_WriteRead高很多 326 | BOOL CH347SPI_Read(ULONG iIndex, // 指定设备序号 327 | ULONG iChipSelect, // 片选控制, 位7为0则忽略片选控制, 位7为1进行片选操作 328 | ULONG oLength, // 准备发出的字节数 329 | PULONG iLength, // 准备读入的数据字节数 330 | PVOID ioBuffer); // 指向一个缓冲区,放置准备从DOUT写出的数据,返回后是从DIN读入的数据 331 | 332 | // 处理SPI数据流,4线接口 333 | BOOL CH347SPI_WriteRead(ULONG iIndex, // 指定设备序号 334 | ULONG iChipSelect, // 片选控制, 位7为0则忽略片选控制, 位7为1则操作片选 335 | ULONG iLength, // 准备传输的数据字节数 336 | PVOID ioBuffer); // 指向一个缓冲区,放置准备从DOUT写出的数据,返回后是从DIN读入的数据 337 | 338 | // 处理SPI数据流,4线接口 339 | BOOL CH347StreamSPI4(ULONG iIndex, // 指定设备序号 340 | ULONG iChipSelect, // 片选控制, 位7为0则忽略片选控制, 位7为1则参数有效 341 | ULONG iLength, // 准备传输的数据字节数 342 | PVOID ioBuffer); // 指向一个缓冲区,放置准备从DOUT写出的数据,返回后是从DIN读入的数据 343 | 344 | /********IIC***********/ 345 | typedef enum _EEPROM_TYPE 346 | { // EEPROM型号 347 | ID_24C01, 348 | ID_24C02, 349 | ID_24C04, 350 | ID_24C08, 351 | ID_24C16, 352 | ID_24C32, 353 | ID_24C64, 354 | ID_24C128, 355 | ID_24C256, 356 | ID_24C512, 357 | ID_24C1024, 358 | ID_24C2048, 359 | ID_24C4096 360 | } EEPROM_TYPE; 361 | 362 | // 设置串口流模式 363 | BOOL CH347I2C_Set(ULONG iIndex, // 指定设备序号 364 | ULONG iMode); // 指定模式,见下行 365 | // 位1-位0: I2C接口速度/SCL频率, 00=低速/20KHz,01=标准/100KHz(默认值),10=快速/400KHz,11=高速/750KHz 366 | // 其它保留,必须为0 367 | 368 | // 设置硬件异步延时,调用后很快返回,而在下一个流操作之前延时指定毫秒数 369 | BOOL CH347I2C_SetDelaymS(ULONG iIndex, // 指定设备序号 370 | ULONG iDelay); // 指定延时的毫秒数 371 | 372 | // 处理I2C数据流,2线接口,时钟线为SCL引脚,数据线为SDA引脚 373 | BOOL CH347StreamI2C(ULONG iIndex, // 指定设备序号 374 | ULONG iWriteLength, // 准备写出的数据字节数 375 | PVOID iWriteBuffer, // 指向一个缓冲区,放置准备写出的数据,首字节通常是I2C设备地址及读写方向位 376 | ULONG iReadLength, // 准备读取的数据字节数 377 | PVOID oReadBuffer); // 指向一个缓冲区,返回后是读入的数据 378 | 379 | BOOL CH347ReadEEPROM( // 从EEPROM中读取数据块,速度约56K字节 380 | ULONG iIndex, // 指定CH341设备序号 381 | EEPROM_TYPE iEepromID, // 指定EEPROM型号 382 | ULONG iAddr, // 指定数据单元的地址 383 | ULONG iLength, // 准备读取的数据字节数 384 | PUCHAR oBuffer); // 指向一个缓冲区,返回后是读入的数据 385 | 386 | // 向EEPROM中写入数据块 387 | BOOL CH347WriteEEPROM(ULONG iIndex, // 指定设备序号 388 | EEPROM_TYPE iEepromID, // 指定EEPROM型号 389 | ULONG iAddr, // 指定数据单元的地址 390 | ULONG iLength, // 准备写出的数据字节数 391 | PUCHAR iBuffer); // 指向一个缓冲区,放置准备写出的数据 392 | #endif -------------------------------------------------------------------------------- /lib/x86_64/CH347SPILIB.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ch34x_lib.h for ch341 in Epp/MEM/I2C/SPI/GPIO 3 | * Copyright (C) WCH 2019 4 | * Running Environment: Linux 5 | * Version:2.3 6 | */ 7 | 8 | #ifndef _CH34X_LIB_H 9 | #define _CH34X_LIB_H 10 | 11 | #ifndef CHAR 12 | #define CHAR char 13 | #endif 14 | 15 | #ifndef UCHAR 16 | #define UCHAR unsigned char 17 | #endif 18 | 19 | #ifndef USHORT 20 | #define USHORT unsigned short 21 | #endif 22 | 23 | #ifndef ULONG 24 | #define ULONG unsigned long 25 | #endif 26 | 27 | #ifndef LONGLONG 28 | #define LONGLONG unsigned long long 29 | #endif 30 | 31 | #ifndef PUCHAR 32 | #define PUCHAR unsigned char * 33 | #endif 34 | 35 | #ifndef PCHAR 36 | #define PCHAR char * 37 | #endif 38 | 39 | #ifndef PUSHORT 40 | #define PUSHORT unsigned short * 41 | #endif 42 | 43 | #ifndef PULONG 44 | #define PULONG unsigned long * 45 | #endif 46 | 47 | #ifndef VOID 48 | #define VOID void 49 | #endif 50 | 51 | #ifndef PVOID 52 | #define PVOID void * 53 | #endif 54 | 55 | #define true 1 56 | #define false 0 57 | 58 | #define TRUE true 59 | #define FALSE false 60 | 61 | #ifndef min 62 | #define min(x, y) (((x) < (y)) ? (x) : (y)) 63 | #endif 64 | 65 | #ifndef max 66 | #define max(x, y) (((x) < (y)) ? (y) : (x)) 67 | #endif 68 | 69 | typedef enum 70 | { 71 | FALSE_H = 0, 72 | TRUE_H = !FALSE_H 73 | } BOOL; 74 | 75 | #define MAX_PATH 512 76 | 77 | #define CH341_PACKET_LENGTH 32 78 | #define CH341_PKT_LEN_SHORT 8 79 | 80 | #define CH341_MAX_NUMBER 16 81 | #define MAX_BUFFER_LENGTH 0x1000 82 | #define DEFAULT_BUFFER_LEN 0x0400 83 | 84 | #define mCH341_PACKET_LENGTH 32 85 | #define mCH341_MAX_NUMBER 16 86 | 87 | #define CH347_USB_VENDOR 0 88 | #define CH347_USB_HID 2 89 | #define CH347_USB_VCP 3 90 | 91 | // USB to SPI CMD 92 | #define SET_CS 0 93 | #define CLR_CS 1 94 | 95 | #define USB20_CMD_SPI_INIT 0xC0 // 用于初始化SPI接口,设置SPI接口的数据位、时钟分频、高低字节顺序等等参数。 96 | #define USB20_CMD_SPI_CONTROL 0xC1 // SPI接口控制命令,用于控制SPI接口片选引脚输出高低电平以及电平延时时间。 97 | #define USB20_CMD_SPI_RD_WR 0xC2 // SPI接口常规读取写入数据命令,用于SPI接口通用读取写入操作,一般用于简短常规命令操作。该命令写N个字节数据的同时会回读N个字节数据。 98 | #define USB20_CMD_SPI_BLCK_RD 0xC3 // SPI接口批量读取数据命令,用于SPI接口批量读取数据,一般用于批量数据的读取操作。启用该命令读取数据后,读取的数据会按最大包大小进行打包上传,直到所有数据读取返回完毕。 99 | #define USB20_CMD_SPI_BLCK_WR 0xC4 // SPI接口批量写入数据命令,用于SPI接口批量写入数据,一般用于批量数据的写入操作。 100 | #define USB20_CMD_INFO_RD 0xCA // 参数获取,用于获取SPI接口相关参数等 101 | 102 | #define mMAX_BUFFER_LENGTH 0x1000 103 | #define USB20_CMD_HEADER 3 104 | 105 | #define SPI_CS_ACTIVE 0x00 106 | #define SPI_CS_DEACTIVE 0x01 107 | 108 | /* SPI_data_direction */ 109 | #define SPI_Direction_2Lines_FullDuplex ((USHORT)0x0000) 110 | #define SPI_Direction_2Lines_RxOnly ((USHORT)0x0400) 111 | #define SPI_Direction_1Line_Rx ((USHORT)0x8000) 112 | #define SPI_Direction_1Line_Tx ((USHORT)0xC000) 113 | 114 | /* SPI_mode */ 115 | #define SPI_Mode_Master ((USHORT)0x0104) 116 | #define SPI_Mode_Slave ((USHORT)0x0000) 117 | 118 | /* SPI_data_size */ 119 | #define SPI_DataSize_16b ((USHORT)0x0800) 120 | #define SPI_DataSize_8b ((USHORT)0x0000) 121 | 122 | /* SPI_Clock_Polarity */ 123 | #define SPI_CPOL_Low ((USHORT)0x0000) 124 | #define SPI_CPOL_High ((USHORT)0x0002) 125 | 126 | /* SPI_Clock_Phase */ 127 | #define SPI_CPHA_1Edge ((USHORT)0x0000) 128 | #define SPI_CPHA_2Edge ((USHORT)0x0001) 129 | 130 | /* SPI_Slave_Select_management */ 131 | #define SPI_NSS_Soft ((USHORT)0x0200) 132 | #define SPI_NSS_Hard ((USHORT)0x0000) 133 | 134 | #define mWIN32_COMMAND_HEAD 32 // WIN32命令接口的头长度 135 | 136 | /* SPI_MSB_LSB_transmission */ 137 | #define SPI_FirstBit_MSB ((USHORT)0x0000) 138 | #define SPI_FirstBit_LSB ((USHORT)0x0080) 139 | 140 | #define mMAX_BUFFER_LENGTH 0x1000 // 数据缓冲区最大长度4096 141 | 142 | #define mMAX_COMMAND_LENGTH (mWIN32_COMMAND_HEAD + mMAX_BUFFER_LENGTH) // 最大数据长度加上命令结构头的长度 143 | 144 | #define mDEFAULT_BUFFER_LEN 0x0400 // 数据缓冲区默认长度1024 145 | 146 | #define mDEFAULT_COMMAND_LEN (mWIN32_COMMAND_HEAD + mDEFAULT_BUFFER_LEN) // 默认数据长度加上命令结构头的长度 147 | 148 | /* SPI Init structure definition */ 149 | typedef struct _SPI_InitTypeDef 150 | { 151 | USHORT SPI_Direction; /* Specifies th e SPI unidirectional or bidirectional data mode. 152 | This parameter can be a value of @ref SPI_data_direction */ 153 | 154 | USHORT SPI_Mode; /* Specifies the SPI operating mode. 155 | This parameter can be a value of @ref SPI_mode */ 156 | 157 | USHORT SPI_DataSize; /* Specifies the SPI data size. 158 | This parameter can be a value of @ref SPI_data_size */ 159 | 160 | USHORT SPI_CPOL; /* Specifies the serial clock steady state. 161 | This parameter can be a value of @ref SPI_Clock_Polarity */ 162 | 163 | USHORT SPI_CPHA; /* Specifies the clock active edge for the bit capture. 164 | This parameter can be a value of @ref SPI_Clock_Phase */ 165 | 166 | USHORT SPI_NSS; /* Specifies whether the NSS signal is managed by 167 | hardware (NSS pin) or by software using the SSI bit. 168 | This parameter can be a value of @ref SPI_Slave_Select_management */ 169 | 170 | USHORT SPI_BaudRatePrescaler; /* Specifies the Baud Rate prescaler value which will be 171 | used to configure the transmit and receive SCK clock. 172 | This parameter can be a value of @ref SPI_BaudRate_Prescaler. 173 | @note The communication clock is derived from the master 174 | clock. The slave clock does not need to be set. */ 175 | 176 | USHORT SPI_FirstBit; /* Specifies whether data transfers start from MSB or LSB bit. 177 | This parameter can be a value of @ref SPI_MSB_LSB_transmission */ 178 | 179 | USHORT SPI_CRCPolynomial; /* Specifies the polynomial used for the CRC calculation. */ 180 | } SPI_InitTypeDef; 181 | 182 | typedef struct _SpiUSBCFG 183 | { 184 | SPI_InitTypeDef SPIInitCfg; 185 | USHORT SpiWriteReadInterval; // SPI接口常规读取写入数据命令(DEF_CMD_SPI_RD_WR)),单位为uS 186 | UCHAR SpiOutDefaultData; // SPI读数据时默认输出数据 187 | UCHAR OtherCfg; // 1个字节杂项控制; 188 | // 位7:片选CS1极性控制:0:低电平有效;1:有电平有效; 189 | // 位6:片选CS2极性控制:0:低电平有效;1:有电平有效; 190 | // 位5:IIC时钟延展功能控制:0:禁止;1:使能; 191 | // 位4:IIC读取最后1个字节完成时生成或不生成NACK; 192 | // 位3-0:保留; 193 | UCHAR Reserved[4]; // 保留 194 | } SpiHwCfgS, *PSpiHwCfgS; 195 | 196 | typedef struct _CH347_USB_CMD_S 197 | { 198 | UCHAR mFunction; 199 | USHORT mLength; 200 | UCHAR mBuffer[512]; 201 | } CH347SPI_CMD, *mPCH347SPI_CMD; 202 | 203 | typedef struct _StreamUSBCFG 204 | { 205 | SPI_InitTypeDef SPIInitCfg; 206 | USHORT SpiWriteReadInterval; // SPI接口常规读取写入数据命令(DEF_CMD_SPI_RD_WR)),单位为uS 207 | UCHAR SpiOutDefaultData; // SPI读数据时默认输出数据 208 | UCHAR OtherCfg; // 1个字节杂项控制; 209 | // 位7:片选CS1极性控制:0:低电平有效;1:有电平有效; 210 | // 位6:片选CS2极性控制:0:低电平有效;1:有电平有效; 211 | // 位5:IIC时钟延展功能控制:0:禁止;1:使能; 212 | // 位4:IIC读取最后1个字节完成时生成或不生成NACK; 213 | // 位3-0:保留; 214 | UCHAR Reserved[4]; // 保留 215 | } StreamHwCfgS, *PStreamHwCfgS; 216 | 217 | #pragma pack(1) 218 | // SPI控制器配置 219 | typedef struct _SPI_CONFIG 220 | { 221 | UCHAR iMode; // 0-3:SPI Mode0/1/2/3 222 | UCHAR iClock; // 0=60MHz, 1=30MHz, 2=15MHz, 3=7.5MHz, 4=3.75MHz, 5=1.875MHz, 6=937.5KHz,7=468.75KHz 223 | UCHAR iByteOrder; // 0=低位在前(LSB), 1=高位在前(MSB) 224 | USHORT iSpiWriteReadInterval; // SPI接口常规读取写入数据命令,单位为uS 225 | UCHAR iSpiOutDefaultData; // SPI读数据时默认输出数据 226 | ULONG iChipSelect; // 片选控制, 位7为0则忽略片选控制, 位7为1则参数有效: 位1位0为00/01分别选择CS1/CS2引脚作为低电平有效片选 227 | UCHAR CS1Polarity; // 位0:片选CS1极性控制:0:低电平有效;1:高电平有效; 228 | UCHAR CS2Polarity; // 位0:片选CS2极性控制:0:低电平有效;1:高电平有效; 229 | USHORT iIsAutoDeativeCS; // 操作完成后是否自动撤消片选 230 | USHORT iActiveDelay; // 设置片选后执行读写操作的延时时间,单位us 231 | ULONG iDelayDeactive; // 撤消片选后执行读写操作的延时时间,单位us 232 | } mSpiCfgS, *mPSpiCfgS; 233 | 234 | //设备信息 235 | typedef struct _DEV_INFOR 236 | { 237 | UCHAR iIndex; // 当前打开序号 238 | UCHAR DevicePath[MAX_PATH]; // 设备链接名,用于CreateFile 239 | UCHAR UsbClass; // 0:CH347_USB_CH341, 2:CH347_USB_HID,3:CH347_USB_VCP 240 | UCHAR FuncType; // 0:CH347_FUNC_UART,1:CH347_FUNC_SPI_I2C,2:CH347_FUNC_JTAG_I2C 241 | CHAR DeviceID[64]; // USB\VID_xxxx&PID_xxxx 242 | UCHAR ChipMode; // 芯片模式,0:Mode0(UART0/1); 1:Mode1(Uart1+SPI+I2C); 2:Mode2(HID Uart1+SPI+I2C) 3:Mode3(Uart1+Jtag+IIC) 243 | int DevHandle; // 设备句柄 244 | USHORT BulkOutEndpMaxSize; // 上传端点大小 245 | USHORT BulkInEndpMaxSize; // 下传端点大小 246 | UCHAR UsbSpeedType; // USB速度类型,0:FS,1:HS,2:SS 247 | UCHAR CH347IfNum; // 设备接口号: 0:UART,1:SPI/IIC/JTAG/GPIO 248 | UCHAR DataUpEndp; // 端点地址 249 | UCHAR DataDnEndp; // 端点地址 250 | CHAR ProductString[64]; // USB产品字符串 251 | CHAR ManufacturerString[64]; // USB厂商字符串 252 | ULONG WriteTimeout; // USB写超时 253 | ULONG ReadTimeout; // USB读超时 254 | CHAR FuncDescStr[64]; // 接口功能描述符 255 | UCHAR FirewareVer; // 固件版本 256 | } mDeviceInforS, *mPDeviceInforS; 257 | 258 | typedef struct _DevObj 259 | { 260 | UCHAR iIndex; //当前打开序号 261 | UCHAR DevicePath[MAX_PATH]; 262 | UCHAR UsbClass; // 0:CH341 Vendor; 1:CH347 Vendor; 2:HID 263 | UCHAR FuncType; // 0:UART1; 1:SPI+IIC; 2:JTAG+IIC 264 | CHAR DeviceID[64]; // USB\VID_xxxx&PID_xxxx 265 | UCHAR Mode; //芯片模式,0:Mode0(UART*2); 1:Mode1(Uart1+SPI+IIC); 2:Mode2(HID Uart1+SPI+IIC) 3:Mode3(Uart1+Jtag+IIC) 266 | USHORT BulkOutEndpMaxSize; //上传端点大小 267 | USHORT BulkInEndpMaxSize; //下传端点大小 268 | UCHAR UsbSpeedType; // USB速度类型,0:FS,1:HS,2:SS 269 | UCHAR CH347IfNum; // USB接口号 270 | UCHAR DataUpEndp; //端点地址 271 | UCHAR DataDnEndp; //端点地址 272 | CHAR ProductString[64]; // USB产品字符串 273 | CHAR ManufacturerString[64]; // USB厂商字符串 274 | ULONG WriteTimeout; // USB写超时 275 | ULONG ReadTimeout; // USB读超时 276 | CHAR FuncDescStr[64]; 277 | UCHAR FirewareVer; //固件版本 278 | ULONG CmdDataMaxSize; 279 | } mDevObjS, *mPDevObj; 280 | #pragma pack() 281 | 282 | // CH347模式公用函数,支持CH347所有模式下的打开、关闭、USB读、USB写,包含HID 283 | //打开USB设备 284 | int CH347OpenDevice(ULONG DevI); 285 | 286 | //关闭USB设备 287 | BOOL CH347CloseDevice(ULONG iIndex); 288 | 289 | // 读取USB数据块 290 | BOOL CH347ReadData(ULONG iIndex, // 指定设备序号 291 | PVOID oBuffer, // 指向一个足够大的缓冲区,用于保存读取的数据 292 | PULONG ioLength); // 指向长度单元,输入时为准备读取的长度,返回后为实际读取的长度 293 | 294 | // 写取USB数据块 295 | BOOL CH347WriteData(ULONG iIndex, // 指定设备序号 296 | PVOID iBuffer, // 指向一个缓冲区,放置准备写出的数据 297 | PULONG ioLength); // 指向长度单元,输入时为准备写出的长度,返回后为实际写出的长度 298 | 299 | /***************SPI********************/ 300 | // SPI控制器初始化 301 | BOOL CH347SPI_Init(ULONG iIndex, mSpiCfgS *SpiCfg); 302 | 303 | //获取SPI控制器配置信息 304 | BOOL CH347SPI_GetCfg(ULONG iIndex, mSpiCfgS *SpiCfg); 305 | 306 | //设置片选状态,使用前需先调用CH347SPI_Init对CS进行设置 307 | BOOL CH347SPI_ChangeCS(ULONG iIndex, // 指定设备序号 308 | UCHAR iStatus); // 0=撤消片选,1=设置片选 309 | 310 | //设置SPI片选 311 | BOOL CH347SPI_SetChipSelect(ULONG iIndex, // 指定设备序号 312 | USHORT iEnableSelect, // 低八位为CS1,高八位为CS2; 字节值为1=设置CS,为0=忽略此CS设置 313 | USHORT iChipSelect, // 低八位为CS1,高八位为CS2;片选输出,0=撤消片选,1=设置片选 314 | ULONG iIsAutoDeativeCS, // 低16位为CS1,高16位为CS2;操作完成后是否自动撤消片选 315 | ULONG iActiveDelay, // 低16位为CS1,高16位为CS2;设置片选后执行读写操作的延时时间,单位us 316 | ULONG iDelayDeactive); // 低16位为CS1,高16位为CS2;撤消片选后执行读写操作的延时时间,单位us 317 | 318 | // SPI4写数据 319 | BOOL CH347SPI_Write(ULONG iIndex, // 指定设备序号 320 | ULONG iChipSelect, // 片选控制, 位7为0则忽略片选控制, 位7为1进行片选操作 321 | ULONG iLength, // 准备传输的数据字节数 322 | ULONG iWriteStep, // 准备读取的单个块的长度 323 | PVOID ioBuffer); // 指向一个缓冲区,放置准备从MOSI写出的数据 324 | 325 | // SPI4读数据.无需先写数据,效率较CH347SPI_WriteRead高很多 326 | BOOL CH347SPI_Read(ULONG iIndex, // 指定设备序号 327 | ULONG iChipSelect, // 片选控制, 位7为0则忽略片选控制, 位7为1进行片选操作 328 | ULONG oLength, // 准备发出的字节数 329 | PULONG iLength, // 准备读入的数据字节数 330 | PVOID ioBuffer); // 指向一个缓冲区,放置准备从DOUT写出的数据,返回后是从DIN读入的数据 331 | 332 | // 处理SPI数据流,4线接口 333 | BOOL CH347SPI_WriteRead(ULONG iIndex, // 指定设备序号 334 | ULONG iChipSelect, // 片选控制, 位7为0则忽略片选控制, 位7为1则操作片选 335 | ULONG iLength, // 准备传输的数据字节数 336 | PVOID ioBuffer); // 指向一个缓冲区,放置准备从DOUT写出的数据,返回后是从DIN读入的数据 337 | 338 | // 处理SPI数据流,4线接口 339 | BOOL CH347StreamSPI4(ULONG iIndex, // 指定设备序号 340 | ULONG iChipSelect, // 片选控制, 位7为0则忽略片选控制, 位7为1则参数有效 341 | ULONG iLength, // 准备传输的数据字节数 342 | PVOID ioBuffer); // 指向一个缓冲区,放置准备从DOUT写出的数据,返回后是从DIN读入的数据 343 | 344 | /********IIC***********/ 345 | typedef enum _EEPROM_TYPE 346 | { // EEPROM型号 347 | ID_24C01, 348 | ID_24C02, 349 | ID_24C04, 350 | ID_24C08, 351 | ID_24C16, 352 | ID_24C32, 353 | ID_24C64, 354 | ID_24C128, 355 | ID_24C256, 356 | ID_24C512, 357 | ID_24C1024, 358 | ID_24C2048, 359 | ID_24C4096 360 | } EEPROM_TYPE; 361 | 362 | // 设置串口流模式 363 | BOOL CH347I2C_Set(ULONG iIndex, // 指定设备序号 364 | ULONG iMode); // 指定模式,见下行 365 | // 位1-位0: I2C接口速度/SCL频率, 00=低速/20KHz,01=标准/100KHz(默认值),10=快速/400KHz,11=高速/750KHz 366 | // 其它保留,必须为0 367 | 368 | // 设置硬件异步延时,调用后很快返回,而在下一个流操作之前延时指定毫秒数 369 | BOOL CH347I2C_SetDelaymS(ULONG iIndex, // 指定设备序号 370 | ULONG iDelay); // 指定延时的毫秒数 371 | 372 | // 处理I2C数据流,2线接口,时钟线为SCL引脚,数据线为SDA引脚 373 | BOOL CH347StreamI2C(ULONG iIndex, // 指定设备序号 374 | ULONG iWriteLength, // 准备写出的数据字节数 375 | PVOID iWriteBuffer, // 指向一个缓冲区,放置准备写出的数据,首字节通常是I2C设备地址及读写方向位 376 | ULONG iReadLength, // 准备读取的数据字节数 377 | PVOID oReadBuffer); // 指向一个缓冲区,返回后是读入的数据 378 | 379 | BOOL CH347ReadEEPROM( // 从EEPROM中读取数据块,速度约56K字节 380 | ULONG iIndex, // 指定CH341设备序号 381 | EEPROM_TYPE iEepromID, // 指定EEPROM型号 382 | ULONG iAddr, // 指定数据单元的地址 383 | ULONG iLength, // 准备读取的数据字节数 384 | PUCHAR oBuffer); // 指向一个缓冲区,返回后是读入的数据 385 | 386 | // 向EEPROM中写入数据块 387 | BOOL CH347WriteEEPROM(ULONG iIndex, // 指定设备序号 388 | EEPROM_TYPE iEepromID, // 指定EEPROM型号 389 | ULONG iAddr, // 指定数据单元的地址 390 | ULONG iLength, // 准备写出的数据字节数 391 | PUCHAR iBuffer); // 指向一个缓冲区,放置准备写出的数据 392 | #endif -------------------------------------------------------------------------------- /src/ch347lib/ch347dll.rs: -------------------------------------------------------------------------------- 1 | use crate::windows::basetsd::*; 2 | use std::ffi::CStr; 3 | use std::fmt; 4 | 5 | pub const INVALID_HANDLE_VALUE: HANDLE = -1 as LONG as HANDLE; 6 | 7 | #[derive(Debug)] 8 | pub enum NotifyiEventStatus { 9 | Inserted, 10 | Removed, 11 | Unknow(ULONG), 12 | } 13 | 14 | #[derive(Debug)] 15 | pub enum UsbClass { 16 | Ch341, 17 | Hid, 18 | Vcp, 19 | } 20 | 21 | #[derive(Debug)] 22 | pub enum UsbSpeedType { 23 | FS, // USB1.0 12Mbit/s 24 | HS, // USB2.0 480Mbit/s 25 | SS, // USB3.0 5GMbit/s 26 | } 27 | 28 | #[derive(Debug)] 29 | pub enum FuncType { 30 | Uart, 31 | SpiI2c, 32 | JtagI2c, 33 | } 34 | 35 | #[derive(Debug)] 36 | pub enum SpiClockLevel { 37 | S60M, 38 | S30M, 39 | S15M, 40 | S7_5M, 41 | S3_75M, 42 | S1_875M, 43 | S937_5K, 44 | S468_75K, 45 | } 46 | 47 | impl SpiClockLevel { 48 | pub fn from_byte(data: u8) -> Option { 49 | match data { 50 | 0 => Some(SpiClockLevel::S60M), 51 | 1 => Some(SpiClockLevel::S30M), 52 | 2 => Some(SpiClockLevel::S15M), 53 | 3 => Some(SpiClockLevel::S7_5M), 54 | 4 => Some(SpiClockLevel::S3_75M), 55 | 5 => Some(SpiClockLevel::S1_875M), 56 | 6 => Some(SpiClockLevel::S937_5K), 57 | 7 => Some(SpiClockLevel::S468_75K), 58 | _ => None, 59 | } 60 | } 61 | } 62 | 63 | impl fmt::Display for SpiClockLevel { 64 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 65 | write!( 66 | f, 67 | "{}", 68 | match self { 69 | SpiClockLevel::S60M => "60MHz", 70 | SpiClockLevel::S30M => "30MHz", 71 | SpiClockLevel::S15M => "15MHz", 72 | SpiClockLevel::S7_5M => "7.5MHz", 73 | SpiClockLevel::S3_75M => "3.75MHz", 74 | SpiClockLevel::S1_875M => "1.875MHz", 75 | SpiClockLevel::S937_5K => "937kHz", 76 | SpiClockLevel::S468_75K => "468.75kHz", 77 | } 78 | ) 79 | } 80 | } 81 | 82 | /// 设备信息 83 | #[repr(C)] 84 | #[derive(Debug)] 85 | pub struct DeviceInfo { 86 | pub index: UCHAR, // 当前打开序号 87 | pub device_path: [UCHAR; MAX_PATH], // 设备链接名,用于CreateFile 88 | 89 | /// 0:CH347_USB_CH341, 2:CH347_USB_HID,3:CH347_USB_VCP 90 | usb_class: UCHAR, 91 | 92 | /// - 0: CH347_FUNC_UART 93 | /// - 1: CH347_FUNC_SPI_I2C 94 | /// - 2: CH347_FUNC_JTAG_I2C 95 | func_type: UCHAR, 96 | 97 | /// USB\VID_xxxx&PID_xxxx 98 | device_id: [UCHAR; 64], 99 | 100 | /// 芯片模式 101 | /// - 0: Mode0(UART0/1); 102 | /// - 1: Mode1(Uart1+SPI+I2C); 103 | /// - 2: Mode2(HID Uart1+SPI+I2C) 104 | /// - 3: Mode3(Uart1+Jtag+IIC) 105 | pub chip_mode: UCHAR, 106 | 107 | /// 设备句柄 108 | pub device_handle: HANDLE, 109 | /// 上传端点大小 110 | pub bulk_out_ep_max_size: USHORT, 111 | /// 下传端点大小 112 | pub bulk_in_ep_max_size: USHORT, 113 | /// USB速度类型,0:FS,1:HS,2:SS 114 | usb_speed_type: UCHAR, 115 | /// 设备接口号: 0:UART,1:SPI/IIC/JTAG/GPIO 116 | pub ch347_if_num: UCHAR, 117 | /// 端点地址 118 | pub data_up_ep: UCHAR, 119 | /// 端点地址 120 | pub data_down_ep: UCHAR, 121 | /// USB产品字符串 122 | rpoduct_string: [UCHAR; 64], 123 | /// USB厂商字符串 124 | manufacturer_string: [UCHAR; 64], 125 | /// USB写超时 126 | pub write_timeout: ULONG, 127 | /// USB读超时 128 | pub read_timeout: ULONG, 129 | /// 接口功能描述符 130 | func_desc_str: [UCHAR; 64], 131 | /// 固件版本 132 | pub fw_ver: UCHAR, 133 | } 134 | 135 | #[repr(C)] 136 | #[derive(Debug, Default)] 137 | pub struct SpiConfig { 138 | /// 0-3:SPI Mode0/1/2/3 139 | pub mode: UCHAR, 140 | 141 | // 0=60MHz, 1=30MHz, 2=15MHz, 3=7.5MHz, 4=3.75MHz, 5=1.875MHz, 6=937.5KHz,7=468.75KHz 142 | pub clock: UCHAR, 143 | 144 | /// 0=低位在前(LSB), 1=高位在前(MSB) 145 | pub byte_order: UCHAR, 146 | 147 | /// SPI接口常规读取写入数据命令,单位为uS 148 | pub write_read_interval: USHORT, 149 | 150 | /// SPI读数据时默认输出数据 151 | pub out_default_data: UCHAR, 152 | 153 | /// 片选控制, 位7为0则忽略片选控制, 位7为1则参数有效: 位1位0为00/01分别选择CS1/CS2引脚作为低电平有效片选 154 | pub chip_select: ULONG, 155 | 156 | // 位0:片选CS1极性控制:0:低电平有效;1:高电平有效; 157 | pub cs1_polarity: UCHAR, 158 | 159 | /// 位0:片选CS2极性控制:0:低电平有效;1:高电平有效; 160 | pub cs2_polarity: UCHAR, 161 | 162 | /// 操作完成后是否自动撤消片选 163 | pub is_auto_deative_cs: USHORT, 164 | 165 | /// 设置片选后执行读写操作的延时时间,单位us 166 | pub active_delay: USHORT, 167 | 168 | /// 撤消片选后执行读写操作的延时时间,单位us 169 | pub delay_deactive: ULONG, 170 | } 171 | 172 | impl DeviceInfo { 173 | pub fn default() -> DeviceInfo { 174 | DeviceInfo { 175 | index: 0, 176 | device_path: [0; 260], 177 | usb_class: 0, 178 | func_type: 0, 179 | device_id: [0; 64], 180 | chip_mode: 0, 181 | device_handle: INVALID_HANDLE_VALUE, 182 | bulk_out_ep_max_size: 0, 183 | bulk_in_ep_max_size: 0, 184 | usb_speed_type: 0, 185 | ch347_if_num: 0, 186 | data_up_ep: 0, 187 | data_down_ep: 0, 188 | rpoduct_string: [0; 64], 189 | manufacturer_string: [0; 64], 190 | write_timeout: 0, 191 | read_timeout: 0, 192 | func_desc_str: [0; 64], 193 | fw_ver: 0, 194 | } 195 | } 196 | 197 | pub fn get_device_path(&self) -> String { 198 | unsafe { 199 | let str = CStr::from_bytes_with_nul_unchecked(&self.device_path); 200 | return String::from(str.to_str().unwrap().trim_end_matches('\0')); 201 | } 202 | } 203 | 204 | pub fn get_usb_class(&self) -> UsbClass { 205 | match self.usb_class { 206 | 0 => UsbClass::Ch341, 207 | 2 => UsbClass::Hid, 208 | 3 => UsbClass::Vcp, 209 | _ => panic!("Unknown usb class {}", self.usb_class), 210 | } 211 | } 212 | 213 | pub fn get_func_type(&self) -> FuncType { 214 | match self.func_type { 215 | 0 => FuncType::Uart, 216 | 1 => FuncType::SpiI2c, 217 | 2 => FuncType::JtagI2c, 218 | _ => panic!("Unknown func type {}", self.usb_class), 219 | } 220 | } 221 | 222 | pub fn get_device_id(&self) -> String { 223 | unsafe { 224 | let str = CStr::from_bytes_with_nul_unchecked(&self.device_id); 225 | return String::from(str.to_str().unwrap().trim_end_matches('\0')); 226 | } 227 | } 228 | 229 | pub fn get_rpoduct_string(&self) -> String { 230 | unsafe { 231 | let str = CStr::from_bytes_with_nul_unchecked(&self.rpoduct_string); 232 | return String::from(str.to_str().unwrap().trim_end_matches('\0')); 233 | } 234 | } 235 | 236 | pub fn get_usb_speed_type(&self) -> Option { 237 | match self.usb_speed_type { 238 | 0 => Some(UsbSpeedType::FS), 239 | 1 => Some(UsbSpeedType::HS), 240 | 2 => Some(UsbSpeedType::SS), 241 | _ => None, 242 | } 243 | } 244 | 245 | pub fn get_manufacturer_string(&self) -> String { 246 | unsafe { 247 | let str = CStr::from_bytes_with_nul_unchecked(&self.manufacturer_string); 248 | return String::from(str.to_str().unwrap().trim_end_matches('\0')); 249 | } 250 | } 251 | 252 | pub fn get_func_desc_str(&self) -> String { 253 | unsafe { 254 | let str = CStr::from_bytes_with_nul_unchecked(&self.func_desc_str); 255 | return String::from(str.to_str().unwrap().trim_end_matches('\0')); 256 | } 257 | } 258 | } 259 | 260 | impl fmt::Display for DeviceInfo { 261 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 262 | write!( 263 | f, 264 | "#{} usb:{:?}, func:{:?}, id:{}", 265 | self.index, 266 | self.get_usb_class(), 267 | self.get_func_type(), 268 | self.get_device_id() 269 | ) 270 | } 271 | } 272 | 273 | // NOTE: The following are stubs for functions missing in the closed source 274 | // .so library for Linux. 275 | 276 | #[cfg(target_os = "linux")] 277 | #[allow(non_snake_case)] 278 | /// # Safety 279 | /// This comes from a closed source C library. Rewrite it in Rust. 280 | pub unsafe fn CH347Uart_GetDeviceInfor(_iIndex: ULONG, _DevInformation: *const DeviceInfo) -> BOOL { 281 | 0 282 | } 283 | 284 | #[cfg(target_os = "linux")] 285 | #[allow(non_snake_case)] 286 | /// # Safety 287 | /// This comes from a closed source C library. Rewrite it in Rust. 288 | pub unsafe fn CH347Uart_Open(_DevI: ULONG) -> HANDLE { 289 | std::ptr::null_mut::() 290 | } 291 | 292 | #[cfg(target_os = "linux")] 293 | #[allow(non_snake_case)] 294 | /// # Safety 295 | /// This comes from a closed source C library. Rewrite it in Rust. 296 | pub unsafe fn CH347Uart_SetDeviceNotify() {} 297 | 298 | #[cfg_attr(target_os = "linux", link(name = "ch347"))] 299 | #[cfg_attr(target_os = "windows", link(name = "CH347DLLA64"))] 300 | extern "C" { 301 | 302 | /// 该函数用于获得驱动版本、库版本、设备版本、芯片类型(CH341(FS)/CH347(HS)) 303 | /// # Arguments 304 | /// 305 | /// * `iIndex` - 指定操作设备序号 306 | /// * `iDriverVer` - 驱动版本信息 307 | /// * `iDLLVer` - 库版本信息 308 | /// * `ibcdDevice` - 设备版本信息 309 | /// * `iChipType` - 芯片类型 310 | /// 311 | /// # Return 312 | /// 313 | /// 执行成功返回 1,失败返回 0 314 | /// 315 | pub fn CH347GetVersion( 316 | iIndex: libc::c_ulong, 317 | iDriverVer: *mut libc::c_uchar, // 驱动版本 318 | iDLLVer: *mut libc::c_uchar, // 库版本 319 | ibcdDevice: *mut libc::c_uchar, // 设备版本 320 | iChipType: *mut libc::c_uchar, // 芯片类型(CH341(FS)/CH347HS) 321 | ) -> BOOL; 322 | 323 | /// 该函数用于打开 CH347 设备,支持 CH347 所有模式下的 SPI/I2C/JTAG 接口的打开 324 | /// 325 | /// # Arguments 326 | /// 327 | /// * `DevI` - 指定操作设备序号 328 | /// 329 | /// # Return 330 | /// 331 | /// 执行成功返回设备序号 332 | /// 333 | #[cfg(target_os = "linux")] 334 | pub fn CH347OpenDevice(DevI: ULONG) -> i32; 335 | #[cfg(target_os = "windows")] 336 | pub fn CH347OpenDevice(DevI: ULONG) -> HANDLE; 337 | 338 | /// 该函数用于关闭 CH347 设备,支持 CH347 所有模式下 SPI/I2C/JTAG 接口的关闭 339 | /// 340 | /// # Arguments 341 | /// 342 | /// * `DevI` - 指定操作设备序号 343 | /// 344 | /// # Return 345 | /// 346 | /// 执行成功返回 1,失败返回 0 347 | /// 348 | pub fn CH347CloseDevice(DevI: ULONG) -> BOOL; 349 | 350 | /// 该函数用于获取设备当前接口模式、VID/PID 等信息 351 | /// 352 | /// # Arguments 353 | /// 354 | /// * `iIndex` - 指定操作设备序号 355 | /// * `DevInformation` - 设备信息结构体 356 | /// 357 | /// # Return 358 | /// 359 | /// 执行成功返回 1,失败返回 0 360 | /// 361 | pub fn CH347GetDeviceInfor(iIndex: ULONG, DevInformation: *const DeviceInfo) -> BOOL; 362 | 363 | /// 设定设备事件通知程序 364 | /// 365 | /// 该函数用于指定设备事件通知程序,可用于 CH347 所有模式下 SPI/I2C/JTAG 接口的动态插拔检测 366 | /// 367 | /// # Arguments 368 | /// 369 | /// * `iIndex` - 指定设备序号,0对应第一个设备 370 | /// * `iDeviceID` - 可选参数,指向字符串,指定被监控的设备的ID,字符串以\0终止 371 | /// * `iNotifyRoutine` - 指定设备事件回调程序,为NULL则取消事件通知,否则在检测到事件时调用该程序 372 | /// 373 | /// # Return 374 | /// 375 | /// 执行成功返回 1,失败返回 0 376 | /// 377 | /// # Example 378 | /// 379 | /// ```c 380 | /// // 启用 CH347 同步串行接口 USB 的插入和移除的监测: 381 | /// CH347SetDeviceNotify(DevIndex, USBDevID, UsbDevPnpNotify); 382 | /// // 关闭 CH347 同步串行接口 USB 的插入和移除的监测,在程序退出时一定要关闭。 383 | /// CH347SetDeviceNotify(DevIndex, USBDevID, NULL); 384 | /// 385 | /// // CH347 设备插拔检测通知程序 386 | /// VOID CALLBACK UsbDevPnpNotify (ULONG iEventStatus ) 387 | /// { 388 | /// if(iEventStatus==CH347_DEVICE_ARRIVAL) // 设备插入事件,已经插入 389 | /// PostMessage(DebugHwnd,WM_CH347DevArrive,0,0); 390 | /// else if(iEventStatus==CH347_DEVICE_REMOVE) // 设备拔出事件,已经拔出 391 | /// PostMessage(DebugHwnd,WM_CH347DevRemove,0,0); 392 | /// return; 393 | /// } 394 | /// ``` 395 | /// 若需做到准确检测各模式下的 SPI/I2C/JTAG 接口插拔信息,可写下如下完整 USBID,在使用 CH347SetDeviceNotify 时将 iDeviceID 替换成相应的 USBID 宏即可。 396 | /// ```c 397 | /// //MODE1 SPI/I2C 398 | /// #define USBID_VEN_Mode1_SPI_I2C "VID_1A86&PID_55DB&MI_02\0" 399 | /// //MODE2 SPI/I2C 400 | /// #define USBID_HID_Mode2_SPI_I2C "VID_1A86&PID_55DC&MI_01\0" 401 | /// //MODE3 JTAG/I2C 402 | /// #define USBID_VEN_Mode3_JTAG_I2C "VID_1A86&PID_55DA&MI_02\0" 403 | /// ``` 404 | /// 405 | /// # Comment 406 | /// 407 | /// iDeviceID 该参数为可变参数,若需实现 CH347 设备的插拔检测,可定义宏如下:。 408 | /// ```c 409 | /// #define CH347DevID "VID_1A86&PID_55D\0" 410 | /// ``` 411 | /// 传参时 iDeviceID 替换为 CH347DevID 即可实现对 CH347 同步串行接口的动态插拔检测 412 | /// 413 | /// 若需准确检测各模式下接口的插拔动作,可写下完整的 USBID,以模式 1 中 SPI 接口为例,可定义下方宏: 414 | /// ```c 415 | /// #define USBID_VEN_SPI_I2C “VID_1A86&PID_55DB&MI_02\0” 416 | /// ``` 417 | /// 传参时 iDeviceID 替换为 USBID_VEN_SPI_I2C 即可实现对 CH347 模式 1 的 SPI&I2C 接口的动态插拔检测 418 | /// 419 | pub fn CH347SetDeviceNotify( 420 | iIndex: ULONG, 421 | iDeviceID: *const libc::c_uchar, 422 | iNotifyRoutine: *mut libc::c_void, 423 | // iNotifyRoutine: Ch347NotifyRoutine, 424 | ) -> BOOL; 425 | 426 | /// 该函数用于打开 CH347 串口 427 | /// 428 | /// ```c 429 | /// HANDLE WINAPI CH347Uart_Open(ULONG iIndex); 430 | /// ``` 431 | #[cfg(target_os = "windows")] 432 | pub fn CH347Uart_Open(DevI: ULONG) -> HANDLE; 433 | 434 | /// 该函数用于关闭 CH347 串口 435 | /// 436 | /// ```c 437 | /// HANDLE WINAPI CH347Uart_Close(ULONG iIndex); 438 | /// ``` 439 | pub fn CH347Uart_Close(DevI: ULONG) -> HANDLE; 440 | 441 | /// ```c 442 | /// BOOL WINAPI CH347Uart_GetDeviceInfor(ULONG iIndex,mDeviceInforS *DevInformation); 443 | /// ``` 444 | #[cfg(target_os = "windows")] 445 | pub fn CH347Uart_GetDeviceInfor(iIndex: ULONG, DevInformation: *const DeviceInfo) -> BOOL; 446 | 447 | // pub fn CH347Uart_SetDeviceNotify 448 | 449 | /// 获取CH347的GPIO方向和引脚电平值 450 | /// 451 | /// ```c 452 | /// BOOL WINAPI CH347GPIO_Get(ULONG iIndex, 453 | /// UCHAR *iDir, //引脚方向:GPIO0-7对应位0-7.0:输入;1:输出 454 | /// UCHAR *iData); //GPIO0电平:GPIO0-7对应位0-7,0:低电平;1:高电平) 455 | /// ``` 456 | pub fn CH347GPIO_Get(iIndex: ULONG, iDir: PUCHAR, iData: PUCHAR) -> BOOL; 457 | 458 | /// 设置CH347的GPIO方向和引脚电平值 459 | /// 460 | /// ```c 461 | /// BOOL WINAPI CH347GPIO_Set(ULONG iIndex, 462 | /// UCHAR iEnable, //数据有效标志:对应位0-7,对应GPIO0-7. 463 | /// UCHAR iSetDirOut, //设置I/O方向,某位清0则对应引脚为输入,某位置1则对应引脚为输出.GPIO0-7对应位0-7. 464 | /// UCHAR iSetDataOut); //输出数据,如果I/O方向为输出,那么某位清0时对应引脚输出低电平,某位置1时对应引脚输出高电平 465 | /// ``` 466 | pub fn CH347GPIO_Set( 467 | iIndex: ULONG, 468 | iEnable: UCHAR, 469 | iSetDirOut: UCHAR, 470 | iSetDataOut: UCHAR, 471 | ) -> BOOL; 472 | 473 | /// ```c 474 | /// BOOL WINAPI CH347I2C_Set( 475 | /// ULONG iIndex, // 指定设备序号 476 | /// ULONG iMode ); // 指定模式,见下行 477 | /// // 位1-位0: I2C接口速度/SCL频率, 00=低速/20KHz,01=标准/100KHz(默认值),10=快速/400KHz,11=高速/750KHz 478 | /// // 其它保留,必须为0 479 | /// ``` 480 | pub fn CH347I2C_Set(iIndex: ULONG, iMode: ULONG) -> BOOL; 481 | 482 | /// 处理I2C数据流,2线接口,时钟线为SCL引脚,数据线为SDA引脚 483 | /// 484 | /// ```c 485 | /// BOOL WINAPI CH347StreamI2C( 486 | /// ULONG iIndex, // 指定设备序号 487 | /// ULONG iWriteLength, // 准备写出的数据字节数 488 | /// PVOID iWriteBuffer, // 指向一个缓冲区,放置准备写出的数据,首字节通常是I2C设备地址及读写方向位 489 | /// ULONG iReadLength, // 准备读取的数据字节数 490 | /// PVOID oReadBuffer ); // 指向一个缓冲区,返回后是读入的数据 491 | /// ``` 492 | pub fn CH347StreamI2C( 493 | iIndex: ULONG, 494 | iWriteLength: ULONG, 495 | iWriteBuffer: PVOID, 496 | iReadLength: ULONG, 497 | oReadBuffer: PVOID, 498 | ) -> BOOL; 499 | 500 | /// SPI控制器初始化 501 | /// 502 | /// ``` c 503 | /// BOOL WINAPI CH347SPI_Init(ULONG iIndex,mSpiCfgS *SpiCfg); 504 | /// ``` 505 | pub fn CH347SPI_Init(iIndex: ULONG, mSpiCfgS: *mut SpiConfig) -> BOOL; 506 | 507 | /// 获取SPI控制器配置信息 508 | /// ```c 509 | /// BOOL WINAPI CH347SPI_GetCfg(ULONG iIndex,mSpiCfgS *SpiCfg); 510 | /// ``` 511 | pub fn CH347SPI_GetCfg(iIndex: ULONG, mSpiCfgS: *mut SpiConfig) -> BOOL; 512 | 513 | pub fn CH347SPI_ChangeCS(iIndex: ULONG, iStatus: UCHAR); 514 | 515 | /// 该函数用于设置 SPI 片选 516 | pub fn CH347SPI_SetChipSelect( 517 | iIndex: ULONG, 518 | iEnableSelect: USHORT, 519 | iChipSelect: USHORT, 520 | iIsAutoDeativeCS: ULONG, 521 | iActiveDelay: ULONG, 522 | iDelayDeactive: ULONG, 523 | ); 524 | 525 | /// 该函数用于 SPI 写数据 526 | pub fn CH347SPI_Write( 527 | iIndex: ULONG, 528 | iChipSelect: ULONG, 529 | iLength: ULONG, 530 | iWriteStep: ULONG, 531 | ioBuffer: PVOID, 532 | ) -> BOOL; 533 | 534 | /// 该函数用于读取 SPI 数据 535 | pub fn CH347SPI_Read( 536 | iIndex: ULONG, 537 | iChipSelect: ULONG, 538 | oLength: ULONG, 539 | iLength: PULONG, 540 | ioBuffer: PVOID, 541 | ) -> BOOL; 542 | 543 | /// 该函数用于写入和读取 SPI 数据流 544 | pub fn CH347SPI_WriteRead( 545 | iIndex: ULONG, 546 | iChipSelect: ULONG, 547 | iLength: ULONG, 548 | ioBuffer: PVOID, 549 | ) -> BOOL; 550 | 551 | /// 该函数用于处理 SPI 数据流,写入的同时读出数据 552 | pub fn CH347StreamSPI4( 553 | iIndex: ULONG, 554 | iChipSelect: ULONG, 555 | iLength: ULONG, 556 | ioBuffer: PVOID, 557 | ) -> BOOL; 558 | } 559 | -------------------------------------------------------------------------------- /src/bin/ch347tool/spi_flash/reg.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | error::Error, 3 | io::{stdin, stdout, Write}, 4 | }; 5 | 6 | use clap::Parser; 7 | use cli_table::{ 8 | format::{Align, Justify}, 9 | Cell, Style, Table, TableStruct, 10 | }; 11 | 12 | use super::utils; 13 | 14 | #[derive(Parser, Clone, Debug)] 15 | #[clap(about = "Operate chip registers")] 16 | pub struct CmdReg { 17 | /// register name, eg: QE, addr_mode, ADP, 18 | #[clap(value_parser)] 19 | register: Option, 20 | 21 | /// write value, eg: true, false, 0b11, 0x02 22 | #[clap(value_parser)] 23 | value: Option, 24 | } 25 | 26 | pub fn cli_main(flash_args: &super::CmdSpiFlash, args: &CmdReg) -> Result<(), Box> { 27 | let (device, chip_info) = flash_args.init()?; 28 | 29 | let reg_defines = match chip_info.vendor.reg_defines { 30 | None => return Err("Not define Registers".into()), 31 | Some(a) => a, 32 | }; 33 | 34 | if (args.register == None) && (args.value == None) { 35 | // show all registers 36 | show_all_registers(device, chip_info, reg_defines)?; 37 | } else if (args.register != None) && (args.value == None) { 38 | // Read the specified register 39 | 40 | let reg_name = args.register.as_deref().unwrap(); 41 | 42 | let find_result = utils::find_reg_by_name(reg_name, reg_defines); 43 | 44 | let find_result = match find_result { 45 | None => return Err(format!("Not Found Reg: {:?}", reg_name).into()), 46 | Some(a) => a, 47 | }; 48 | 49 | show_one_registers(device, find_result)?; 50 | } else { 51 | // write to the specified register 52 | 53 | let reg_name = args.register.as_deref().unwrap(); 54 | let reg_write_value = args.value.as_deref().unwrap(); 55 | 56 | let find_result = utils::find_reg_by_name(reg_name, reg_defines); 57 | 58 | let find_result = match find_result { 59 | None => return Err(format!("Not Found Reg: {:?}", reg_name).into()), 60 | Some(a) => a, 61 | }; 62 | 63 | write_registers(device, find_result, reg_write_value)?; 64 | } 65 | 66 | Ok(()) 67 | } 68 | 69 | fn show_register_item_table(r: &[ch347_rs::RegisterItem], v: ch347_rs::RegReadRet) -> TableStruct { 70 | let mut items_table = Vec::new(); 71 | 72 | // name line 73 | let mut items_name_table = Vec::new(); 74 | items_name_table.push("Name".cell().bold(true)); 75 | for ri in r.iter().rev() { 76 | items_name_table.push(ri.name.cell()); 77 | } 78 | items_table.push(items_name_table); 79 | 80 | // bit line 81 | let mut items_posion_table = Vec::new(); 82 | items_posion_table.push("Bit".cell().bold(true)); 83 | for ri in r.iter().rev() { 84 | items_posion_table.push( 85 | if ri.width == 1 { 86 | format!("{}", ri.offset) 87 | } else { 88 | format!("{}..{}", ri.offset + ri.width - 1, ri.offset) 89 | } 90 | .cell(), 91 | ); 92 | } 93 | items_table.push(items_posion_table); 94 | 95 | let mut items_desc_table = Vec::new(); 96 | items_desc_table.push("Desc".cell().bold(true)); 97 | for ri in r.iter().rev() { 98 | items_desc_table.push(ri.describe.cell()); 99 | } 100 | items_table.push(items_desc_table); 101 | 102 | // value line 103 | let mut items_val_table = Vec::new(); 104 | items_val_table.push("Val".cell().bold(true)); 105 | for ri in r.iter().rev() { 106 | items_val_table.push( 107 | match v { 108 | ch347_rs::RegReadRet::One(a) => { 109 | if ri.width == 1 { 110 | utils::display_bool_with_color(a & (1 << ri.offset) != 0) 111 | } else { 112 | let mut v: u8 = 0; 113 | 114 | for i in (ri.offset..(ri.offset + ri.width)).rev() { 115 | v <<= 1; 116 | if a & (1 << i) != 0 { 117 | v |= 1; 118 | } 119 | } 120 | 121 | let width = ri.width as usize; 122 | format!("{:0>width$b}'b{}", v, width) 123 | } 124 | } 125 | ch347_rs::RegReadRet::Muti(_) => { 126 | panic!(); 127 | } 128 | } 129 | .cell(), 130 | ); 131 | } 132 | items_table.push(items_val_table); 133 | 134 | let mut items_access_table = Vec::new(); 135 | items_access_table.push("Access".cell().bold(true)); 136 | for ri in r.iter().rev() { 137 | items_access_table.push( 138 | console::style(ri.access.to_string()) 139 | .bg(match ri.access { 140 | ch347_rs::RegisterAccess::ReadOnly => console::Color::Black, 141 | ch347_rs::RegisterAccess::ReadWrite => console::Color::Blue, 142 | ch347_rs::RegisterAccess::ReadWriteOTP => console::Color::Yellow, 143 | }) 144 | .cell(), 145 | ); 146 | } 147 | items_table.push(items_access_table); 148 | 149 | items_table.table() 150 | } 151 | 152 | fn show_all_registers( 153 | spi_flash: ch347_rs::SpiFlash, 154 | _chip_info: ch347_rs::Chip, 155 | reg_defines: &[ch347_rs::Register], 156 | ) -> Result<(), Box> { 157 | let mut table = Vec::new(); 158 | 159 | for r in reg_defines { 160 | let v = match (r.reader)(&spi_flash) { 161 | Err(e) => panic!("{}", e), 162 | Ok(v) => v, 163 | }; 164 | 165 | table.push(match &v { 166 | ch347_rs::RegReadRet::One(a) => { 167 | vec![ 168 | r.name.cell().align(Align::Center), 169 | format!("0x{:02X?}", a).cell().align(Align::Center), 170 | match r.items { 171 | None => "".cell(), 172 | Some(items) => show_register_item_table(items, v) 173 | .display()? 174 | .cell() 175 | .justify(Justify::Left), 176 | }, 177 | ] 178 | } 179 | ch347_rs::RegReadRet::Muti(a) => { 180 | vec![ 181 | r.name.cell().align(Align::Center), 182 | format!("{} Byte ->", a.len()).cell().align(Align::Center), 183 | format!("{:02X?}", a).cell(), 184 | ] 185 | } 186 | }); 187 | 188 | // table.push(vec![r.name.to_string(), "".to_string(), v_str]); 189 | // println!("{} {}", r.name, v_str); 190 | } 191 | 192 | let table = table.table().title(vec![ 193 | "Name".cell().bold(true), 194 | "Value".cell().bold(true), 195 | "Item".cell().bold(true), 196 | ]); 197 | 198 | println!("{}", table.display()?); 199 | 200 | Ok(()) 201 | } 202 | 203 | fn show_one_registers( 204 | spi_flash: ch347_rs::SpiFlash, 205 | reg_result: utils::FindRegType, 206 | ) -> Result<(), Box> { 207 | match reg_result { 208 | utils::FindRegType::Reg(r) => { 209 | let v = match (r.reader)(&spi_flash) { 210 | Err(e) => panic!("{}", e), 211 | Ok(v) => v, 212 | }; 213 | 214 | println!("{} {:02X?}", r.name, v); 215 | 216 | let mut table = Vec::new(); 217 | 218 | match v { 219 | ch347_rs::RegReadRet::One(a) => { 220 | table.push(vec![ 221 | r.name.cell().align(Align::Center), 222 | format!("0x{:02X?}", a).cell().align(Align::Center), 223 | match r.items { 224 | None => "".cell(), 225 | Some(items) => show_register_item_table(items, v) 226 | .display()? 227 | .cell() 228 | .justify(Justify::Left), 229 | }, 230 | ]); 231 | } 232 | ch347_rs::RegReadRet::Muti(a) => { 233 | table.push(vec![ 234 | r.name.cell().align(Align::Center), 235 | format!("{} Byte ->", a.len()).cell().align(Align::Center), 236 | format!("{:02X?}", a).cell(), 237 | ]); 238 | } 239 | } 240 | 241 | let table = table.table().title(vec![ 242 | "Name".cell().bold(true), 243 | "Value".cell().bold(true), 244 | "Item".cell().bold(true), 245 | ]); 246 | 247 | println!("{}", table.display()?); 248 | } 249 | utils::FindRegType::RegItem(r, i) => { 250 | let v = match (r.reader)(&spi_flash) { 251 | Err(e) => panic!("{}", e), 252 | Ok(v) => v, 253 | }; 254 | 255 | let item = &r.items.unwrap()[i]; 256 | println!( 257 | "{}({:?}) <= {}({}..{})", 258 | item.name, 259 | item.alias.join(","), 260 | r.name, 261 | item.offset + item.width, 262 | item.offset, 263 | ); 264 | println!("Desc: {}", item.describe); 265 | println!("Access: {:?}({})", item.access, item.access); 266 | 267 | match v { 268 | ch347_rs::RegReadRet::One(a) => { 269 | let mut v: u8 = 0; 270 | 271 | if item.width == 1 { 272 | let v = a & (1 << item.offset) != 0; 273 | 274 | let v_str = if v { 275 | console::style("True").green() 276 | } else { 277 | console::style("False").red() 278 | }; 279 | 280 | println!("Value: {}", v_str); 281 | } else { 282 | for i in (item.offset..(item.offset + item.width)).rev() { 283 | v <<= 1; 284 | if a & (1 << i) != 0 { 285 | v |= 1; 286 | } 287 | } 288 | 289 | let width = item.width as usize; 290 | println!("Value: {:0>width$b}'b{}", v, width) 291 | } 292 | } 293 | ch347_rs::RegReadRet::Muti(a) => { 294 | let l: Vec = a.iter().map(|i| format!("0x{:02X}", i)).collect(); 295 | let l = l.join(", "); 296 | println!("{}", l); 297 | } 298 | } 299 | } 300 | } 301 | 302 | Ok(()) 303 | } 304 | 305 | fn write_registers( 306 | spi_flash: ch347_rs::SpiFlash, 307 | reg_result: utils::FindRegType, 308 | input_str: &str, 309 | ) -> Result<(), Box> { 310 | match reg_result { 311 | utils::FindRegType::Reg(r) => { 312 | let v = match (r.reader)(&spi_flash) { 313 | Err(e) => panic!("{}", e), 314 | Ok(v) => v, 315 | }; 316 | 317 | println!("{} old: {}", r.name, v); 318 | 319 | let write_val: u8 = utils::parse_cli_arg_number(&input_str, false)?; 320 | 321 | let reg_writer = match r.writer { 322 | None => panic!("The Reg Not Support Write"), 323 | Some(a) => a, 324 | }; 325 | 326 | println!("will to write: {}", utils::display_u8_hex(write_val)); 327 | 328 | let v = match v { 329 | ch347_rs::RegReadRet::One(a) => a, 330 | ch347_rs::RegReadRet::Muti(a) => a[0], 331 | }; 332 | 333 | if let Some(items) = r.items { 334 | for ri in items { 335 | let old_ri_val: bool = v & (1 << ri.offset) != 0; 336 | let new_ri_val: bool = write_val & (1 << ri.offset) != 0; 337 | 338 | if old_ri_val == new_ri_val { 339 | continue; 340 | } 341 | 342 | if ri.width == 1 { 343 | println!( 344 | " Name: {} Val: {} => {} Access: {}", 345 | ri.name, 346 | utils::display_bool_with_color(old_ri_val), 347 | utils::display_bool_with_color(new_ri_val), 348 | ri.access, 349 | ); 350 | } else { 351 | let mut old_ri_val: u8 = 0; 352 | let mut new_ri_val: u8 = 0; 353 | 354 | for i in (ri.offset..(ri.offset + ri.width)).rev() { 355 | old_ri_val <<= 1; 356 | if v & (1 << i) != 0 { 357 | old_ri_val |= 1; 358 | } 359 | 360 | new_ri_val <<= 1; 361 | if write_val & (1 << i) != 0 { 362 | new_ri_val |= 1; 363 | } 364 | } 365 | 366 | if old_ri_val == new_ri_val { 367 | continue; 368 | } 369 | 370 | let width = ri.width as usize; 371 | println!( 372 | " Name: {} Val: 0b{:0>width$b} => 0b{:0>width$b} Access: {}", 373 | ri.name, old_ri_val, new_ri_val, ri.access, 374 | ); 375 | } 376 | } 377 | } 378 | 379 | reg_writer(&spi_flash, &vec![write_val])?; 380 | 381 | let v = (r.reader)(&spi_flash)?; 382 | println!("ReRead Chk: {}", v); 383 | } 384 | utils::FindRegType::RegItem(r, i) => { 385 | let ri = &r.items.unwrap()[i]; 386 | let width = ri.width as usize; 387 | 388 | let reg_writer = match r.writer { 389 | None => return Err("The Reg Not Support Write".into()), 390 | Some(a) => a, 391 | }; 392 | 393 | if let ch347_rs::RegisterAccess::ReadOnly = ri.access { 394 | return Err("This RegItem is Read-only, cannot be write".into()); 395 | } 396 | 397 | let new_ri_val = utils::parse_cli_arg_number(input_str, width == 1)?; 398 | 399 | let v = match (r.reader)(&spi_flash)? { 400 | ch347_rs::RegReadRet::One(a) => a, 401 | ch347_rs::RegReadRet::Muti(a) => a[0], 402 | }; 403 | 404 | let ri_bitmask: u8 = if width == 1 { 405 | 1 << ri.offset 406 | } else { 407 | !(1 << ri.width) << ri.offset 408 | }; 409 | 410 | let write_val = v & !(ri_bitmask); 411 | let write_val = write_val | ((new_ri_val << ri.offset) & ri_bitmask); 412 | 413 | let mut old_ri_val: u8 = 0; 414 | for i in (ri.offset..(ri.offset + ri.width)).rev() { 415 | old_ri_val <<= 1; 416 | if v & (1 << i) != 0 { 417 | old_ri_val |= 1; 418 | } 419 | } 420 | 421 | if width == 1 { 422 | println!( 423 | "{} Val: {} => {}", 424 | ri.name, 425 | utils::display_bool_with_color(old_ri_val != 0), 426 | utils::display_bool_with_color(new_ri_val != 0), 427 | ); 428 | } else { 429 | println!( 430 | "{} Val: 0b{:0>width$b} => 0b{:0>width$b}", 431 | ri.name, old_ri_val, new_ri_val 432 | ); 433 | } 434 | 435 | println!("{} Val:", r.name); 436 | println!(" Old: {}", utils::display_u8_hex(v)); 437 | println!(" New: {}", utils::display_u8_hex(write_val)); 438 | 439 | if old_ri_val == new_ri_val { 440 | stdout().write_all(b"Need to rewrite ? (Y): ")?; 441 | stdout().flush()?; 442 | let mut s = String::new(); 443 | stdin().read_line(&mut s)?; 444 | if !s.trim().to_lowercase().eq("y") { 445 | return Err("Operation must be confirmed".into()); 446 | } 447 | } 448 | 449 | if let ch347_rs::RegisterAccess::ReadWriteOTP = ri.access { 450 | // if let ch347_rs::RegisterAccess::ReadWrite = ri.access { 451 | stdout().write_all(b"OTP Reg must be confirmed(Y): ")?; 452 | stdout().flush()?; 453 | let mut s = String::new(); 454 | stdin().read_line(&mut s)?; 455 | if !s.trim().to_lowercase().eq("y") { 456 | return Err("Operation must be confirmed".into()); 457 | } 458 | } 459 | 460 | reg_writer(&spi_flash, &vec![write_val])?; 461 | 462 | match (r.reader)(&spi_flash)? { 463 | ch347_rs::RegReadRet::One(a) => { 464 | println!(" Chk: {}", utils::display_u8_hex(a)); 465 | } 466 | ch347_rs::RegReadRet::Muti(a) => { 467 | println!(" Chk: {:02X?}", a); 468 | } 469 | }; 470 | } 471 | } 472 | 473 | Ok(()) 474 | } 475 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "android_system_properties" 7 | version = "0.1.5" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 10 | dependencies = [ 11 | "libc", 12 | ] 13 | 14 | [[package]] 15 | name = "atty" 16 | version = "0.2.14" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 19 | dependencies = [ 20 | "hermit-abi", 21 | "libc", 22 | "winapi", 23 | ] 24 | 25 | [[package]] 26 | name = "autocfg" 27 | version = "1.1.0" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 30 | 31 | [[package]] 32 | name = "bitflags" 33 | version = "1.3.2" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 36 | 37 | [[package]] 38 | name = "bstr" 39 | version = "0.2.17" 40 | source = "registry+https://github.com/rust-lang/crates.io-index" 41 | checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" 42 | dependencies = [ 43 | "lazy_static", 44 | "memchr", 45 | "regex-automata", 46 | "serde", 47 | ] 48 | 49 | [[package]] 50 | name = "bumpalo" 51 | version = "3.11.0" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" 54 | 55 | [[package]] 56 | name = "cc" 57 | version = "1.0.73" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" 60 | dependencies = [ 61 | "jobserver", 62 | ] 63 | 64 | [[package]] 65 | name = "cfg-if" 66 | version = "1.0.0" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 69 | 70 | [[package]] 71 | name = "ch347_rs" 72 | version = "0.2.1" 73 | dependencies = [ 74 | "clap", 75 | "cli-table", 76 | "console", 77 | "hex", 78 | "humantime", 79 | "indicatif", 80 | "libc", 81 | "serde", 82 | "serde_json", 83 | "shadow-rs", 84 | ] 85 | 86 | [[package]] 87 | name = "clap" 88 | version = "3.2.22" 89 | source = "registry+https://github.com/rust-lang/crates.io-index" 90 | checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750" 91 | dependencies = [ 92 | "atty", 93 | "bitflags", 94 | "clap_derive", 95 | "clap_lex", 96 | "indexmap", 97 | "once_cell", 98 | "strsim", 99 | "termcolor", 100 | "textwrap", 101 | ] 102 | 103 | [[package]] 104 | name = "clap_derive" 105 | version = "3.2.18" 106 | source = "registry+https://github.com/rust-lang/crates.io-index" 107 | checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" 108 | dependencies = [ 109 | "heck", 110 | "proc-macro-error", 111 | "proc-macro2", 112 | "quote", 113 | "syn", 114 | ] 115 | 116 | [[package]] 117 | name = "clap_lex" 118 | version = "0.2.4" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" 121 | dependencies = [ 122 | "os_str_bytes", 123 | ] 124 | 125 | [[package]] 126 | name = "cli-table" 127 | version = "0.4.7" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | checksum = "adfbb116d9e2c4be7011360d0c0bee565712c11e969c9609b25b619366dc379d" 130 | dependencies = [ 131 | "cli-table-derive", 132 | "csv", 133 | "termcolor", 134 | "unicode-width", 135 | ] 136 | 137 | [[package]] 138 | name = "cli-table-derive" 139 | version = "0.4.5" 140 | source = "registry+https://github.com/rust-lang/crates.io-index" 141 | checksum = "2af3bfb9da627b0a6c467624fb7963921433774ed435493b5c08a3053e829ad4" 142 | dependencies = [ 143 | "proc-macro2", 144 | "quote", 145 | "syn", 146 | ] 147 | 148 | [[package]] 149 | name = "console" 150 | version = "0.15.2" 151 | source = "registry+https://github.com/rust-lang/crates.io-index" 152 | checksum = "c050367d967ced717c04b65d8c619d863ef9292ce0c5760028655a2fb298718c" 153 | dependencies = [ 154 | "encode_unicode", 155 | "lazy_static", 156 | "libc", 157 | "terminal_size", 158 | "unicode-width", 159 | "winapi", 160 | ] 161 | 162 | [[package]] 163 | name = "const_fn" 164 | version = "0.4.9" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935" 167 | 168 | [[package]] 169 | name = "const_format" 170 | version = "0.2.26" 171 | source = "registry+https://github.com/rust-lang/crates.io-index" 172 | checksum = "939dc9e2eb9077e0679d2ce32de1ded8531779360b003b4a972a7a39ec263495" 173 | dependencies = [ 174 | "const_format_proc_macros", 175 | ] 176 | 177 | [[package]] 178 | name = "const_format_proc_macros" 179 | version = "0.2.22" 180 | source = "registry+https://github.com/rust-lang/crates.io-index" 181 | checksum = "ef196d5d972878a48da7decb7686eded338b4858fbabeed513d63a7c98b2b82d" 182 | dependencies = [ 183 | "proc-macro2", 184 | "quote", 185 | "unicode-xid", 186 | ] 187 | 188 | [[package]] 189 | name = "core-foundation-sys" 190 | version = "0.8.3" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" 193 | 194 | [[package]] 195 | name = "csv" 196 | version = "1.1.6" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" 199 | dependencies = [ 200 | "bstr", 201 | "csv-core", 202 | "itoa 0.4.8", 203 | "ryu", 204 | "serde", 205 | ] 206 | 207 | [[package]] 208 | name = "csv-core" 209 | version = "0.1.10" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" 212 | dependencies = [ 213 | "memchr", 214 | ] 215 | 216 | [[package]] 217 | name = "encode_unicode" 218 | version = "0.3.6" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" 221 | 222 | [[package]] 223 | name = "errno" 224 | version = "0.2.8" 225 | source = "registry+https://github.com/rust-lang/crates.io-index" 226 | checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" 227 | dependencies = [ 228 | "errno-dragonfly", 229 | "libc", 230 | "winapi", 231 | ] 232 | 233 | [[package]] 234 | name = "errno-dragonfly" 235 | version = "0.1.2" 236 | source = "registry+https://github.com/rust-lang/crates.io-index" 237 | checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" 238 | dependencies = [ 239 | "cc", 240 | "libc", 241 | ] 242 | 243 | [[package]] 244 | name = "form_urlencoded" 245 | version = "1.1.0" 246 | source = "registry+https://github.com/rust-lang/crates.io-index" 247 | checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" 248 | dependencies = [ 249 | "percent-encoding", 250 | ] 251 | 252 | [[package]] 253 | name = "git2" 254 | version = "0.15.0" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | checksum = "2994bee4a3a6a51eb90c218523be382fd7ea09b16380b9312e9dbe955ff7c7d1" 257 | dependencies = [ 258 | "bitflags", 259 | "libc", 260 | "libgit2-sys", 261 | "log", 262 | "url", 263 | ] 264 | 265 | [[package]] 266 | name = "hashbrown" 267 | version = "0.12.3" 268 | source = "registry+https://github.com/rust-lang/crates.io-index" 269 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 270 | 271 | [[package]] 272 | name = "heck" 273 | version = "0.4.0" 274 | source = "registry+https://github.com/rust-lang/crates.io-index" 275 | checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" 276 | 277 | [[package]] 278 | name = "hermit-abi" 279 | version = "0.1.19" 280 | source = "registry+https://github.com/rust-lang/crates.io-index" 281 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 282 | dependencies = [ 283 | "libc", 284 | ] 285 | 286 | [[package]] 287 | name = "hex" 288 | version = "0.4.3" 289 | source = "registry+https://github.com/rust-lang/crates.io-index" 290 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 291 | 292 | [[package]] 293 | name = "humantime" 294 | version = "2.1.0" 295 | source = "registry+https://github.com/rust-lang/crates.io-index" 296 | checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" 297 | 298 | [[package]] 299 | name = "iana-time-zone" 300 | version = "0.1.50" 301 | source = "registry+https://github.com/rust-lang/crates.io-index" 302 | checksum = "fd911b35d940d2bd0bea0f9100068e5b97b51a1cbe13d13382f132e0365257a0" 303 | dependencies = [ 304 | "android_system_properties", 305 | "core-foundation-sys", 306 | "js-sys", 307 | "wasm-bindgen", 308 | "winapi", 309 | ] 310 | 311 | [[package]] 312 | name = "idna" 313 | version = "0.3.0" 314 | source = "registry+https://github.com/rust-lang/crates.io-index" 315 | checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" 316 | dependencies = [ 317 | "unicode-bidi", 318 | "unicode-normalization", 319 | ] 320 | 321 | [[package]] 322 | name = "indexmap" 323 | version = "1.9.1" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" 326 | dependencies = [ 327 | "autocfg", 328 | "hashbrown", 329 | ] 330 | 331 | [[package]] 332 | name = "indicatif" 333 | version = "0.17.1" 334 | source = "registry+https://github.com/rust-lang/crates.io-index" 335 | checksum = "bfddc9561e8baf264e0e45e197fd7696320026eb10a8180340debc27b18f535b" 336 | dependencies = [ 337 | "console", 338 | "number_prefix", 339 | "unicode-width", 340 | ] 341 | 342 | [[package]] 343 | name = "io-lifetimes" 344 | version = "0.7.3" 345 | source = "registry+https://github.com/rust-lang/crates.io-index" 346 | checksum = "1ea37f355c05dde75b84bba2d767906ad522e97cd9e2eef2be7a4ab7fb442c06" 347 | 348 | [[package]] 349 | name = "is_debug" 350 | version = "1.0.1" 351 | source = "registry+https://github.com/rust-lang/crates.io-index" 352 | checksum = "06d198e9919d9822d5f7083ba8530e04de87841eaf21ead9af8f2304efd57c89" 353 | 354 | [[package]] 355 | name = "itoa" 356 | version = "0.4.8" 357 | source = "registry+https://github.com/rust-lang/crates.io-index" 358 | checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" 359 | 360 | [[package]] 361 | name = "itoa" 362 | version = "1.0.3" 363 | source = "registry+https://github.com/rust-lang/crates.io-index" 364 | checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" 365 | 366 | [[package]] 367 | name = "jobserver" 368 | version = "0.1.25" 369 | source = "registry+https://github.com/rust-lang/crates.io-index" 370 | checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" 371 | dependencies = [ 372 | "libc", 373 | ] 374 | 375 | [[package]] 376 | name = "js-sys" 377 | version = "0.3.60" 378 | source = "registry+https://github.com/rust-lang/crates.io-index" 379 | checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" 380 | dependencies = [ 381 | "wasm-bindgen", 382 | ] 383 | 384 | [[package]] 385 | name = "lazy_static" 386 | version = "1.4.0" 387 | source = "registry+https://github.com/rust-lang/crates.io-index" 388 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 389 | 390 | [[package]] 391 | name = "libc" 392 | version = "0.2.134" 393 | source = "registry+https://github.com/rust-lang/crates.io-index" 394 | checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb" 395 | 396 | [[package]] 397 | name = "libgit2-sys" 398 | version = "0.14.0+1.5.0" 399 | source = "registry+https://github.com/rust-lang/crates.io-index" 400 | checksum = "47a00859c70c8a4f7218e6d1cc32875c4b55f6799445b842b0d8ed5e4c3d959b" 401 | dependencies = [ 402 | "cc", 403 | "libc", 404 | "libz-sys", 405 | "pkg-config", 406 | ] 407 | 408 | [[package]] 409 | name = "libz-sys" 410 | version = "1.1.8" 411 | source = "registry+https://github.com/rust-lang/crates.io-index" 412 | checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf" 413 | dependencies = [ 414 | "cc", 415 | "libc", 416 | "pkg-config", 417 | "vcpkg", 418 | ] 419 | 420 | [[package]] 421 | name = "linux-raw-sys" 422 | version = "0.0.46" 423 | source = "registry+https://github.com/rust-lang/crates.io-index" 424 | checksum = "d4d2456c373231a208ad294c33dc5bff30051eafd954cd4caae83a712b12854d" 425 | 426 | [[package]] 427 | name = "log" 428 | version = "0.4.17" 429 | source = "registry+https://github.com/rust-lang/crates.io-index" 430 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 431 | dependencies = [ 432 | "cfg-if", 433 | ] 434 | 435 | [[package]] 436 | name = "memchr" 437 | version = "2.5.0" 438 | source = "registry+https://github.com/rust-lang/crates.io-index" 439 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 440 | 441 | [[package]] 442 | name = "num_threads" 443 | version = "0.1.6" 444 | source = "registry+https://github.com/rust-lang/crates.io-index" 445 | checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" 446 | dependencies = [ 447 | "libc", 448 | ] 449 | 450 | [[package]] 451 | name = "number_prefix" 452 | version = "0.4.0" 453 | source = "registry+https://github.com/rust-lang/crates.io-index" 454 | checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" 455 | 456 | [[package]] 457 | name = "once_cell" 458 | version = "1.15.0" 459 | source = "registry+https://github.com/rust-lang/crates.io-index" 460 | checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" 461 | 462 | [[package]] 463 | name = "os_str_bytes" 464 | version = "6.3.0" 465 | source = "registry+https://github.com/rust-lang/crates.io-index" 466 | checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" 467 | 468 | [[package]] 469 | name = "percent-encoding" 470 | version = "2.2.0" 471 | source = "registry+https://github.com/rust-lang/crates.io-index" 472 | checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" 473 | 474 | [[package]] 475 | name = "pkg-config" 476 | version = "0.3.25" 477 | source = "registry+https://github.com/rust-lang/crates.io-index" 478 | checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" 479 | 480 | [[package]] 481 | name = "proc-macro-error" 482 | version = "1.0.4" 483 | source = "registry+https://github.com/rust-lang/crates.io-index" 484 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 485 | dependencies = [ 486 | "proc-macro-error-attr", 487 | "proc-macro2", 488 | "quote", 489 | "syn", 490 | "version_check", 491 | ] 492 | 493 | [[package]] 494 | name = "proc-macro-error-attr" 495 | version = "1.0.4" 496 | source = "registry+https://github.com/rust-lang/crates.io-index" 497 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 498 | dependencies = [ 499 | "proc-macro2", 500 | "quote", 501 | "version_check", 502 | ] 503 | 504 | [[package]] 505 | name = "proc-macro2" 506 | version = "1.0.46" 507 | source = "registry+https://github.com/rust-lang/crates.io-index" 508 | checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" 509 | dependencies = [ 510 | "unicode-ident", 511 | ] 512 | 513 | [[package]] 514 | name = "quote" 515 | version = "1.0.21" 516 | source = "registry+https://github.com/rust-lang/crates.io-index" 517 | checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" 518 | dependencies = [ 519 | "proc-macro2", 520 | ] 521 | 522 | [[package]] 523 | name = "regex-automata" 524 | version = "0.1.10" 525 | source = "registry+https://github.com/rust-lang/crates.io-index" 526 | checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" 527 | 528 | [[package]] 529 | name = "rustix" 530 | version = "0.35.11" 531 | source = "registry+https://github.com/rust-lang/crates.io-index" 532 | checksum = "fbb2fda4666def1433b1b05431ab402e42a1084285477222b72d6c564c417cef" 533 | dependencies = [ 534 | "bitflags", 535 | "errno", 536 | "io-lifetimes", 537 | "libc", 538 | "linux-raw-sys", 539 | "windows-sys", 540 | ] 541 | 542 | [[package]] 543 | name = "ryu" 544 | version = "1.0.11" 545 | source = "registry+https://github.com/rust-lang/crates.io-index" 546 | checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" 547 | 548 | [[package]] 549 | name = "serde" 550 | version = "1.0.145" 551 | source = "registry+https://github.com/rust-lang/crates.io-index" 552 | checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" 553 | dependencies = [ 554 | "serde_derive", 555 | ] 556 | 557 | [[package]] 558 | name = "serde_derive" 559 | version = "1.0.145" 560 | source = "registry+https://github.com/rust-lang/crates.io-index" 561 | checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" 562 | dependencies = [ 563 | "proc-macro2", 564 | "quote", 565 | "syn", 566 | ] 567 | 568 | [[package]] 569 | name = "serde_json" 570 | version = "1.0.85" 571 | source = "registry+https://github.com/rust-lang/crates.io-index" 572 | checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" 573 | dependencies = [ 574 | "itoa 1.0.3", 575 | "ryu", 576 | "serde", 577 | ] 578 | 579 | [[package]] 580 | name = "shadow-rs" 581 | version = "0.16.3" 582 | source = "registry+https://github.com/rust-lang/crates.io-index" 583 | checksum = "8c0ea0c68418544f725eba5401a5b965a2263254c92458d04aeae74e9d88ff4e" 584 | dependencies = [ 585 | "const_format", 586 | "git2", 587 | "is_debug", 588 | "time", 589 | "tzdb", 590 | ] 591 | 592 | [[package]] 593 | name = "strsim" 594 | version = "0.10.0" 595 | source = "registry+https://github.com/rust-lang/crates.io-index" 596 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 597 | 598 | [[package]] 599 | name = "syn" 600 | version = "1.0.101" 601 | source = "registry+https://github.com/rust-lang/crates.io-index" 602 | checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2" 603 | dependencies = [ 604 | "proc-macro2", 605 | "quote", 606 | "unicode-ident", 607 | ] 608 | 609 | [[package]] 610 | name = "termcolor" 611 | version = "1.1.3" 612 | source = "registry+https://github.com/rust-lang/crates.io-index" 613 | checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" 614 | dependencies = [ 615 | "winapi-util", 616 | ] 617 | 618 | [[package]] 619 | name = "terminal_size" 620 | version = "0.1.17" 621 | source = "registry+https://github.com/rust-lang/crates.io-index" 622 | checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" 623 | dependencies = [ 624 | "libc", 625 | "winapi", 626 | ] 627 | 628 | [[package]] 629 | name = "textwrap" 630 | version = "0.15.1" 631 | source = "registry+https://github.com/rust-lang/crates.io-index" 632 | checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" 633 | 634 | [[package]] 635 | name = "time" 636 | version = "0.3.14" 637 | source = "registry+https://github.com/rust-lang/crates.io-index" 638 | checksum = "3c3f9a28b618c3a6b9251b6908e9c99e04b9e5c02e6581ccbb67d59c34ef7f9b" 639 | dependencies = [ 640 | "itoa 1.0.3", 641 | "libc", 642 | "num_threads", 643 | ] 644 | 645 | [[package]] 646 | name = "tinyvec" 647 | version = "1.6.0" 648 | source = "registry+https://github.com/rust-lang/crates.io-index" 649 | checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" 650 | dependencies = [ 651 | "tinyvec_macros", 652 | ] 653 | 654 | [[package]] 655 | name = "tinyvec_macros" 656 | version = "0.1.0" 657 | source = "registry+https://github.com/rust-lang/crates.io-index" 658 | checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" 659 | 660 | [[package]] 661 | name = "tz-rs" 662 | version = "0.6.14" 663 | source = "registry+https://github.com/rust-lang/crates.io-index" 664 | checksum = "33851b15c848fad2cf4b105c6bb66eb9512b6f6c44a4b13f57c53c73c707e2b4" 665 | dependencies = [ 666 | "const_fn", 667 | ] 668 | 669 | [[package]] 670 | name = "tzdb" 671 | version = "0.4.6" 672 | source = "registry+https://github.com/rust-lang/crates.io-index" 673 | checksum = "0f73d7058e3fffe1eba5e59ff9a8cbce4fb27805cffe3e9de588074debef31be" 674 | dependencies = [ 675 | "iana-time-zone", 676 | "tz-rs", 677 | "utcnow", 678 | ] 679 | 680 | [[package]] 681 | name = "unicode-bidi" 682 | version = "0.3.8" 683 | source = "registry+https://github.com/rust-lang/crates.io-index" 684 | checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" 685 | 686 | [[package]] 687 | name = "unicode-ident" 688 | version = "1.0.4" 689 | source = "registry+https://github.com/rust-lang/crates.io-index" 690 | checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" 691 | 692 | [[package]] 693 | name = "unicode-normalization" 694 | version = "0.1.22" 695 | source = "registry+https://github.com/rust-lang/crates.io-index" 696 | checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" 697 | dependencies = [ 698 | "tinyvec", 699 | ] 700 | 701 | [[package]] 702 | name = "unicode-width" 703 | version = "0.1.10" 704 | source = "registry+https://github.com/rust-lang/crates.io-index" 705 | checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" 706 | 707 | [[package]] 708 | name = "unicode-xid" 709 | version = "0.2.4" 710 | source = "registry+https://github.com/rust-lang/crates.io-index" 711 | checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" 712 | 713 | [[package]] 714 | name = "url" 715 | version = "2.3.1" 716 | source = "registry+https://github.com/rust-lang/crates.io-index" 717 | checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" 718 | dependencies = [ 719 | "form_urlencoded", 720 | "idna", 721 | "percent-encoding", 722 | ] 723 | 724 | [[package]] 725 | name = "utcnow" 726 | version = "0.2.1" 727 | source = "registry+https://github.com/rust-lang/crates.io-index" 728 | checksum = "80b49db848e09c50db9e7d15aee89030b6ebb8c55e77aff2cef22aeb6844c8b5" 729 | dependencies = [ 730 | "const_fn", 731 | "errno", 732 | "js-sys", 733 | "libc", 734 | "rustix", 735 | "wasi", 736 | "wasm-bindgen", 737 | "winapi", 738 | ] 739 | 740 | [[package]] 741 | name = "vcpkg" 742 | version = "0.2.15" 743 | source = "registry+https://github.com/rust-lang/crates.io-index" 744 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 745 | 746 | [[package]] 747 | name = "version_check" 748 | version = "0.9.4" 749 | source = "registry+https://github.com/rust-lang/crates.io-index" 750 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 751 | 752 | [[package]] 753 | name = "wasi" 754 | version = "0.11.0+wasi-snapshot-preview1" 755 | source = "registry+https://github.com/rust-lang/crates.io-index" 756 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 757 | 758 | [[package]] 759 | name = "wasm-bindgen" 760 | version = "0.2.83" 761 | source = "registry+https://github.com/rust-lang/crates.io-index" 762 | checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" 763 | dependencies = [ 764 | "cfg-if", 765 | "wasm-bindgen-macro", 766 | ] 767 | 768 | [[package]] 769 | name = "wasm-bindgen-backend" 770 | version = "0.2.83" 771 | source = "registry+https://github.com/rust-lang/crates.io-index" 772 | checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" 773 | dependencies = [ 774 | "bumpalo", 775 | "log", 776 | "once_cell", 777 | "proc-macro2", 778 | "quote", 779 | "syn", 780 | "wasm-bindgen-shared", 781 | ] 782 | 783 | [[package]] 784 | name = "wasm-bindgen-macro" 785 | version = "0.2.83" 786 | source = "registry+https://github.com/rust-lang/crates.io-index" 787 | checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" 788 | dependencies = [ 789 | "quote", 790 | "wasm-bindgen-macro-support", 791 | ] 792 | 793 | [[package]] 794 | name = "wasm-bindgen-macro-support" 795 | version = "0.2.83" 796 | source = "registry+https://github.com/rust-lang/crates.io-index" 797 | checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" 798 | dependencies = [ 799 | "proc-macro2", 800 | "quote", 801 | "syn", 802 | "wasm-bindgen-backend", 803 | "wasm-bindgen-shared", 804 | ] 805 | 806 | [[package]] 807 | name = "wasm-bindgen-shared" 808 | version = "0.2.83" 809 | source = "registry+https://github.com/rust-lang/crates.io-index" 810 | checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" 811 | 812 | [[package]] 813 | name = "winapi" 814 | version = "0.3.9" 815 | source = "registry+https://github.com/rust-lang/crates.io-index" 816 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 817 | dependencies = [ 818 | "winapi-i686-pc-windows-gnu", 819 | "winapi-x86_64-pc-windows-gnu", 820 | ] 821 | 822 | [[package]] 823 | name = "winapi-i686-pc-windows-gnu" 824 | version = "0.4.0" 825 | source = "registry+https://github.com/rust-lang/crates.io-index" 826 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 827 | 828 | [[package]] 829 | name = "winapi-util" 830 | version = "0.1.5" 831 | source = "registry+https://github.com/rust-lang/crates.io-index" 832 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 833 | dependencies = [ 834 | "winapi", 835 | ] 836 | 837 | [[package]] 838 | name = "winapi-x86_64-pc-windows-gnu" 839 | version = "0.4.0" 840 | source = "registry+https://github.com/rust-lang/crates.io-index" 841 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 842 | 843 | [[package]] 844 | name = "windows-sys" 845 | version = "0.36.1" 846 | source = "registry+https://github.com/rust-lang/crates.io-index" 847 | checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" 848 | dependencies = [ 849 | "windows_aarch64_msvc", 850 | "windows_i686_gnu", 851 | "windows_i686_msvc", 852 | "windows_x86_64_gnu", 853 | "windows_x86_64_msvc", 854 | ] 855 | 856 | [[package]] 857 | name = "windows_aarch64_msvc" 858 | version = "0.36.1" 859 | source = "registry+https://github.com/rust-lang/crates.io-index" 860 | checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" 861 | 862 | [[package]] 863 | name = "windows_i686_gnu" 864 | version = "0.36.1" 865 | source = "registry+https://github.com/rust-lang/crates.io-index" 866 | checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" 867 | 868 | [[package]] 869 | name = "windows_i686_msvc" 870 | version = "0.36.1" 871 | source = "registry+https://github.com/rust-lang/crates.io-index" 872 | checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" 873 | 874 | [[package]] 875 | name = "windows_x86_64_gnu" 876 | version = "0.36.1" 877 | source = "registry+https://github.com/rust-lang/crates.io-index" 878 | checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" 879 | 880 | [[package]] 881 | name = "windows_x86_64_msvc" 882 | version = "0.36.1" 883 | source = "registry+https://github.com/rust-lang/crates.io-index" 884 | checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" 885 | --------------------------------------------------------------------------------