diff --git a/Cargo.toml b/Cargo.toml index a9ebd1b..31803d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,3 +16,6 @@ bindgen = "0.69" fs_extra = "1.0" pkg-config = "0.3" walkdir = "2.3" + +[features] +patch-twi-inconsistencies = [] diff --git a/README.md b/README.md index e455eec..367af89 100644 --- a/README.md +++ b/README.md @@ -3,15 +3,21 @@ [crates-badge]: https://img.shields.io/crates/v/simavr-ffi.svg [crates-link]: https://crates.io/crates/simavr-ffi -Low-level bindings to [simavr](https://github.com/buserror/simavr). +Low-level bindings to [simavr](https://github.com/buserror/simavr): -Supported platforms: Linux & Mac (pull requests for Windows welcome!). +```toml +[dependencies] +simavr-ffi = "1.0.1" +``` + +Supported platforms: Linux & Mac (pull requests welcome!). ## Requirements Compile-time: - clang, +- git, - libelf, - pkg-config, - zlib. @@ -34,6 +40,12 @@ $ sudo apt install clang libelf-dev pkg-config $ brew install libelf pkg-config ``` +## Feature flags + +All feature flags are disabled by default - if you want, you can activate: + +- `patch-twi-inconsistencies` - applies a fix for https://github.com/buserror/simavr/issues/453 + ## FAQ ### How does it compare to `simavr-sys`? diff --git a/build.rs b/build.rs index 509aadf..353107d 100644 --- a/build.rs +++ b/build.rs @@ -9,40 +9,90 @@ use walkdir::WalkDir; fn main() { println!("cargo:rerun-if-changed=build.rs"); - let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); - - build_simavr(&out_path); - generate_simavr_bindings(&out_path); - link_libelf(); - link_zlib(); - link_libzstd(); + let out = PathBuf::from(env::var("OUT_DIR").unwrap()); + + check(); + patch(); + copy(&out); + revert_patch(); + build(&out); + generate_bindings(&out); + link(); } -fn build_simavr(out: &Path) { - println!("=> Building simavr"); - +fn check() { if !Path::new("vendor") .join("simavr") .join("README.md") .exists() { panic!( - "\ - `vendor/simavr` doesn't seem to contain the expected source code. \ - If you're cloning simavr-ffi by hand, please use `git clone ... \ - --recurse-submodules`." + "`vendor/simavr` doesn't exist - if you're cloning simavr-ffi by \ + hand, please use `git clone ... --recurse-submodules`" ); } +} + +fn patch() { + println!("=> Patching simavr"); + + simavr_git(&["reset", "--hard", "HEAD"]); + + #[cfg(feature = "patch-twi-inconsistencies")] + patch_ex("twi-inconsistencies.patch"); +} + +#[allow(unused)] +fn patch_ex(name: &str) { + let path = Path::new(file!()) + .parent() + .unwrap() + .join("patches") + .join(name) + .canonicalize() + .unwrap(); + + simavr_git(&["apply", &path.display().to_string()]); +} + +fn copy(out: &Path) { + println!("=> Copying simavr"); + + let out_simavr = out.join("simavr"); + + if !out_simavr.exists() { + fs::create_dir(&out_simavr) + .with_context(|| format!("Couldn't create directory: {}", out_simavr.display())) + .unwrap(); + + fs_extra::copy_items(&["vendor/simavr"], &out_simavr, &Default::default()) + .with_context(|| { + format!( + "Couldn't copy simavr's sources to: {}", + out_simavr.display() + ) + }) + .unwrap(); + } +} + +fn revert_patch() { + // Don't leave the tree dirty during development + simavr_git(&["reset", "--hard", "HEAD"]); +} + +fn build(out: &Path) { + println!("=> Building simavr"); #[cfg(target_family = "unix")] - build_simavr_unix(out); + build_unix(out); #[cfg(not(target_family = "unix"))] - panic!("Sorry, I don't know how to build simavr on this architecture"); + panic!("simavr-ffi works only on Unixes right now - pull requests are welcome!"); } #[cfg(target_family = "unix")] -fn build_simavr_unix(out: &Path) { +fn build_unix(out: &Path) { let out_simavr = out.join("simavr"); if !out_simavr.exists() { @@ -76,7 +126,7 @@ fn build_simavr_unix(out: &Path) { println!("cargo:rustc-link-lib=static=simavr"); } -fn generate_simavr_bindings(out: &Path) { +fn generate_bindings(out: &Path) { println!("=> Generating simavr bindings"); let mut builder = bindgen::Builder::default(); @@ -97,7 +147,6 @@ fn generate_simavr_bindings(out: &Path) { .map(|header| header.path().to_string_lossy().to_string()); for header in headers { - println!("-> Found header: {}", header); builder = builder.header(header); } @@ -110,6 +159,12 @@ fn generate_simavr_bindings(out: &Path) { .expect("Couldn't write simavr's bindings"); } +fn link() { + link_libelf(); + link_zlib(); + link_libzstd(); +} + fn link_libelf() { println!("=> Linking libelf"); @@ -133,3 +188,21 @@ fn link_libzstd() { println!("cargo-rustc-link-lib=zstd"); } + +fn simavr_git(args: &[&str]) { + let status = Command::new("git") + .current_dir("vendor/simavr") + .args(args) + .status() + .expect("Couldn't run `git`"); + + if !status.success() { + panic!( + "`git {}` returned a non-zero exit code", + args.iter() + .map(|arg| arg.to_string()) + .collect::>() + .join(" ") + ); + } +} diff --git a/patches/twi-inconsistencies.patch b/patches/twi-inconsistencies.patch new file mode 100644 index 0000000..ee7e9f1 --- /dev/null +++ b/patches/twi-inconsistencies.patch @@ -0,0 +1,63 @@ +diff --git a/simavr/sim/avr_twi.c b/simavr/sim/avr_twi.c +index 521c03c..f96dedd 100644 +--- a/simavr/sim/avr_twi.c ++++ b/simavr/sim/avr_twi.c +@@ -289,11 +289,11 @@ avr_twi_write( + avr_twi_irq_msg(msgv, p->peer_addr, avr->data[p->r_twdr])); + + if (do_read) { // read ? +- _avr_twi_delay_state(p, 9, ++ _avr_twi_delay_state(p, 0, + msgv & TWI_COND_ACK ? + TWI_MRX_DATA_ACK : TWI_MRX_DATA_NACK); + } else { +- _avr_twi_delay_state(p, 9, ++ _avr_twi_delay_state(p, 0, + p->state & TWI_COND_ACK ? + TWI_MTX_DATA_ACK : TWI_MTX_DATA_NACK); + } +@@ -319,7 +319,7 @@ avr_twi_write( + + if (p->peer_addr & 1) { // read ? + p->state |= TWI_COND_READ; // always allow read to start with +- _avr_twi_delay_state(p, 9, ++ _avr_twi_delay_state(p, 0, + p->state & TWI_COND_ACK ? + TWI_MRX_ADR_ACK : TWI_MRX_ADR_NACK); + } else { +@@ -328,7 +328,7 @@ avr_twi_write( + p->state & TWI_COND_ACK ? + TWI_MTX_ADR_ACK : TWI_MTX_ADR_NACK); + }else{ +- _avr_twi_delay_state(p, 9, ++ _avr_twi_delay_state(p, 0, + p->state & TWI_COND_ACK ? + TWI_MTX_DATA_ACK : TWI_MTX_DATA_NACK); + } +@@ -426,7 +426,7 @@ avr_twi_irq_input( + // INVERSE logic here + if (!(msg.u.twi.msg & TWI_COND_WRITE)) + p->peer_addr |= 1; +- _avr_twi_delay_state(p, 9, ++ _avr_twi_delay_state(p, 0, + msg.u.twi.msg & TWI_COND_WRITE ? + TWI_SRX_ADR_ACK : TWI_STX_ADR_ACK ); + } +@@ -439,7 +439,7 @@ avr_twi_irq_input( + } + } + if (msg.u.twi.msg & TWI_COND_STOP) { +- _avr_twi_delay_state(p, 9, ++ _avr_twi_delay_state(p, 0, + msg.u.twi.msg & TWI_COND_WRITE ? + TWI_SRX_ADR_ACK : TWI_STX_ADR_ACK ); + } +@@ -456,7 +456,7 @@ avr_twi_irq_input( + if (p->state & TWI_COND_SLAVE) { + if (msg.u.twi.msg & TWI_COND_WRITE) { + avr->data[p->r_twdr] = msg.u.twi.data; +- _avr_twi_delay_state(p, 9, TWI_SRX_ADR_DATA_ACK ); ++ _avr_twi_delay_state(p, 0, TWI_SRX_ADR_DATA_ACK ); + } + } else { + // receive a data byte from a slave