libgpiod: Add Rust bindings
v4 cover libgpiod: Add Rust bindings
Hello,
Here is another version of rust bindings for libgpiod v2.0, based of the next/libgpiod-2.0.
Pushed here:
https://github.com/vireshk/libgpiod master
V3->V4:
- Rebased on top of new changes, and made changes accordingly.
- Added rust integration tests with gpiosim.
- Found a kernel bug with tests, sent a patch for that to LKML.
V2->V3:
- Remove naming redundancy, users just need to do this now
use libgpiod:{Chip, Direction, LineConfig} now (Bartosz);
- Fix lifetime issues between event-buffer and edge-event modules, the event buffer is released after the last edge-event reference is dropped (Bartosz).
- Allow edge-event to be copied, and freed later (Bartosz).
- Add two separate rust crates, sys and wrapper (Gerard).
- Null-terminate the strings passed to libgpiod (Wedson).
- Drop unnecessary checks to validate string returned from chip:name/label/path.
- Fix SAFETY comments (Wedson).
- Drop unnecessary clone() instances (Bartosz).
V1->V2:
- Added examples (I tested everything except gpiomon.rs, didn't have right hardware/mock device to test).
- Build rust bindings as part of Make, update documentation.
Thanks.
--
Viresh
Viresh Kumar (8):
libgpiod: Add libgpiod-sys rust crate
libgpiod: Add pre generated rust bindings
libgpiod-sys: Add support to generate gpiosim bindings
libgpiod: Add rust wrapper crate
libgpiod: Add rust examples
libgpiod: Derive debug traits for few definitions
libgpiod: Add rust tests
libgpiod: Integrate building of rust bindings with make
.gitignore | 5 + README | 8 +- TODO | 8 - bindings/Makefile.am | 6 + bindings/rust/Cargo.toml | 15 + bindings/rust/Makefile.am | 18 + bindings/rust/examples/gpiodetect.rs | 37 + bindings/rust/examples/gpiofind.rs | 42 + bindings/rust/examples/gpioget.rs | 42 + bindings/rust/examples/gpioinfo.rs | 89 + bindings/rust/examples/gpiomon.rs | 68 + bindings/rust/examples/gpioset.rs | 52 + bindings/rust/libgpiod-sys/Cargo.toml | 16 + bindings/rust/libgpiod-sys/README.md | 10 + bindings/rust/libgpiod-sys/build.rs | 84 + bindings/rust/libgpiod-sys/gpiosim_wrapper.h | 1 + bindings/rust/libgpiod-sys/src/bindings.rs | 1920 ++++++++++++++++++ bindings/rust/libgpiod-sys/src/lib.rs | 20 + bindings/rust/libgpiod-sys/wrapper.h | 2 + bindings/rust/src/chip.rs | 184 ++ bindings/rust/src/chip_info.rs | 70 + bindings/rust/src/edge_event.rs | 105 + bindings/rust/src/event_buffer.rs | 88 + bindings/rust/src/info_event.rs | 68 + bindings/rust/src/lib.rs | 323 +++ bindings/rust/src/line_config.rs | 493 +++++ bindings/rust/src/line_info.rs | 190 ++ bindings/rust/src/line_request.rs | 249 +++ bindings/rust/src/request_config.rs | 122 ++ bindings/rust/tests/chip.rs | 96 + bindings/rust/tests/common/config.rs | 117 ++ bindings/rust/tests/common/mod.rs | 16 + bindings/rust/tests/common/sim.rs | 306 +++ bindings/rust/tests/edge_event.rs | 389 ++++ bindings/rust/tests/info_event.rs | 126 ++ bindings/rust/tests/line_config.rs | 187 ++ bindings/rust/tests/line_info.rs | 90 + bindings/rust/tests/line_request.rs | 234 +++ bindings/rust/tests/request_config.rs | 42 + configure.ac | 16 + 40 files changed, 5943 insertions(+), 11 deletions(-) create mode 100644 bindings/rust/Cargo.toml create mode 100644 bindings/rust/Makefile.am create mode 100644 bindings/rust/examples/gpiodetect.rs create mode 100644 bindings/rust/examples/gpiofind.rs create mode 100644 bindings/rust/examples/gpioget.rs create mode 100644 bindings/rust/examples/gpioinfo.rs create mode 100644 bindings/rust/examples/gpiomon.rs create mode 100644 bindings/rust/examples/gpioset.rs create mode 100644 bindings/rust/libgpiod-sys/Cargo.toml create mode 100644 bindings/rust/libgpiod-sys/README.md create mode 100644 bindings/rust/libgpiod-sys/build.rs create mode 100644 bindings/rust/libgpiod-sys/gpiosim_wrapper.h create mode 100644 bindings/rust/libgpiod-sys/src/bindings.rs create mode 100644 bindings/rust/libgpiod-sys/src/lib.rs create mode 100644 bindings/rust/libgpiod-sys/wrapper.h create mode 100644 bindings/rust/src/chip.rs create mode 100644 bindings/rust/src/chip_info.rs create mode 100644 bindings/rust/src/edge_event.rs create mode 100644 bindings/rust/src/event_buffer.rs create mode 100644 bindings/rust/src/info_event.rs create mode 100644 bindings/rust/src/lib.rs create mode 100644 bindings/rust/src/line_config.rs create mode 100644 bindings/rust/src/line_info.rs create mode 100644 bindings/rust/src/line_request.rs create mode 100644 bindings/rust/src/request_config.rs create mode 100644 bindings/rust/tests/chip.rs create mode 100644 bindings/rust/tests/common/config.rs create mode 100644 bindings/rust/tests/common/mod.rs create mode 100644 bindings/rust/tests/common/sim.rs create mode 100644 bindings/rust/tests/edge_event.rs create mode 100644 bindings/rust/tests/info_event.rs create mode 100644 bindings/rust/tests/line_config.rs create mode 100644 bindings/rust/tests/line_info.rs create mode 100644 bindings/rust/tests/line_request.rs create mode 100644 bindings/rust/tests/request_config.rs
Signature · 1 line
2.31.1.272.g89b43f80a514
PATCH v4 1/8 libgpiod: Add libgpiod-sys rust crate
This adds libgpiod-sys rust crate, which provides FFI (foreign function interface) bindings for libgpiod APIs.
--- .gitignore | 5 ++ bindings/rust/libgpiod-sys/Cargo.toml | 15 ++++++ bindings/rust/libgpiod-sys/build.rs | 69 +++++++++++++++++++++++++++ bindings/rust/libgpiod-sys/src/lib.rs | 20 ++++++++ bindings/rust/libgpiod-sys/wrapper.h | 2 + 5 files changed, 111 insertions(+) create mode 100644 bindings/rust/libgpiod-sys/Cargo.toml create mode 100644 bindings/rust/libgpiod-sys/build.rs create mode 100644 bindings/rust/libgpiod-sys/src/lib.rs create mode 100644 bindings/rust/libgpiod-sys/wrapper.h
.gitignore +5 -0
@@ -33,3 +33,8 @@ stamp-h1 # profiling *.gcda *.gcno + +# Added by cargo + +target +Cargo.lock
bindings/rust/libgpiod-sys/Cargo.toml new file +15 -0
@@ -0,0 +1,15 @@ +[package] +name = "libgpiod-sys" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[features] +generate = [ "bindgen" ] + +[build-dependencies] +bindgen = { version = "0.59.1", optional = true } +cc = "1.0.46"
bindings/rust/libgpiod-sys/build.rs new file +69 -0
69 lines not on this page · raw message
bindings/rust/libgpiod-sys/src/lib.rs new file +20 -0
@@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0 + +#[allow( +clippy::all, +deref_nullptr, +dead_code, +non_camel_case_types, +non_upper_case_globals, +non_snake_case, +improper_ctypes +)] + +mod bindings_raw { +#[cfg(feature = "generate" )] +include! (concat! (env! ("OUT_DIR" ),"/bindings.rs" )); + +#[cfg(not(feature = "generate" ))] +include! ("bindings.rs" ); +} +pub use bindings_raw::*;
bindings/rust/libgpiod-sys/wrapper.h new file +2 -0
@@ -0,0 +1,2 @@ +#include <string.h> +#include "../../../include/gpiod.h"
Signature · 1 line
2.31.1.272.g89b43f80a514
Re: [PATCH V4 1/8] libgpiod: Add libgpiod-sys rust crate
Quoted 4 lines · Viresh Kumar
This adds libgpiod-sys rust crate, which provides FFI (foreign function interface) bindings for libgpiod APIs.
Signed-off-by Viresh Kumar <[email protected]>
Just a quick qualifier before we get started - I'm relatively new to Rust and this the first Rust code I've reviewed, so my opinions may not reflect current idiomatic Rust or may even be complete rubbish.
Quoted 111 lines
--- .gitignore | 5 ++ bindings/rust/libgpiod-sys/Cargo.toml | 15 ++++++ bindings/rust/libgpiod-sys/build.rs | 69 +++++++++++++++++++++++++++ bindings/rust/libgpiod-sys/src/lib.rs | 20 ++++++++ bindings/rust/libgpiod-sys/wrapper.h | 2 + 5 files changed, 111 insertions(+) create mode 100644 bindings/rust/libgpiod-sys/Cargo.toml create mode 100644 bindings/rust/libgpiod-sys/build.rs create mode 100644 bindings/rust/libgpiod-sys/src/lib.rs create mode 100644 bindings/rust/libgpiod-sys/wrapper.h.gitignore +5
-0@@ -33,3 +33,8 @@ stamp-h1 # profiling *.gcda *.gcno + +# Added by cargo + +target +Cargo.lockbindings/rust/libgpiod-sys/Cargo.toml new file +15
-0@@ -0,0 +1,15 @@ +[package] +name = "libgpiod-sys" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[features] +generate = [ "bindgen" ] + +[build-dependencies] +bindgen = { version = "0.59.1", optional = true } +cc = "1.0.46"bindings/rust/libgpiod-sys/build.rs new file +59
-0@@ -0,0 +1,69 @@ +#[cfg(feature = "generate" )] +extern crate bindgen; +#[cfg(feature = "generate" )] +use std::env; +#[cfg(feature = "generate" )] +use std::path::PathBuf ; + +#[cfg(feature = "generate" )] +fn generate_bindings (files: &Vec <&str >) { +// Tell cargo to invalidate the built crate whenever following files change +println! ("cargo:rerun-if-changed=wrapper.h" ); + +for filein files { +println! ("cargo:rerun-if-changed={}" , file); + } + +// The bindgen::Builder is the main entry point +// to bindgen, and lets you build up options for +// the resulting bindings. +let bindings = bindgen::Builder ::default () +// The input header we would like to generate +// bindings for. + .header ("wrapper.h" ) +// Tell cargo to invalidate the built crate whenever any of the +// included header files changed. + .parse_callbacks (Box ::new (bindgen::CargoCallbacks )) +// Finish the builder and generate the bindings. + .generate () +// Unwrap the Result and panic on failure. + .expect ("Unable to generate bindings" ); + +// Write the bindings to the $OUT_DIR/bindings.rs file. +let out_path =PathBuf ::from (env::var ("OUT_DIR" ).unwrap ()); + bindings + .write_to_file (out_path.join ("bindings.rs" )) + .expect ("Couldn't write bindings!" ); +} + +fn build_gpiod (files:Vec <&str >) { +// Tell Cargo that if the given file changes, to rerun this build script. +println! ("cargo:rerun-if-changed=../../../lib/" ); + +// Use the `cc` crate to build a C file and statically link it. + cc::Build ::new () + .files (files) + .define ("_GNU_SOURCE" ,None ) + .define ("GPIOD_VERSION_STR" ,"\"libgpio-sys\"" ) + .include ("../../../include" ) + .compile ("gpiod" ); +} + +fn main () { + let files = vec![ + "../../../lib/chip.c", + "../../../lib/chip-info.c", + "../../../lib/edge-event.c", + "../../../lib/info-event.c", + "../../../lib/internal.c", + "../../../lib/line-config.c",
bindings/rust/libgpiod-sys/build.rs line 69
Quoted 10 lines
bindings/rust/libgpiod-sys/build.rs new file +10
-0+ "../../../lib/line-info.c", + "../../../lib/line-request.c", + "../../../lib/misc.c", + "../../../lib/request-config.c", + ]; + +#[cfg(feature = "generate" )] +generate_bindings (&files); +build_gpiod (files); +}
Shouldn't bindings wrap libgpiod and dynamically link against it rather than building and linking statically?
Quoted 8 lines
bindings/rust/libgpiod-sys/src/lib.rs new file +2
-0@@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0 +
bindings/rust/libgpiod-sys/src/lib.rs line 12
Quoted 10 lines
bindings/rust/libgpiod-sys/src/lib.rs new file +10
-0+#[allow( +clippy::all, +deref_nullptr, +dead_code, +non_camel_case_types, +non_upper_case_globals, +non_snake_case, +improper_ctypes +)] +
Are all these really necessary?
Builds mostly clean for me with just:
+ non_camel_case_types,
+ non_upper_case_globals,
Both non_snake_case and deref_nullptr are only required for tests.
The deref_nullptr masks several warnings like this:
warning: dereferencing a null pointer
--> src/bindings.rs:121:14
|
121 | &(*(::std::ptr::null::<max_align_t>())).__clang_max_align_nonce1 as *const _ as usize
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed
| = note: #[warn(deref_nullptr)] on by default
which is code generated by bindgen, which is a bit of a worry. It is only used for alignment tests, but you'd think they would disable the warning just around that code themselves.
Disabling deref_nullptr globally for all builds is at best poor form. Perhaps only disable it for test builds, i.e.
#[cfg_attr(test, allow(deref_nullptr, non_snake_case))]
Quoted 14 lines
bindings/rust/libgpiod-sys/src/lib.rs new file +8
-0+mod bindings_raw { +#[cfg(feature = "generate" )] +include! (concat! (env! ("OUT_DIR" ),"/bindings.rs" )); + +#[cfg(not(feature = "generate" ))] +include! ("bindings.rs" ); +} +pub use bindings_raw::*;bindings/rust/libgpiod-sys/wrapper.h new file +0
-0
bindings/rust/libgpiod-sys/wrapper.h line 2
Quoted 2 lines
bindings/rust/libgpiod-sys/wrapper.h new file +2
-0@@ -0,0 +1,2 @@ +#include <string.h> +#include "../../../include/gpiod.h"
The string.h is just to provide strlen() for the wrapper crate?? (but also pulls in the other string functions)
The wrapper crate already depends on libc - why not use libc::strlen() there and drop this include here?
And then wrapper.h becomes redundant - call bindgen on gpiod.h directly.
Cheers,
Kent.
Re: [PATCH V4 1/8] libgpiod: Add libgpiod-sys rust crate
Quoted 14 lines · Kent Gibson
Quoted 13 lines · Viresh Kumar
bindings/rust/libgpiod-sys/build.rs new file +13
-0+fn main () { +let files =vec! [ +"../../../lib/chip.c" , +"../../../lib/chip-info.c" , +"../../../lib/edge-event.c" , +"../../../lib/info-event.c" , +"../../../lib/internal.c" , +"../../../lib/line-config.c" , +"../../../lib/line-info.c" , +"../../../lib/line-request.c" , +"../../../lib/misc.c" , +"../../../lib/request-config.c" , + ];
Quoted 8 lines
bindings/rust/libgpiod-sys/build.rs line 69
Quoted 5 lines
bindings/rust/libgpiod-sys/build.rs new file +5
-0+ +#[cfg(feature = "generate" )] +generate_bindings (&files); +build_gpiod (files); +}Shouldn't bindings wrap libgpiod and dynamically link against it rather than building and linking statically?
There are few problems I faced, because of which I had to do it this way.
- I couldn't find a way to do a "Make" for libgpiod from here and then link to the resultant library.
- libgpiod may not be automatically installed in the environment where the end user of these Rust APIs exists. So I had to build it.
- And then the API is changing a lot, maybe down the line once it is stable enough we can change this to something else.
Quoted 12 lines
Quoted 12 lines
bindings/rust/libgpiod-sys/src/lib.rs new file +6
-0@@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0 + +#[allow( + clippy::all, + deref_nullptr, + dead_code,
Quoted 8 lines
bindings/rust/libgpiod-sys/src/lib.rs line 12
Quoted 6 lines
bindings/rust/libgpiod-sys/src/lib.rs new file +6
-0+ non_camel_case_types, + non_upper_case_globals, + non_snake_case, + improper_ctypes +)] +Are all these really necessary?
Actually not, thanks for pointing this out.
Quoted 6 lines
Builds mostly clean for me with just:
+
non_camel_case_types,
+non_upper_case_globals,Both
non_snake_caseandderef_nullptrare only required for tests.
and if you want to run sanity checks with "fmt" or "clippy".
Quoted 8 lines
The
deref_nullptrmasks several warnings like this:warning: dereferencing a null pointer
-->src/bindings.rs:121:14| 121 | &(*(::std::ptr::null::<max_align_t>())).__clang_max_align_nonce1 as *const _ as usize | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed |
Quoted 7 lines
= note:
#[warn(deref_nullptr)]on by defaultwhich is code generated by bindgen, which is a bit of a worry. It is only used for alignment tests, but you'd think they would disable the warning just around that code themselves.
Disabling
deref_nullptrglobally for all builds is at best poor form.
Even with this these will get disabled only for the code present in libgpiod-sys crate, file bindgen.rs (the automatically generated one). This won't cause the warnings to be skipped for the libgpiod rust wrappers in the libgpiod crate.
Quoted 3 lines
Perhaps only disable it for test builds, i.e.
#[cfg_attr(test,
allow(deref_nullptr, non_snake_case))]
I also run following normally:
cargo fmt --all -- --check; cargo clippy --workspace --bins --examples --benches --all-features -- -D warnings
to do sanity checks, etc. And this will also generate warnings, not just tests.
Quoted 13 lines
Quoted 13 lines
bindings/rust/libgpiod-sys/src/lib.rs new file +8
-0+mod bindings_raw { +#[cfg(feature = "generate" )] +include! (concat! (env! ("OUT_DIR" ),"/bindings.rs" )); + +#[cfg(not(feature = "generate" ))] +include! ("bindings.rs" ); +} +pub use bindings_raw::*;bindings/rust/libgpiod-sys/wrapper.h new file +0
-0
Quoted 8 lines
bindings/rust/libgpiod-sys/wrapper.h line 2
Quoted 3 lines
bindings/rust/libgpiod-sys/wrapper.h new file +2
-0@@ -0,0 +1,2 @@ +#include <string.h> +#include "../../../include/gpiod.h" The
string.his just to providestrlen()for the wrapper crate?? (but also pulls in the other string functions)
The wrapper crate already depends on libc - why not uselibc::strlen()there and drop this include here?
Right, done.
Quoted 1 line
And then
wrapper.hbecomes redundant - call bindgen ongpiod.hdirectly.
Rust documentation specifically suggests wrapper.h to be created [1], maybe it it is better to just keep it, even if we have a single entry in there.
Signature · 3 lines
viresh [1] https://rust-lang.github.io/rust-bindgen/tutorial-2.html
Re: [PATCH V4 1/8] libgpiod: Add libgpiod-sys rust crate
Quoted 26 lines · Viresh Kumar
Quoted 14 lines · Kent Gibson
Quoted 13 lines · Viresh Kumar
bindings/rust/libgpiod-sys/build.rs new file +13
-0+fn main () { +let files =vec! [ +"../../../lib/chip.c" , +"../../../lib/chip-info.c" , +"../../../lib/edge-event.c" , +"../../../lib/info-event.c" , +"../../../lib/internal.c" , +"../../../lib/line-config.c" , +"../../../lib/line-info.c" , +"../../../lib/line-request.c" , +"../../../lib/misc.c" , +"../../../lib/request-config.c" , + ];Quoted 8 lines
bindings/rust/libgpiod-sys/build.rs line 69
Quoted 5 lines
bindings/rust/libgpiod-sys/build.rs new file +5
-0+ +#[cfg(feature = "generate" )] +generate_bindings (&files); +build_gpiod (files); +}Shouldn't bindings wrap libgpiod and dynamically link against it rather than building and linking statically?
There are few problems I faced, because of which I had to do it this way.
Quoted 9 lines
- I couldn't find a way to do a "Make" for libgpiod from here and then link to the resultant library.
- libgpiod may not be automatically installed in the environment where the end user of these Rust APIs exists. So I had to build it.
- And then the API is changing a lot, maybe down the line once it is stable enough we can change this to something else.
Sure, it is a problem, but static isn't the solution.
You should be able to get the appropriate paths from autoconf, but I would refer you to Bart on that.
Wrt, "API changing a lot", autoconf make build dependencies should sort that out for you.
Quoted 46 lines
Quoted 12 lines
Quoted 12 lines
bindings/rust/libgpiod-sys/src/lib.rs new file +6
-0@@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0 + +#[allow( + clippy::all, + deref_nullptr, + dead_code,Quoted 8 lines
bindings/rust/libgpiod-sys/src/lib.rs line 12
Quoted 6 lines
bindings/rust/libgpiod-sys/src/lib.rs new file +6
-0+ non_camel_case_types, + non_upper_case_globals, + non_snake_case, + improper_ctypes +)] +Are all these really necessary?
Actually not, thanks for pointing this out.
Quoted 6 lines
Builds mostly clean for me with just:
+
non_camel_case_types,
+non_upper_case_globals,Both
non_snake_caseandderef_nullptrare only required for tests.and if you want to run sanity checks with "fmt" or "clippy".
Quoted 14 lines
The
deref_nullptrmasks several warnings like this:warning: dereferencing a null pointer
-->src/bindings.rs:121:14| 121 | &(*(::std::ptr::null::<max_align_t>())).__clang_max_align_nonce1 as *const _ as usize | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed |= note:
#[warn(deref_nullptr)]on by defaultwhich is code generated by bindgen, which is a bit of a worry. It is only used for alignment tests, but you'd think they would disable the warning just around that code themselves.
Quoted 6 lines
Quoted 1 line
Disabling
deref_nullptrglobally for all builds is at best poor form.Even with this these will get disabled only for the code present in libgpiod-sys crate, file
bindgen.rs(the automatically generated one). This won't cause the warnings to be skipped for the libgpiod rust wrappers in the libgpiod crate.
By "all builds" I meant build/tests/fmt/clippy etc of this module, not others.
My concern being that a subsequent bindgen may introduce a problem into the generated code that the allows would hide. So try to keep them restricted to the problem at hand as much as possible.
Quoted 10 lines
Quoted 3 lines
Perhaps only disable it for test builds, i.e.
#[cfg_attr(test,
allow(deref_nullptr, non_snake_case))]I also run following normally:
cargo fmt --all -- --check; cargo clippy --workspace --bins --examples --benches --all-features -- -D warnings
to do sanity checks, etc. And this will also generate warnings, not just tests.
So
#[cfg_attr(any(test,fmt,clippy), allow(deref_nullptr, non_snake_case))] ?
Quoted 22 lines
Quoted 13 lines
Quoted 13 lines
bindings/rust/libgpiod-sys/src/lib.rs new file +8
-0+mod bindings_raw { +#[cfg(feature = "generate" )] +include! (concat! (env! ("OUT_DIR" ),"/bindings.rs" )); + +#[cfg(not(feature = "generate" ))] +include! ("bindings.rs" ); +} +pub use bindings_raw::*;bindings/rust/libgpiod-sys/wrapper.h new file +0
-0Quoted 8 lines
bindings/rust/libgpiod-sys/wrapper.h line 2
Quoted 3 lines
bindings/rust/libgpiod-sys/wrapper.h new file +2
-0@@ -0,0 +1,2 @@ +#include <string.h> +#include "../../../include/gpiod.h" The
string.his just to providestrlen()for the wrapper crate?? (but also pulls in the other string functions)
The wrapper crate already depends on libc - why not uselibc::strlen()there and drop this include here?
Quoted 7 lines
Right, done.
Quoted 1 line
And then
wrapper.hbecomes redundant - call bindgen ongpiod.hdirectly.Rust documentation specifically suggests
wrapper.hto be created [1], maybe it it is better to just keep it, even if we have a single entry in there.
Specifically the tutoral says:
"The wrapper.h file will include all the various headers containing declarations of structs and functions we would like bindings for."
If you do need to bundle several headers then fair enough, but I don't see any benefit in this case - gpiod.h contains all that.
The tutorial is probably written that way so it is easy for them to refer to the general "wrapper.h", but there is nothing in bindgen that requires it.
Cheers,
Kent.
Re: [PATCH V4 1/8] libgpiod: Add libgpiod-sys rust crate
Quoted 3 lines · Kent Gibson
Sure, it is a problem, but static isn't the solution.
You should be able to get the appropriate paths from autoconf, but I would refer you to Bart on that.
Sure, if someone can suggest a better way of doing this, I am up for it. I just don't know how to do it as of now.
Quoted 2 lines
By "all builds" I meant build/tests/fmt/clippy etc of this module, not others.
Ahh.
Quoted 3 lines
My concern being that a subsequent bindgen may introduce a problem into the generated code that the allows would hide. So try to keep them restricted to the problem at hand as much as possible.
I agree.
Quoted 2 lines
#[cfg_attr(any(test,fmt,clippy),
allow(deref_nullptr, non_snake_case))] ?
Finally just this was enough for fmt/clippy too :)
#[cfg_attr(test, allow(deref_nullptr, non_snake_case))]
Quoted 10 lines
Specifically the tutoral says:
"Thewrapper.hfile will include all the various headers containing declarations of structs and functions we would like bindings for."If you do need to bundle several headers then fair enough, but I don't see any benefit in this case -
gpiod.hcontains all that.The tutorial is probably written that way so it is easy for them to refer to the general "
wrapper.h", but there is nothing in bindgen that requires it.
Sure nothing will break if the file isn't there. Okay removed it now:
bindings/rust/libgpiod-sys/build.rs +2 -2
@@ -8,7 +8,7 @@ use std::path::PathBuf;#[cfg(feature = "generate" )] fn generate_bindings (files: &Vec <&str >) {// Tell cargo to invalidate the built crate whenever following files change -println! ("cargo:rerun-if-changed=wrapper.h" ); +println! ("cargo:rerun-if-changed=../../../include/gpiod.h" );for filein files {println! ("cargo:rerun-if-changed={}" , file);
@@ -24,7 +24,7 @@ fn generate_bindings(files: &Vec<&str>) {let mut builder = bindgen::Builder ::default ()// The input header we would like to generate // bindings for. - .header ("wrapper.h" ); + .header ("../../../include/gpiod.h" );if cfg! (feature ="gpiosim" ) { builder = builder.header ("gpiosim_wrapper.h" );
bindings/rust/libgpiod-sys/wrapper.h deleted +0 -1
@@ -1 +0,0 @@ -#include "../../../include/gpiod.h"
Signature · 1 line
viresh
Re: [PATCH V4 1/8] libgpiod: Add libgpiod-sys rust crate
In reply to Kent GibsonQuoted 33 lines · Kent Gibson
Quoted 32 lines · Viresh Kumar
Quoted 14 lines · Kent Gibson
Quoted 13 lines · Viresh Kumar
bindings/rust/libgpiod-sys/build.rs new file +13
-0+fn main () { +let files =vec! [ +"../../../lib/chip.c" , +"../../../lib/chip-info.c" , +"../../../lib/edge-event.c" , +"../../../lib/info-event.c" , +"../../../lib/internal.c" , +"../../../lib/line-config.c" , +"../../../lib/line-info.c" , +"../../../lib/line-request.c" , +"../../../lib/misc.c" , +"../../../lib/request-config.c" , + ];Quoted 8 lines
bindings/rust/libgpiod-sys/build.rs line 69
Quoted 5 lines
bindings/rust/libgpiod-sys/build.rs new file +5
-0+ +#[cfg(feature = "generate" )] +generate_bindings (&files); +build_gpiod (files); +}Shouldn't bindings wrap libgpiod and dynamically link against it rather than building and linking statically?
There are few problems I faced, because of which I had to do it this way.
- I couldn't find a way to do a "Make" for libgpiod from here and then link to the resultant library.
- libgpiod may not be automatically installed in the environment where the end user of these Rust APIs exists. So I had to build it.
Quoted 7 lines
Quoted 3 lines
- And then the API is changing a lot, maybe down the line once it is stable enough we can change this to something else.
Sure, it is a problem, but static isn't the solution.
You should be able to get the appropriate paths from autoconf, but I would refer you to Bart on that.
I am still looking for some help on how to link this dynamically.
FWIW, the problem is that the user crates, like vhost-device, will mention libgpiod as a dependency crate and likely won't have libgpiod installed in environment. So build.rs here needs to do some magic so the definitions are all available to the users.
Signature · 1 line
viresh
Re: [PATCH V4 1/8] libgpiod: Add libgpiod-sys rust crate
Quoted 42 lines · Viresh Kumar
Quoted 33 lines · Kent Gibson
Quoted 32 lines · Viresh Kumar
Quoted 14 lines · Kent Gibson
Quoted 13 lines · Viresh Kumar
bindings/rust/libgpiod-sys/build.rs new file +13
-0+fn main () { +let files =vec! [ +"../../../lib/chip.c" , +"../../../lib/chip-info.c" , +"../../../lib/edge-event.c" , +"../../../lib/info-event.c" , +"../../../lib/internal.c" , +"../../../lib/line-config.c" , +"../../../lib/line-info.c" , +"../../../lib/line-request.c" , +"../../../lib/misc.c" , +"../../../lib/request-config.c" , + ];Quoted 8 lines
bindings/rust/libgpiod-sys/build.rs line 69
Quoted 5 lines
bindings/rust/libgpiod-sys/build.rs new file +5
-0+ +#[cfg(feature = "generate" )] +generate_bindings (&files); +build_gpiod (files); +}Shouldn't bindings wrap libgpiod and dynamically link against it rather than building and linking statically?
There are few problems I faced, because of which I had to do it this way.
- I couldn't find a way to do a "Make" for libgpiod from here and then link to the resultant library.
- libgpiod may not be automatically installed in the environment where the end user of these Rust APIs exists. So I had to build it.
Quoted 7 lines
Quoted 3 lines
- And then the API is changing a lot, maybe down the line once it is stable enough we can change this to something else.
Sure, it is a problem, but static isn't the solution.
You should be able to get the appropriate paths from autoconf, but I would refer you to Bart on that.
Quoted 7 lines
I am still looking for some help on how to link this dynamically.
FWIW, the problem is that the user crates, like vhost-device, will mention libgpiod as a dependency crate and likely won't have libgpiod installed in environment. So
build.rshere needs to do some magic so the definitions are all available to the users.
The Rust bindings themselves should be building against the local build tree, so well known relative paths.
For users, require they have libgpiod installed and use pkg_config to locate it?
Is that what you mean?
Else, how do other Rust crates wrapping dynamic C libraries do it?
Cheers,
Kent.
Re: [PATCH V4 1/8] libgpiod: Add libgpiod-sys rust crate
Quoted 2 lines · Kent Gibson
The Rust bindings themselves should be building against the local build tree, so well known relative paths.
Right, when we build everything with "make" we better use the already built library. I agree.
Quoted 4 lines
For users, require they have libgpiod installed and use
pkg_configto locate it?Is that what you mean?
Since we need the latest APIs, we can't trust the packages provided by distributions for now. i.e. "apt-get install libgpiod-dev" won't install the latest stuff we need.
The only other option to get it working in environments like rust-vmm-containers (which tests the vhost-device crate currently) is to build / install libgpiod first. As I have understood, people don't really like it there (Maintainers of rust-vmm-containers) as this will have further dependencies and require more tools.
I even tried to generate the libgpiod-sys bindings on the fly first, but it required more tooling and there were issues with Musl build specifically. They suggested to use prebuild bindings as a solution, which I have now.
What about we have two separate features:
- "default" one will be used with "make" and will use prebuild library.
- "generate" one will be used by user crates and we will build the files there like it is done now.
Quoted 1 line
Else, how do other Rust crates wrapping dynamic C libraries do it?
I think for standard libraries that are stable, the -sys crates just contain the pre-built bindings and wrappers and expect the library to be already installed.
Signature · 1 line
viresh
Re: [PATCH V4 1/8] libgpiod: Add libgpiod-sys rust crate
Quoted 7 lines · Viresh Kumar
Quoted 2 lines · Kent Gibson
The Rust bindings themselves should be building against the local build tree, so well known relative paths.
Right, when we build everything with "make" we better use the already built library. I agree.
Quoted 9 lines
Quoted 4 lines
For users, require they have libgpiod installed and use
pkg_configto locate it?Is that what you mean?
Since we need the latest APIs, we can't trust the packages provided by distributions for now. i.e. "apt-get install libgpiod-dev" won't install the latest stuff we need.
For user builds with the Rust bindings you always require an appropriate version of libgpiod to be already installed, or it is the users problem to arrange that.
For the time being that means it needs to be built and installed from source. I don't see an alternative to that, or a problem with it. In the longer term it will be provided by the packaging system for the platform.
I may well be missing something, but I don't see the problem, at least nothing that requires addressing.
Cheers,
Kent.
Quoted 26 lines
The only other option to get it working in environments like rust-vmm-containers (which tests the vhost-device crate currently) is to build / install libgpiod first. As I have understood, people don't really like it there (Maintainers of rust-vmm-containers) as this will have further dependencies and require more tools.
I even tried to generate the libgpiod-sys bindings on the fly first, but it required more tooling and there were issues with Musl build specifically. They suggested to use prebuild bindings as a solution, which I have now.
What about we have two separate features:
- "default" one will be used with "make" and will use prebuild library.
- "generate" one will be used by user crates and we will build the files there like it is done now.
Quoted 1 line
Else, how do other Rust crates wrapping dynamic C libraries do it?
I think for standard libraries that are stable, the -sys crates just contain the pre-built bindings and wrappers and expect the library to be already installed.
Signature · 1 line
viresh
PATCH v4 2/8 libgpiod: Add pre generated rust bindings
In reply to Viresh KumarThis adds a copy of pre generated bindings and adds the suggested way of updating those in README.
--- bindings/rust/libgpiod-sys/README.md | 10 + bindings/rust/libgpiod-sys/src/bindings.rs | 1920 ++++++++++++++++++++ 2 files changed, 1930 insertions(+) create mode 100644 bindings/rust/libgpiod-sys/README.md create mode 100644 bindings/rust/libgpiod-sys/src/bindings.rs
bindings/rust/libgpiod-sys/README.md new file +10 -0
@@ -0,0 +1,10 @@ +# Generated libgpiod-sys Rust FFI bindings +Automatically generated Rust FFI bindings via + [bindgen](https://github.com/rust-lang/rust-bindgen). + +## Updating bindings +1. Clone the source from + <https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/> +2. run `cd libgpiod/bindings/rust/libgpiod-sys/` +2. run `cargo build --features generate` +3. Commit changes in `src/bindings.rs`
bindings/rust/libgpiod-sys/src/bindings.rs new file +1920 -0
1,920 lines not on this page · raw message
Signature · 1 line
2.31.1.272.g89b43f80a514
Re: [PATCH V4 2/8] libgpiod: Add pre generated rust bindings
Quoted 17 lines · Viresh Kumar
This adds a copy of pre generated bindings and adds the suggested way of updating those in README.
Signed-off-by Viresh Kumar <[email protected]>--- bindings/rust/libgpiod-sys/README.md | 10 + bindings/rust/libgpiod-sys/src/bindings.rs | 1920 ++++++++++++++++++++ 2 files changed, 1930 insertions(+) create mode 100644 bindings/rust/libgpiod-sys/README.md create mode 100644 bindings/rust/libgpiod-sys/src/bindings.rsbindings/rust/libgpiod-sys/README.md new file +0
-0
bindings/rust/libgpiod-sys/README.md line 10
Quoted 10 lines
bindings/rust/libgpiod-sys/README.md new file +10
-0@@ -0,0 +1,10 @@ +# Generated libgpiod-sys Rust FFI bindings +Automatically generated Rust FFI bindings via + [bindgen](https://github.com/rust-lang/rust-bindgen). + +## Updating bindings +1. Clone the source from + <https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/> +2. run `cd libgpiod/bindings/rust/libgpiod-sys/` +2. run `cargo build --features generate` +3. Commit changes in `src/bindings.rs`
Those instructions do not force the regeneration of the bindings. I assume the rust build system is being clever and determining nothing has changed and short circuiting, but I would like to regnerate them to be sure. How do I do that?
How is the --features approach better than just bindgen -o src/bindings.rs ../../../include/gpiod.h ?
Why not build this all the time rather than hiding it behind a feature?
bindings/rust/libgpiod-sys/src/bindings.rs line 2
Quoted 8 lines
bindings/rust/libgpiod-sys/src/bindings.rs new file +2
-0@@ -0,0 +1,1920 @@ +/* automatically generated by rust-bindgen 0.59.2 */ +
Yet the Cargo.toml specifies 0.59.1.
So you didn't generate this with the instructions above then?
Having said that, I can confirm that
bindgen -o src/bindings.rs wrapper.h
with the appropriate bindgen, regenerates the pre-generated file.
Cheers,
Kent.
Re: [PATCH V4 2/8] libgpiod: Add pre generated rust bindings
Quoted 9 lines · Kent Gibson
bindings/rust/libgpiod-sys/README.md line 10
Quoted 6 lines · Viresh Kumar
bindings/rust/libgpiod-sys/README.md new file +6
-0+## Updating bindings +1. Clone the source from + <https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/> +2. run `cd libgpiod/bindings/rust/libgpiod-sys/` +2. run `cargo build --features generate` +3. Commit changes in `src/bindings.rs`Those instructions do not force the regeneration of the bindings.
It does, just that the new file that got generated lands somewhere like this:
target/debug/build/libgpiod-sys-769f98853e1c0550/out/bindings.rs
and the end user crate will use this instead of the one in src/.
Quoted 3 lines
I assume the rust build system is being clever and determining nothing has changed and short circuiting, but I would like to regnerate them to be sure. How do I do that?
If you use the "generate" feature, you will end up building it again.
Quoted 3 lines
How is the --features approach better than just bindgen -o
src/bindings.rs../../../include/gpiod.h?
This lets the user crates control it from their Cargo.toml file, libgpiod doesn't do it right now but it can like:
libgpiod-sys = { path = "libgpiod-sys", features = ["generate"] }
Quoted 1 line
Why not build this all the time rather than hiding it behind a feature?
That's what I did initially but there are environments, like rust-vmm containers [1], where all tools aren't available to run a bindgen for each architecture/platform type. I ran into a lot of issues there and the maintainers suggested to do this instead as the bindings won't change a lot later on.
FWIW, in my use case vhost-device crate [2] is using libgpiod, which uses libgpiod-sys and the vhost-device updates get tested with [1] automatically by bots.
Quoted 11 lines
bindings/rust/libgpiod-sys/src/bindings.rs line 2
Quoted 8 lines
bindings/rust/libgpiod-sys/src/bindings.rs new file +2
-0@@ -0,0 +1,1920 @@ +/* automatically generated by rust-bindgen 0.59.2 */ +Yet the Cargo.toml specifies 0.59.1.
So you didn't generate this with the instructions above then?
I did.
The dependency like: bindgen = "0.59.1", doesn't force a specific version but actually a range. An update is allowed if the new version number does not modify the left-most non-zero digit in the major, minor, patch grouping [3].
Quoted 5 lines
Having said that, I can confirm that
bindgen -o
src/bindings.rswrapper.hwith the appropriate bindgen, regenerates the pre-generated file.
Signature · 5 lines
viresh [1] https://github.com/rust-vmm/rust-vmm-container [2] https://github.com/rust-vmm/vhost-device/tree/main/gpio [3] https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html
Re: [PATCH V4 2/8] libgpiod: Add pre generated rust bindings
Quoted 11 lines · Viresh Kumar
Quoted 9 lines · Kent Gibson
bindings/rust/libgpiod-sys/README.md line 10
Quoted 6 lines · Viresh Kumar
bindings/rust/libgpiod-sys/README.md new file +6
-0+## Updating bindings +1. Clone the source from + <https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/> +2. run `cd libgpiod/bindings/rust/libgpiod-sys/` +2. run `cargo build --features generate` +3. Commit changes in `src/bindings.rs`Those instructions do not force the regeneration of the bindings.
Quoted 6 lines
It does, just that the new file that got generated lands somewhere like this:
target/debug/build/libgpiod-sys-769f98853e1c0550/out/bindings.rsand the end user crate will use this instead of the one in src/.
But the instructions don't mention any of that, and the implication is that src/bindings.rs will be regenerated.
Cheers,
Kent.
Re: [PATCH V4 2/8] libgpiod: Add pre generated rust bindings
Quoted 14 lines · Kent Gibson
Quoted 13 lines · Viresh Kumar
Quoted 9 lines · Kent Gibson
Review
Quoted 6 lines · Viresh Kumar
(quoted diff) +6
-0+## Updating bindings +1. Clone the source from + <https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/> +2. run `cd libgpiod/bindings/rust/libgpiod-sys/` +2. run `cargo build --features generate` +3. Commit changes in `src/bindings.rs`Those instructions do not force the regeneration of the bindings.
It does, just that the new file that got generated lands somewhere like this:
Quoted 7 lines
Quoted 4 lines
target/debug/build/libgpiod-sys-769f98853e1c0550/out/bindings.rsand the end user crate will use this instead of the one in src/.
But the instructions don't mention any of that, and the implication is that
src/bindings.rswill be regenerated.
Something like this is okay ?
bindings/rust/libgpiod-sys/README.md +2 -1
@@ -7,4 +7,5 @@ Automatically generated Rust FFI bindings via <https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/> 2. run `cd libgpiod/bindings/rust/libgpiod-sys/` 2. run `cargo build --features generate` -3. Commit changes in `src/bindings.rs` +3. Copy the bindings 'cp target/debug/build/libgpiod-sys-###/out/bindings.rs src/bindings.rs' +4. Commit changes in `src/bindings.rs`
Signature · 1 line
viresh
Re: [PATCH V4 2/8] libgpiod: Add pre generated rust bindings
Quoted 31 lines · Viresh Kumar
Quoted 14 lines · Kent Gibson
Quoted 13 lines · Viresh Kumar
Quoted 9 lines · Kent Gibson
Review
Quoted 6 lines · Viresh Kumar
(quoted diff) +6
-0+## Updating bindings +1. Clone the source from + <https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/> +2. run `cd libgpiod/bindings/rust/libgpiod-sys/` +2. run `cargo build --features generate` +3. Commit changes in `src/bindings.rs`Those instructions do not force the regeneration of the bindings.
It does, just that the new file that got generated lands somewhere like this:
Quoted 7 lines
Quoted 4 lines
target/debug/build/libgpiod-sys-769f98853e1c0550/out/bindings.rsand the end user crate will use this instead of the one in src/.
But the instructions don't mention any of that, and the implication is that
src/bindings.rswill be regenerated.Something like this is okay ?
bindings/rust/libgpiod-sys/README.md +0
-0
bindings/rust/libgpiod-sys/README.md Automatically generated Rust FFI bindings via · line 11
Quoted 6 lines
bindings/rust/libgpiod-sys/README.md +2
-1@@ -7,4 +7,5 @@ Automatically generated Rust FFI bindings via <https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/> 2. run `cd libgpiod/bindings/rust/libgpiod-sys/` 2. run `cargo build --features generate` -3. Commit changes in `src/bindings.rs` +3. Copy the bindings 'cp target/debug/build/libgpiod-sys-###/out/bindings.rs src/bindings.rs' +4. Commit changes in `src/bindings.rs`
Definitely clearer.
Still not sure how all this is easier than just calling bindgen directly, but ok. I guess at least it specifies the appropriate-ish bindgen version.
Cheers,
Kent.
PATCH v4 3/8 libgpiod-sys: Add support to generate gpiosim bindings
In reply to Viresh KumarAdd support to generate gpiosim bindings.
--- bindings/rust/libgpiod-sys/Cargo.toml | 1 + bindings/rust/libgpiod-sys/build.rs | 19 +++++++++++++++++-- bindings/rust/libgpiod-sys/gpiosim_wrapper.h | 1 + 3 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 bindings/rust/libgpiod-sys/gpiosim_wrapper.h
bindings/rust/libgpiod-sys/Cargo.toml +1 -0
@@ -9,6 +9,7 @@ edition = "2018" [features] generate = [ "bindgen" ] +gpiosim = [ "generate", "bindgen" ] [build-dependencies] bindgen = { version = "0.59.1", optional = true }
bindings/rust/libgpiod-sys/build.rs +17 -2
@@ -14,13 +14,25 @@ fn generate_bindings(files: &Vec<&str>) {println! ("cargo:rerun-if-changed={}" , file); } +if cfg! (feature ="gpiosim" ) { +println! ("cargo:rerun-if-changed=gpiosim_wrapper.h" ); + } +// The bindgen::Builder is the main entry point // to bindgen, and lets you build up options for // the resulting bindings. -let bindings = bindgen::Builder ::default () +let mut builder = bindgen::Builder ::default ()// The input header we would like to generate // bindings for. - .header ("wrapper.h" ) + .header ("wrapper.h" ); + +if cfg! (feature ="gpiosim" ) { + builder = builder.header ("gpiosim_wrapper.h" ); +println! ("cargo:rustc-link-lib=kmod" ); +println! ("cargo:rustc-link-lib=mount" ); + } + +let bindings = builder// Tell cargo to invalidate the built crate whenever any of the // included header files changed. .parse_callbacks (Box ::new (bindgen::CargoCallbacks ))
@@ -46,6 +58,7 @@ fn build_gpiod(files: Vec<&str>) { .define("_GNU_SOURCE", None) .define ("GPIOD_VERSION_STR" ,"\"libgpio-sys\"" ) .include ("../../../include" ) + .include ("/usr/include/libmount" ) .compile ("gpiod" ); }
@@ -61,6 +74,8 @@ fn main() { "../../../lib/line-request.c", "../../../lib/misc.c", "../../../lib/request-config.c", +#[cfg(feature = "gpiosim" )] + "../../../tests/gpiosim/gpiosim.c", ];#[cfg(feature = "generate" )]
bindings/rust/libgpiod-sys/gpiosim_wrapper.h new file +1 -0
@@ -0,0 +1 @@ +#include "../../../tests/gpiosim/gpiosim.h"
Signature · 1 line
2.31.1.272.g89b43f80a514
Re: [PATCH V4 3/8] libgpiod-sys: Add support to generate gpiosim bindings
Quoted 15 lines · Viresh Kumar
Add support to generate gpiosim bindings.
Signed-off-by Viresh Kumar <[email protected]>--- bindings/rust/libgpiod-sys/Cargo.toml | 1 + bindings/rust/libgpiod-sys/build.rs | 19 +++++++++++++++++-- bindings/rust/libgpiod-sys/gpiosim_wrapper.h | 1 + 3 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 bindings/rust/libgpiod-sys/gpiosim_wrapper.hbindings/rust/libgpiod-sys/Cargo.toml +0
-0
bindings/rust/libgpiod-sys/Cargo.toml edition = "2018" · line 13
Quoted 5 lines
bindings/rust/libgpiod-sys/Cargo.toml +1
-0@@ -9,6 +9,7 @@ edition = "2018" [features] generate = [ "bindgen" ] +gpiosim = [ "generate", "bindgen" ]
Should gpiosim be a feature or a separate library/crate? I would expect it to be a separate crate and for libgpiod to have a dev-dependency on it (it is only required for tests, right?).
Quoted 58 lines
bindings/rust/libgpiod-sys/Cargo.toml +0
-0[build-dependencies] bindgen = { version = "0.59.1", optional = true }bindings/rust/libgpiod-sys/build.rs +17
-2@@ -14,13 +14,25 @@ fn generate_bindings(files: &Vec<&str>) {println! ("cargo:rerun-if-changed={}" , file); } +if cfg! (feature ="gpiosim" ) { +println! ("cargo:rerun-if-changed=gpiosim_wrapper.h" ); + } +// The bindgen::Builder is the main entry point // to bindgen, and lets you build up options for // the resulting bindings. -let bindings = bindgen::Builder ::default () +let mut builder = bindgen::Builder ::default ()// The input header we would like to generate // bindings for. - .header ("wrapper.h" ) + .header ("wrapper.h" ); + +if cfg! (feature ="gpiosim" ) { + builder = builder.header ("gpiosim_wrapper.h" ); +println! ("cargo:rustc-link-lib=kmod" ); +println! ("cargo:rustc-link-lib=mount" ); + } + +let bindings = builder// Tell cargo to invalidate the built crate whenever any of the // included header files changed. .parse_callbacks (Box ::new (bindgen::CargoCallbacks ))@@ -46,6 +58,7 @@ fn build_gpiod(files: Vec<&str>) { .define("_GNU_SOURCE", None) .define ("GPIOD_VERSION_STR" ,"\"libgpio-sys\"" ) .include ("../../../include" ) + .include ("/usr/include/libmount" ) .compile ("gpiod" ); }@@ -61,6 +74,8 @@ fn main() { "../../../lib/line-request.c", "../../../lib/misc.c", "../../../lib/request-config.c", +#[cfg(feature = "gpiosim" )] + "../../../tests/gpiosim/gpiosim.c", ];#[cfg(feature = "generate" )] bindings/rust/libgpiod-sys/gpiosim_wrapper.h new file +0
-0
bindings/rust/libgpiod-sys/gpiosim_wrapper.h line 1
Quoted 1 line
bindings/rust/libgpiod-sys/gpiosim_wrapper.h new file +1
-0@@ -0,0 +1 @@ +#include "../../../tests/gpiosim/gpiosim.h"
Why bother with this wrapper - just bindgen that header directly?
Cheers,
Kent.
Re: [PATCH V4 3/8] libgpiod-sys: Add support to generate gpiosim bindings
Quoted 17 lines · Kent Gibson
Quoted 16 lines · Viresh Kumar
Add support to generate gpiosim bindings.
Signed-off-by Viresh Kumar <[email protected]>--- bindings/rust/libgpiod-sys/Cargo.toml | 1 + bindings/rust/libgpiod-sys/build.rs | 19 +++++++++++++++++-- bindings/rust/libgpiod-sys/gpiosim_wrapper.h | 1 + 3 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 bindings/rust/libgpiod-sys/gpiosim_wrapper.hbindings/rust/libgpiod-sys/Cargo.toml +0
-0@@ -9,6 +9,7 @@ edition = "2018"
Quoted 6 lines
bindings/rust/libgpiod-sys/Cargo.toml edition = "2018" · line 13
Quoted 4 lines
bindings/rust/libgpiod-sys/Cargo.toml +1
-0[features] generate = [ "bindgen" ] +gpiosim = [ "generate", "bindgen" ]Should gpiosim be a feature or a separate library/crate?
It can be. I don't have any objections to that. It will add a bit more code, i.e. a libgpiosim-sys crate in bindings/rust/ directory, but that's fine I think.
Quoted 2 lines
I would expect it to be a separate crate and for libgpiod to have a dev-dependency on it (it is only required for tests, right?).
Yes.
Quoted 10 lines
bindings/rust/libgpiod-sys/gpiosim_wrapper.h line 1
Quoted 8 lines
bindings/rust/libgpiod-sys/gpiosim_wrapper.h new file +1
-0@@ -0,0 +1 @@ +#include "../../../tests/gpiosim/gpiosim.h" Why bother with this wrapper - just bindgen that header directly?
Whatever we decide for wrapper.h, will be done here as well.
Signature · 1 line
viresh
PATCH v4 4/8 libgpiod: Add rust wrapper crate
In reply to Viresh KumarAdd rust wrapper crate, around the libpiod-sys crate added earlier, to provide a convenient interface for the users.
--- bindings/rust/Cargo.toml | 12 + bindings/rust/src/chip.rs | 179 ++++++++++ bindings/rust/src/chip_info.rs | 69 ++++ bindings/rust/src/edge_event.rs | 105 ++++++ bindings/rust/src/event_buffer.rs | 88 +++++ bindings/rust/src/info_event.rs | 68 ++++ bindings/rust/src/lib.rs | 317 ++++++++++++++++++ bindings/rust/src/line_config.rs | 493 ++++++++++++++++++++++++++++ bindings/rust/src/line_info.rs | 189 +++++++++++ bindings/rust/src/line_request.rs | 248 ++++++++++++++ bindings/rust/src/request_config.rs | 122 +++++++ 11 files changed, 1890 insertions(+) create mode 100644 bindings/rust/Cargo.toml create mode 100644 bindings/rust/src/chip.rs create mode 100644 bindings/rust/src/chip_info.rs create mode 100644 bindings/rust/src/edge_event.rs create mode 100644 bindings/rust/src/event_buffer.rs create mode 100644 bindings/rust/src/info_event.rs create mode 100644 bindings/rust/src/lib.rs create mode 100644 bindings/rust/src/line_config.rs create mode 100644 bindings/rust/src/line_info.rs create mode 100644 bindings/rust/src/line_request.rs create mode 100644 bindings/rust/src/request_config.rs
bindings/rust/Cargo.toml new file +12 -0
@@ -0,0 +1,12 @@ +[package] +name = "libgpiod" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +libc = ">=0.2.39" +libgpiod-sys = { path = "libgpiod-sys" } +thiserror = "1.0" +vmm-sys-util = "=0.9.0"
bindings/rust/src/chip.rs new file +179 -0
179 lines not on this page · raw message
bindings/rust/src/chip_info.rs new file +69 -0
69 lines not on this page · raw message
bindings/rust/src/edge_event.rs new file +105 -0
105 lines not on this page · raw message
bindings/rust/src/event_buffer.rs new file +88 -0
88 lines not on this page · raw message
bindings/rust/src/info_event.rs new file +68 -0
68 lines not on this page · raw message
bindings/rust/src/lib.rs new file +317 -0
317 lines not on this page · raw message
bindings/rust/src/line_config.rs new file +493 -0
493 lines not on this page · raw message
bindings/rust/src/line_info.rs new file +189 -0
189 lines not on this page · raw message
bindings/rust/src/line_request.rs new file +248 -0
248 lines not on this page · raw message
bindings/rust/src/request_config.rs new file +122 -0
122 lines not on this page · raw message
Signature · 1 line
2.31.1.272.g89b43f80a514
Re: [PATCH V4 4/8] libgpiod: Add rust wrapper crate
Quoted 35 lines · Viresh Kumar
Add rust wrapper crate, around the libpiod-sys crate added earlier, to provide a convenient interface for the users.
Signed-off-by Viresh Kumar <[email protected]>--- bindings/rust/Cargo.toml | 12 + bindings/rust/src/chip.rs | 179 ++++++++++ bindings/rust/src/chip_info.rs | 69 ++++ bindings/rust/src/edge_event.rs | 105 ++++++ bindings/rust/src/event_buffer.rs | 88 +++++ bindings/rust/src/info_event.rs | 68 ++++ bindings/rust/src/lib.rs | 317 ++++++++++++++++++ bindings/rust/src/line_config.rs | 493 ++++++++++++++++++++++++++++ bindings/rust/src/line_info.rs | 189 +++++++++++ bindings/rust/src/line_request.rs | 248 ++++++++++++++ bindings/rust/src/request_config.rs | 122 +++++++ 11 files changed, 1890 insertions(+) create mode 100644 bindings/rust/Cargo.toml create mode 100644 bindings/rust/src/chip.rs create mode 100644 bindings/rust/src/chip_info.rs create mode 100644 bindings/rust/src/edge_event.rs create mode 100644 bindings/rust/src/event_buffer.rs create mode 100644 bindings/rust/src/info_event.rs create mode 100644 bindings/rust/src/lib.rs create mode 100644 bindings/rust/src/line_config.rs create mode 100644 bindings/rust/src/line_info.rs create mode 100644 bindings/rust/src/line_request.rs create mode 100644 bindings/rust/src/request_config.rsbindings/rust/Cargo.toml new file +0
-0
bindings/rust/Cargo.toml line 5
Quoted 5 lines
bindings/rust/Cargo.toml new file +5
-0@@ -0,0 +1,12 @@ +[package] +name = "libgpiod" +version = "0.1.0" +edition = "2018" +
Make the top level a workspace, and move libgpiod, libgpiod-sys and gpiosim into sub-directories (as libgpiod-sys already is).
Quoted 62 lines
bindings/rust/Cargo.toml new file +7
-0+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +libc = ">=0.2.39" +libgpiod-sys = { path = "libgpiod-sys" } +thiserror = "1.0" +vmm-sys-util = "=0.9.0"bindings/rust/src/chip.rs new file +49
-0@@ -0,0 +1,179 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar <[email protected]> + +use std::os::raw::c_char; +use std::sync::Arc ; +use std::time::Duration ; +use std::{slice, str}; + +use vmm_sys_util::errno::Error as IoError ; + +use super ::{ + bindings, chip_info::ChipInfo ,Error ,InfoEvent ,LineConfig ,LineInfo ,LineRequest , +RequestConfig ,Result , +}; + +/// GPIO chip +/// +/// A GPIO chip object is associated with an open file descriptor to the GPIO +/// character device. It exposes basic information about the chip and allows +/// callers to retrieve information about each line, watch lines for state +/// changes and make line requests. +pub (crate )struct ChipInternal { + chip: *mut bindings::gpiod_chip , +} + +impl ChipInternal { +/// Find a chip by path. +pub (crate )fn open (path: &str ) ->Result <Self > { +// Null-terminate the string +let path = path.to_owned () +"\0" ; + +let chip =unsafe { bindings::gpiod_chip_open (path.as_ptr ()as *const c_char ) }; +if chip.is_null () { +return Err (Error ::OperationFailed ("Gpio Chip open" ,IoError ::last ())); + } + +Ok (Self { chip }) + } + +/// Private helper, Returns gpiod_chip +pub (crate )fn chip (&self) -> *mut bindings::gpiod_chip { + self.chip + } +} + +impl Drop for ChipInternal { +/// Close the chip and release all associated resources.
bindings/rust/src/chip.rs line 59
Quoted 10 lines
bindings/rust/src/chip.rs new file +10
-0+fn drop (&mut self) { +unsafe { bindings::gpiod_chip_close (self.chip) } + } +} + +pub struct Chip { + ichip:Arc <ChipInternal >, + info:ChipInfo , +} +
Chip is undocumented. (check the generated docs)
Quoted 4 lines
bindings/rust/src/chip.rs new file +4
-0+impl Chip { +/// Find a chip by path. +pub fn open (path: &str ) ->Result <Self > { +let ichip =Arc ::new (ChipInternal ::open (path)?);
bindings/rust/src/chip.rs line 73
Quoted 10 lines
bindings/rust/src/chip.rs new file +10
-0+let info =ChipInfo ::new (ichip.clone ())?; + +Ok (Self { ichip, info }) + } + +/// Get the chip name as represented in the kernel. +pub fn get_name (&self) ->Result <&str > { + self.info.name () + } +
Drop the "get_" prefix from accessor methods (mutators are still prefixed with "set_")[1]
bindings/rust/src/chip.rs line 85
Quoted 12 lines
bindings/rust/src/chip.rs new file +12
-0+/// Get the chip label as represented in the kernel. +pub fn get_label (&self) ->Result <&str > { + self.info.label () + } + +/// Get the number of GPIO lines exposed by the chip. +pub fn get_num_lines (&self) ->u32 { + self.info.num_lines () + } + +/// Get the path used to find the chip. +pub fn get_path (&self) ->Result <&str > {
It seems absurd that a method that simply returns the path provided to open() requires a Result, but that is a consequence of wrapping.
I was considering suggesting caching a copy in struct Chip, but all the other methods require Results so at least this is consistent :-(.
Yay, more unwrapping than Xmas past.
bindings/rust/src/chip.rs line 88
Quoted 3 lines
bindings/rust/src/chip.rs new file +3
-0+// SAFETY: The string returned by libgpiod is guaranteed to live as long +// as the `struct Chip`. +let path =unsafe { bindings::gpiod_chip_get_path (self.ichip.chip ()) };
Trusting that it is never NULL?
Add a null check to be sure.
Quoted 30 lines
bindings/rust/src/chip.rs new file +30
-0+ +// SAFETY: The string is guaranteed to be valid here. + str::from_utf8 (unsafe { + slice::from_raw_parts (pathas *const u8 , bindings::strlen (path)as usize ) + }) + .map_err (Error ::InvalidString ) + } + +/// Get information about the chip. +pub fn info (&self) ->Result <ChipInfo > { +ChipInfo ::new (self.ichip.clone ()) + } + +/// Get a snapshot of information about the line. +pub fn line_info (&self, offset:u32 ) ->Result <LineInfo > { +LineInfo ::new (self.ichip.clone (), offset,false ) + } + +/// Get the current snapshot of information about the line at given offset +/// and optionally start watching it for future changes. +pub fn watch_line_info (&self, offset:u32 ) ->Result <LineInfo > { +LineInfo ::new (self.ichip.clone (), offset,true ) + } + +/// Get the file descriptor associated with the chip. +/// +/// The returned file descriptor must not be closed by the caller, else other methods for the +/// `struct Chip` may fail. +pub fn get_fd (&self) ->Result <u32 > { +let fd =unsafe { bindings::gpiod_chip_get_fd (self.ichip.chip ()) };
bindings/rust/src/chip.rs line 128
Quoted 10 lines
bindings/rust/src/chip.rs new file +10
-0+ +if fd <0 { +Err (Error ::OperationFailed ("Gpio Chip get-fd" ,IoError ::last ())) + }else { +Ok (fdas u32 ) + } + } + +/// Wait for line status events on any of the watched lines on the chip. +pub fn wait_info_event (&self, timeout:Duration ) ->Result <()> {
Durations cannot be negative, so caller cannot make this block indefinitely. Make timeout an Option? (no timeout => block)
Quoted 4 lines
bindings/rust/src/chip.rs new file +4
-0+let ret =unsafe { + bindings::gpiod_chip_wait_info_event (self.ichip.chip (), timeout.as_nanos ()as i64 ) + }; +
bindings/rust/src/chip.rs line 142
Quoted 10 lines
bindings/rust/src/chip.rs new file +10
-0+match ret { + -1 =>Err (Error ::OperationFailed ( +"Gpio Chip info-event-wait" , +IoError ::last (), + )), +0 =>Err (Error ::OperationTimedOut ), + _ =>Ok (()), + } + } +
Better to return Result<bool> indicating if an event is present rather than a timeout error?
bindings/rust/src/chip.rs line 150
Quoted 8 lines
bindings/rust/src/chip.rs new file +8
-0+/// Read a single line status change event from the chip. If no events are +/// pending, this function will block. +pub fn read_info_event (&self) ->Result <InfoEvent > { +InfoEvent ::new (&self.ichip) + } + +/// Map a GPIO line's name to its offset within the chip. +pub fn find_line (&self, name: &str ) ->Result <u32 > {
Rename to line_offset_from_name() to match the renaming in the C API.
Quoted 42 lines
bindings/rust/src/chip.rs new file +29
-0+// Null-terminate the string +let name = name.to_owned () +"\0" ; + +let ret =unsafe { + bindings::gpiod_chip_get_line_offset_from_name ( + self.ichip.chip (), + name.as_ptr ()as *const c_char , + ) + }; + +if ret == -1 { +Err (Error ::OperationFailed ( +"Gpio Chip find-line" , +IoError ::last (), + )) + }else { +Ok (retas u32 ) + } + } + +/// Request a set of lines for exclusive usage. +pub fn request_lines ( + &self, + rconfig: &RequestConfig , + lconfig: &LineConfig , + ) ->Result <LineRequest > { +LineRequest ::new (&self.ichip, rconfig, lconfig) + } +}bindings/rust/src/chip_info.rs new file +7
-0@@ -0,0 +1,69 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar <[email protected]> + +use std::sync::Arc ; +use std::{slice, str};
bindings/rust/src/chip_info.rs line 17
Quoted 10 lines
bindings/rust/src/chip_info.rs new file +10
-0+ +use vmm_sys_util::errno::Error as IoError ; + +use super ::{bindings,ChipInternal ,Error ,Result }; + +/// GPIO chip Information +pub struct ChipInfo { + info: *mut bindings::gpiod_chip_info , +} +
Consider modules and namespaces rather than lumping everything in the gpiod space.
e.g. gpiod::ChipInfo -> gpiod::chip::Info
Quoted 10 lines
bindings/rust/src/chip_info.rs new file +10
-0+impl ChipInfo { +/// Find a GPIO chip by path. +pub (crate )fn new (chip:Arc <ChipInternal >) ->Result <Self > { +let info =unsafe { bindings::gpiod_chip_get_info (chip.chip ()) }; +if info.is_null () { +return Err (Error ::OperationFailed ( +"Gpio Chip get info" , +IoError ::last (), + )); + }
bindings/rust/src/chip_info.rs line 37
Quoted 10 lines
bindings/rust/src/chip_info.rs new file +10
-0+ +Ok (Self { info }) + } + +/// Get the GPIO chip name as represented in the kernel. +pub (crate )fn name (&self) ->Result <&str > { +// SAFETY: The string returned by libgpiod is guaranteed to live as long +// as the `struct Chip`. +let name =unsafe { bindings::gpiod_chip_info_get_name (self.info) }; +
Again, trusting the C not to return NULL.
Similarly elsewhere - don't trust the C library.
Quoted 62 lines
bindings/rust/src/chip_info.rs new file +32
-0+// SAFETY: The string is guaranteed to be valid here. + str::from_utf8 (unsafe { + slice::from_raw_parts (nameas *const u8 , bindings::strlen (name)as usize ) + }) + .map_err (Error ::InvalidString ) + } + +/// Get the GPIO chip label as represented in the kernel. +pub (crate )fn label (&self) ->Result <&str > { +// SAFETY: The string returned by libgpiod is guaranteed to live as long +// as the `struct Chip`. +let label =unsafe { bindings::gpiod_chip_info_get_label (self.info) }; + +// SAFETY: The string is guaranteed to be valid here. + str::from_utf8 (unsafe { + slice::from_raw_parts (labelas *const u8 , bindings::strlen (label)as usize ) + }) + .map_err (Error ::InvalidString ) + } + +/// Get the number of GPIO lines exposed by the chip. +pub (crate )fn num_lines (&self) ->u32 { +unsafe { bindings::gpiod_chip_info_get_num_lines (self.info)as u32 } + } +} + +impl Drop for ChipInfo { +/// Close the GPIO chip info and release all associated resources. +fn drop (&mut self) { +unsafe { bindings::gpiod_chip_info_free (self.info) } + } +}bindings/rust/src/edge_event.rs new file +24
-0@@ -0,0 +1,105 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar <[email protected]> + +use std::sync::Arc ; +use std::time::Duration ; + +use vmm_sys_util::errno::Error as IoError ; + +use super ::{bindings,EdgeEventBufferInternal ,Error ,LineEdgeEvent ,Result }; + +/// Line edge events handling +/// +/// An edge event object contains information about a single line edge event. +/// It contains the event type, timestamp and the offset of the line on which +/// the event occurred as well as two sequence numbers (global for all lines +/// in the associated request and local for this line only). +/// +/// Edge events are stored into an edge-event buffer object to improve +/// performance and to limit the number of memory allocations when a large +/// number of events are being read. + +pub struct EdgeEvent {
bindings/rust/src/edge_event.rs line 34
Quoted 10 lines
bindings/rust/src/edge_event.rs new file +10
-0+ ibuffer: Option<Arc<EdgeEventBufferInternal>>, + event: *mut bindings::gpiod_edge_event, +} + +impl EdgeEvent { +/// Get an event stored in the buffer. +pub (crate )fn new ( + ibuffer: &Arc <EdgeEventBufferInternal >, + index:u64 , + copy: bool,
rename to copied or cloned, or flip the sense and call it buffered or contained.
Quoted 19 lines
bindings/rust/src/edge_event.rs new file +19
-0+ ) -> Result<Self> { +let event =unsafe { bindings::gpiod_edge_event_buffer_get_event (ibuffer.buffer (), index) }; +if event.is_null () { +return Err (Error ::OperationFailed ( +"Gpio EdgeEvent buffer-get-event" , +IoError ::last (), + )); + } + +if copy { +let event =unsafe { bindings::gpiod_edge_event_copy (event) }; +if event.is_null () { +return Err (Error ::OperationFailed ( +"Gpio EdgeEvent copy" , +IoError ::last (), + )); + } + +Ok (Self {
bindings/rust/src/edge_event.rs line 63
Quoted 10 lines
bindings/rust/src/edge_event.rs new file +10
-0+ ibuffer: None, + event, + }) + }else { +Ok (Self { + ibuffer:Some (ibuffer.clone ()), + event, + }) + } + }
This is both a new() and a clone(). Split them out.
Quoted 61 lines
bindings/rust/src/edge_event.rs new file +42
-0+ +/// Get the event type. +pub fn get_event_type (&self) ->Result <LineEdgeEvent > { +LineEdgeEvent ::new (unsafe { bindings::gpiod_edge_event_get_event_type (self.event) }as u32 ) + } + +/// Get the timestamp of the event. +pub fn get_timestamp (&self) ->Duration { +Duration ::from_nanos (unsafe { bindings::gpiod_edge_event_get_timestamp_ns (self.event) }) + } + +/// Get the offset of the line on which the event was triggered. +pub fn get_line_offset (&self) ->u32 { +unsafe { bindings::gpiod_edge_event_get_line_offset (self.event) } + } + +/// Get the global sequence number of the event. +/// +/// Returns sequence number of the event relative to all lines in the +/// associated line request. +pub fn get_global_seqno (&self) ->u64 { +unsafe { bindings::gpiod_edge_event_get_global_seqno (self.event) } + } + +/// Get the event sequence number specific to concerned line. +/// +/// Returns sequence number of the event relative to the line within the +/// lifetime of the associated line request. +pub fn get_line_seqno (&self) ->u64 { +unsafe { bindings::gpiod_edge_event_get_line_seqno (self.event) } + } +} + +impl Drop for EdgeEvent { +/// Free the edge event. +fn drop (&mut self) { +// Free the event only if a copy is made +if self.ibuffer.is_none () { +unsafe { bindings::gpiod_edge_event_free (self.event) }; + } + } +}bindings/rust/src/event_buffer.rs new file +13
-0@@ -0,0 +1,88 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar <[email protected]> + +use std::os::raw::c_ulong; +use std::sync::Arc ; + +use vmm_sys_util::errno::Error as IoError ; + +use super ::{bindings,EdgeEvent ,Error ,Result }; + +/// Line edge events buffer
bindings/rust/src/event_buffer.rs line 23
Quoted 10 lines
bindings/rust/src/event_buffer.rs new file +10
-0+pub (crate )struct EdgeEventBufferInternal { + buffer: *mut bindings::gpiod_edge_event_buffer , +} + +impl EdgeEventBufferInternal { +/// Create a new edge event buffer. +/// +/// If capacity equals 0, it will be set to a default value of 64. If +/// capacity is larger than 1024, it will be limited to 1024. +pub fn new (capacity:u32 ) ->Result <Self > {
Use usize not u32 for array sizes throughout.
Quoted 39 lines
bindings/rust/src/event_buffer.rs new file +39
-0+let buffer =unsafe { bindings::gpiod_edge_event_buffer_new (capacityas c_ulong ) }; +if buffer.is_null () { +return Err (Error ::OperationFailed ( +"Gpio EdgeEventBuffer new" , +IoError ::last (), + )); + } + +Ok (Self { buffer }) + } + +/// Private helper, Returns gpiod_edge_event_buffer +pub (crate )fn buffer (&self) -> *mut bindings::gpiod_edge_event_buffer { + self.buffer + } +} + +impl Drop for EdgeEventBufferInternal { +/// Free the edge event buffer and release all associated resources. +fn drop (&mut self) { +unsafe { bindings::gpiod_edge_event_buffer_free (self.buffer) }; + } +} + +/// Line edge events buffer +pub struct EdgeEventBuffer { + ibuffer:Arc <EdgeEventBufferInternal >, +} + +impl EdgeEventBuffer { +/// Create a new edge event buffer. +/// +/// If capacity equals 0, it will be set to a default value of 64. If +/// capacity is larger than 1024, it will be limited to 1024. +pub fn new (capacity:u32 ) ->Result <Self > { +Ok (Self { + ibuffer:Arc ::new (EdgeEventBufferInternal ::new (capacity)?), + }) + }
bindings/rust/src/event_buffer.rs line 72
Quoted 10 lines
bindings/rust/src/event_buffer.rs new file +10
-0+ +/// Private helper, Returns gpiod_edge_event_buffer +pub (crate )fn buffer (&self) -> *mut bindings::gpiod_edge_event_buffer { + self.ibuffer.buffer () + } + +/// Get the capacity of the event buffer. +pub fn get_capacity (&self) ->u32 { +unsafe { bindings::gpiod_edge_event_buffer_get_capacity (self.buffer ())as u32 } + }
Follow the Vec API and make this capacity().num_events() becomes len().
bindings/rust/src/event_buffer.rs line 82
Quoted 10 lines
bindings/rust/src/event_buffer.rs new file +10
-0+ +/// Read an event stored in the buffer. +pub fn get_event (&self, index:u64 ) ->Result <EdgeEvent > { +EdgeEvent ::new (&self.ibuffer, index,false ) + } + +/// Make copy of an edge event stored in the buffer. +pub fn get_event_copy (&self, index:u64 ) ->Result <EdgeEvent > { +EdgeEvent ::new (&self.ibuffer, index,true ) + }
impl Clone for EdgeEvent instead.
bindings/rust/src/event_buffer.rs line 84
Quoted 2 lines
bindings/rust/src/event_buffer.rs new file +2
-0+ +/// Get the number of events the buffers stores.
stores -> contains
The capacity indicates the number that can be stored. This field indicates the number the buffer currently contains.
And, as mentioned above, rename to len(), and return value should be usize.
Add a set_len() method to replace the max_events parameter in LineRequest::read_edge_event() to handle the unusual case where the user only wants to partially fill the buffer.
Quoted 100 lines
bindings/rust/src/event_buffer.rs new file +4
-0+pub fn get_num_events (&self) ->u32 { +unsafe { bindings::gpiod_edge_event_buffer_get_num_events (self.buffer ())as u32 } + } +}bindings/rust/src/info_event.rs new file +68
-0@@ -0,0 +1,68 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar <[email protected]> + +use std::convert::TryFrom ; +use std::sync::Arc ; +use std::time::Duration ; + +use vmm_sys_util::errno::Error as IoError ; + +use super ::{bindings,ChipInternal ,Error ,Event ,LineInfo ,Result }; + +/// Line status watch events +/// +/// Accessors for the info event objects allowing to monitor changes in GPIO +/// line state. +/// +/// Callers can be notified about changes in line's state using the interfaces +/// exposed by GPIO chips. Each info event contains information about the event +/// itself (timestamp, type) as well as a snapshot of line's state in the form +/// of a line-info object. + +pub struct InfoEvent { + event: *mut bindings::gpiod_info_event , +} + +impl InfoEvent { +/// Get a single chip's line's status change event. +pub (crate )fn new (ichip: &Arc <ChipInternal >) ->Result <Self > { +let event =unsafe { bindings::gpiod_chip_read_info_event (ichip.chip ()) }; +if event.is_null () { +return Err (Error ::OperationFailed ( +"Gpio InfoEvent event-read" , +IoError ::last (), + )); + } + +Ok (Self { event }) + } + +/// Private helper, Returns gpiod_info_event +pub (crate )fn event (&self) -> *mut bindings::gpiod_info_event { + self.event + } + +/// Get the event type of the status change event. +pub fn get_event_type (&self) ->Result <Event > { +Event ::new (unsafe { bindings::gpiod_info_event_get_event_type (self.event) }as u32 ) + } + +/// Get the timestamp of the event, read from the monotonic clock. +pub fn get_timestamp (&self) ->Duration { +Duration ::from_nanos (unsafe { bindings::gpiod_info_event_get_timestamp_ns (self.event) }) + } + +/// Get the line-info object associated with the event. +pub fn line_info (&self) ->Result <LineInfo > { +LineInfo ::try_from (self) + } +} + +impl Drop for InfoEvent { +/// Free the info event object and release all associated resources. +fn drop (&mut self) { +unsafe { bindings::gpiod_info_event_free (self.event) } + } +}bindings/rust/src/lib.rs new file +16
-0@@ -0,0 +1,317 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Rust wrappers for GPIOD APIs +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar <[email protected]> + +//! libgpiod public API +//! +//! This is the complete documentation of the public Rust API made available to +//! users of libgpiod. +//! +//! The API is logically split into several parts such as: GPIO chip & line +//! operators, GPIO events handling etc. + +mod chip;
bindings/rust/src/lib.rs line 26
Quoted 10 lines
bindings/rust/src/lib.rs new file +10
-0+mod chip_info; +mod edge_event; +mod event_buffer; +mod info_event; +mod line_config; +mod line_info; +mod line_request; +mod request_config; + +use libgpiod_sysas bindings;
I find the original name clearer.
From the end user's perspective THIS module is the bindings. If you want a short name how about "gpiod"?
Quoted 6 lines
bindings/rust/src/lib.rs new file +6
-0+ +pub use crate ::chip::*; +pub use crate ::edge_event::*; +pub use crate ::event_buffer::*; +pub use crate ::info_event::*; +pub use crate ::line_config::*;
bindings/rust/src/lib.rs line 42
Quoted 10 lines
bindings/rust/src/lib.rs new file +10
-0+pub use crate ::line_info::*; +pub use crate ::line_request::*; +pub use crate ::request_config::*; + +use std::os::raw::c_char; +use std::{slice, str}; + +use thiserror::Error as ThisError ; +use vmm_sys_util::errno::Error as IoError ; +
IoError could be confused with std::io::Error.
This is just an Errno.
bindings/rust/src/lib.rs line 49
Quoted 7 lines
bindings/rust/src/lib.rs new file +7
-0+/// Result of libgpiod operations +pub type Result <T > = std::result::Result <T ,Error >; + +/// Error codes for libgpiod operations +#[derive( Copy , Clone , Debug , PartialEq , ThisError )] +pub enum Error { + #[error("Failed to find {0}")]
"find" implies a search. Where it is used it is just a get.
bindings/rust/src/lib.rs line 50
Quoted 1 line
bindings/rust/src/lib.rs new file +1
-0+ NameNotFound(&'static str),
This is used when the C API returns a NULL char *. Not sure "NameNotFound" indicates that.
It is a specific case of InvalidValue. Maybe NullString?
bindings/rust/src/lib.rs line 52
Quoted 2 lines
bindings/rust/src/lib.rs new file +2
-0+#[error( "Invalid String: {0:?}" )] + InvalidString(str::Utf8Error),
Specifically the string is not UTF8, so "InvalidString" is a bit vague.
bindings/rust/src/lib.rs line 54
Quoted 2 lines
bindings/rust/src/lib.rs new file +2
-0+#[error( "Invalid {0} value: {1}" )] + InvalidValue(&'static str, u32),
And this is an InvalidEnumValue.
bindings/rust/src/lib.rs line 56
Quoted 2 lines
bindings/rust/src/lib.rs new file +2
-0+#[error( "Operation {0} Failed: {1}" )] + OperationFailed(&'static str, IoError),
Use an enum for the operation rather than a string?
And if it returns an IoError it must be an IoOperation? Else if the IoError is just an errno then call it that.
bindings/rust/src/lib.rs line 58
Quoted 2 lines
bindings/rust/src/lib.rs new file +2
-0+#[error( "Operation Timed-out" )] + OperationTimedOut,
No indication which operation?
Scratch that - this error should be removed.
Refer to comments where used below.
Quoted 35 lines
bindings/rust/src/lib.rs new file +35
-0+} + +/// Direction settings. +pub enum Direction { +/// Request the line(s), but don't change direction. +AsIs , +/// Direction is input - for reading the value of an externally driven GPIO line. +Input , +/// Direction is output - for driving the GPIO line. +Output , +} + +impl Direction { +fn new (dir:u32 ) ->Result <Self > { +match dir { + bindings::GPIOD_LINE_DIRECTION_AS_IS =>Ok (Direction ::AsIs ), + bindings::GPIOD_LINE_DIRECTION_INPUT =>Ok (Direction ::Input ), + bindings::GPIOD_LINE_DIRECTION_OUTPUT =>Ok (Direction ::Output ), + _ =>Err (Error ::InvalidValue ("direction" , dir)), + } + } + +fn gpiod_direction (&self) ->u32 { +match self { +Direction ::AsIs => bindings::GPIOD_LINE_DIRECTION_AS_IS , +Direction ::Input => bindings::GPIOD_LINE_DIRECTION_INPUT , +Direction ::Output => bindings::GPIOD_LINE_DIRECTION_OUTPUT , + } + } +} + +/// Internal bias settings. +pub enum Bias { +/// Don't change the bias setting when applying line config. +AsIs ,
bindings/rust/src/lib.rs line 103
Quoted 10 lines
bindings/rust/src/lib.rs new file +10
-0+/// The internal bias state is unknown. + Unknown, +/// The internal bias is disabled. + Disabled, +/// The internal pull-up bias is enabled. + PullUp, +/// The internal pull-down bias is enabled. + PullDown, +} +
Bias fields should be Option<Bias>
AsIs only makes sense in requests and should be None. Unknown only makes sense in responses and should be None.
Quoted 54 lines
bindings/rust/src/lib.rs new file +54
-0+impl Bias { +fn new (bias:u32 ) ->Result <Self > { +match bias { + bindings::GPIOD_LINE_BIAS_AS_IS =>Ok (Bias ::AsIs ), + bindings::GPIOD_LINE_BIAS_UNKNOWN =>Ok (Bias ::Unknown ), + bindings::GPIOD_LINE_BIAS_DISABLED =>Ok (Bias ::Disabled ), + bindings::GPIOD_LINE_BIAS_PULL_UP =>Ok (Bias ::PullUp ), + bindings::GPIOD_LINE_BIAS_PULL_DOWN =>Ok (Bias ::PullDown ), + _ =>Err (Error ::InvalidValue ("bias" , bias)), + } + } + +fn gpiod_bias (&self) ->u32 { +match self { +Bias ::AsIs => bindings::GPIOD_LINE_BIAS_AS_IS , +Bias ::Unknown => bindings::GPIOD_LINE_BIAS_UNKNOWN , +Bias ::Disabled => bindings::GPIOD_LINE_BIAS_DISABLED , +Bias ::PullUp => bindings::GPIOD_LINE_BIAS_PULL_UP , +Bias ::PullDown => bindings::GPIOD_LINE_BIAS_PULL_DOWN , + } + } +} + +/// Drive settings. +pub enum Drive { +/// Drive setting is push-pull. +PushPull , +/// Line output is open-drain. +OpenDrain , +/// Line output is open-source. +OpenSource , +} + +impl Drive { +fn new (drive:u32 ) ->Result <Self > { +match drive { + bindings::GPIOD_LINE_DRIVE_PUSH_PULL =>Ok (Drive ::PushPull ), + bindings::GPIOD_LINE_DRIVE_OPEN_DRAIN =>Ok (Drive ::OpenDrain ), + bindings::GPIOD_LINE_DRIVE_OPEN_SOURCE =>Ok (Drive ::OpenSource ), + _ =>Err (Error ::InvalidValue ("drive" , drive)), + } + } + +fn gpiod_drive (&self) ->u32 { +match self { +Drive ::PushPull => bindings::GPIOD_LINE_DRIVE_PUSH_PULL , +Drive ::OpenDrain => bindings::GPIOD_LINE_DRIVE_OPEN_DRAIN , +Drive ::OpenSource => bindings::GPIOD_LINE_DRIVE_OPEN_SOURCE , + } + } +} + +/// Edge detection settings. +pub enum Edge {
bindings/rust/src/lib.rs line 167
Quoted 10 lines
bindings/rust/src/lib.rs new file +10
-0+/// Line edge detection is disabled. + None, +/// Line detects rising edge events. + Rising, +/// Line detects falling edge events. + Falling, +/// Line detects both rising and falling edge events. + Both, +} +
As per Bias, the None case should be provided by making the field an Option where used.
Quoted 13 lines
bindings/rust/src/lib.rs new file +13
-0+impl Edge { +fn new (edge:u32 ) ->Result <Self > { +match edge { + bindings::GPIOD_LINE_EDGE_NONE =>Ok (Edge ::None ), + bindings::GPIOD_LINE_EDGE_RISING =>Ok (Edge ::Rising ), + bindings::GPIOD_LINE_EDGE_FALLING =>Ok (Edge ::Falling ), + bindings::GPIOD_LINE_EDGE_BOTH =>Ok (Edge ::Both ), + _ =>Err (Error ::InvalidValue ("edge" , edge)), + } + } + +fn gpiod_edge (&self) ->u32 { +match self {
bindings/rust/src/lib.rs line 190
Quoted 10 lines
bindings/rust/src/lib.rs new file +10
-0+ Edge::None => bindings::GPIOD_LINE_EDGE_NONE, + Edge::Rising => bindings::GPIOD_LINE_EDGE_RISING, + Edge::Falling => bindings::GPIOD_LINE_EDGE_FALLING, + Edge::Both => bindings::GPIOD_LINE_EDGE_BOTH, + } + } +} + +/// Line config settings. +pub enum Config {
Rename to SettingKind.
(see comments in line-config)
Quoted 58 lines
bindings/rust/src/lib.rs new file +58
-0+/// Line direction. + Direction, +/// Edge detection. + EdgeDetection, +/// Bias. + Bias, +/// Drive. + Drive, +/// Active-low setting. + ActiveLow, +/// Debounce period. + DebouncePeriodUs, +/// Event clock type. + EventClock, +/// Output value. + OutputValue, +} + +impl Config { +fn new (config:u32 ) ->Result <Self > { +match config { + bindings::GPIOD_LINE_CONFIG_PROP_DIRECTION =>Ok (Config ::Direction ), + bindings::GPIOD_LINE_CONFIG_PROP_EDGE_DETECTION =>Ok (Config ::EdgeDetection ), + bindings::GPIOD_LINE_CONFIG_PROP_BIAS =>Ok (Config ::Bias ), + bindings::GPIOD_LINE_CONFIG_PROP_DRIVE =>Ok (Config ::Drive ), + bindings::GPIOD_LINE_CONFIG_PROP_ACTIVE_LOW =>Ok (Config ::ActiveLow ), + bindings::GPIOD_LINE_CONFIG_PROP_DEBOUNCE_PERIOD_US =>Ok (Config ::DebouncePeriodUs ), + bindings::GPIOD_LINE_CONFIG_PROP_EVENT_CLOCK =>Ok (Config ::EventClock ), + bindings::GPIOD_LINE_CONFIG_PROP_OUTPUT_VALUE =>Ok (Config ::OutputValue ), + _ =>Err (Error ::InvalidValue ("config" , config)), + } + } +} + +/// Event clock settings. +pub enum EventClock { +/// Line uses the monotonic clock for edge event timestamps. +Monotonic , +/// Line uses the realtime clock for edge event timestamps. +Realtime , +} + +impl EventClock { +fn new (clock:u32 ) ->Result <Self > { +match clock { + bindings::GPIOD_LINE_EVENT_CLOCK_MONOTONIC =>Ok (EventClock ::Monotonic ), + bindings::GPIOD_LINE_EVENT_CLOCK_REALTIME =>Ok (EventClock ::Realtime ), + _ =>Err (Error ::InvalidValue ("event clock" , clock)), + } + } + +fn gpiod_clock (&self) ->u32 { +match self { +EventClock ::Monotonic => bindings::GPIOD_LINE_EVENT_CLOCK_MONOTONIC , +EventClock ::Realtime => bindings::GPIOD_LINE_EVENT_CLOCK_REALTIME , + } + } +}
bindings/rust/src/lib.rs line 258
Quoted 10 lines
bindings/rust/src/lib.rs new file +10
-0+ +/// Line status change event types. +pub enum Event { +/// Line has been requested. +LineRequested , +/// Previously requested line has been released. +LineReleased , +/// Line configuration has changed. +LineConfigChanged , +}
This is not an Event. It is a LineInfoChange or LineInfoChangeKind.
And then the "Line" prefix is redundant.
Quoted 11 lines
bindings/rust/src/lib.rs new file +11
-0+ +impl Event { +fn new (event:u32 ) ->Result <Self > { +match event { + bindings::GPIOD_INFO_EVENT_LINE_REQUESTED =>Ok (Event ::LineRequested ), + bindings::GPIOD_INFO_EVENT_LINE_RELEASED =>Ok (Event ::LineReleased ), + bindings::GPIOD_INFO_EVENT_LINE_CONFIG_CHANGED =>Ok (Event ::LineConfigChanged ), + _ =>Err (Error ::InvalidValue ("event" , event)), + } + } +}
bindings/rust/src/lib.rs line 279
Quoted 10 lines
bindings/rust/src/lib.rs new file +10
-0+ +#[derive( Copy , Clone )] +/// Edge event types. +pub enum LineEdgeEvent { +/// Rising edge event. +Rising , +/// Falling edge event. +Falling , +} +
Similarly LineEdge or LineEdgeKind.
Quoted 13 lines
bindings/rust/src/lib.rs new file +13
-0+impl LineEdgeEvent { +fn new (event:u32 ) ->Result <Self > { +match event { + bindings::GPIOD_EDGE_EVENT_RISING_EDGE =>Ok (LineEdgeEvent ::Rising ), + bindings::GPIOD_EDGE_EVENT_FALLING_EDGE =>Ok (LineEdgeEvent ::Falling ), + _ =>Err (Error ::InvalidValue ("edge event" , event)), + } + } +} + +/// Various libgpiod-related functions. + +/// Check if the file pointed to by path is a GPIO chip character device.
bindings/rust/src/lib.rs line 302
Quoted 10 lines
bindings/rust/src/lib.rs new file +10
-0+/// +/// Returns true if the file exists and is a GPIO chip character device or a +/// symbolic link to it. +pub fn gpiod_is_gpiochip_device (path: &str ) ->bool { +// Null-terminate the string +let path = path.to_owned () +"\0" ; + +unsafe { bindings::gpiod_is_gpiochip_device (path.as_ptr ()as *const c_char ) } +} +
The "gpiod_" prefix is redundant for top level functions in the crate.
Quoted 44 lines
bindings/rust/src/lib.rs new file +15
-0+/// Get the API version of the library as a human-readable string. +pub fn gpiod_version_string () ->Result <&'staticstr > { +// SAFETY: The string returned by libgpiod is guaranteed to live forever. +let version =unsafe { bindings::gpiod_version_string () }; + +if version.is_null () { +return Err (Error ::NameNotFound ("GPIO library version" )); + } + +// SAFETY: The string is guaranteed to be valid here. + str::from_utf8 (unsafe { + slice::from_raw_parts (versionas *const u8 , bindings::strlen (version)as usize ) + }) + .map_err (Error ::InvalidString ) +}bindings/rust/src/line_config.rs new file +23
-0@@ -0,0 +1,493 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar <[email protected]> + +use libc::EINVAL ; +use std::os::raw::c_ulong; +use std::time::Duration ; + +use vmm_sys_util::errno::Error as IoError ; + +use super ::{bindings,Bias ,Config ,Direction ,Drive ,Edge ,Error ,EventClock ,Result }; + +/// Line configuration objects. +/// +/// The line-config object contains the configuration for lines that can be +/// used in two cases: +/// - when making a line request +/// - when reconfiguring a set of already requested lines. +/// +/// A new line-config object is instantiated with a set of sane defaults +/// for all supported configuration settings. Those defaults can be modified by +/// the caller. Default values can be overridden by applying different values
bindings/rust/src/line_config.rs line 33
Quoted 10 lines
bindings/rust/src/line_config.rs new file +10
-0+/// for specific lines. When making a request or reconfiguring an existing one, +/// the overridden settings for specific lines take precedence. For lines +/// without an override the requested default settings are used. +/// +/// For every setting there are two mutators (one setting the default and one +/// for the per-line override), two getters (one for reading the global +/// default and one for retrieving the effective value for the line), +/// a function for testing if a setting is overridden for the line +/// and finally a function for clearing the overrides (per line). +///
Add a Setting enum that has a variant for each setting. Then you only need 3 mutators total.
And the user can define configs as a list of Settings. So perhaps the mutators should accept &[Setting]. And &[offsets] rather than just offset.
Similarly, gets can be consolidated into:
get_prop_offset(self, offset, SettingKind) -> Result<Setting>
and
get_prop_default(self, SettingKind) -> Result<Setting>
(simplified signatures)
As per my comments to the Python bindings, I question the value of the default/override/clear pattern over a straight up set, but this is at least consistent with the other bindings.
Quoted 249 lines
bindings/rust/src/line_config.rs new file +249
-0+/// The mutators don't return errors. If the set of options is too complex to +/// be translated into kernel uAPI structures then an error will be returned at +/// the time of the request or reconfiguration. If an invalid value was passed +/// to any of the mutators then the default value will be silently used instead. +/// +/// Operating on lines in struct LineConfig has no immediate effect on real +/// GPIOs, it only manipulates the config object in memory. Those changes are +/// only applied to the hardware at the time of the request or reconfiguration. +/// +/// Overrides for lines that don't end up being requested are silently ignored +/// both in LineRequest::new() as well as in LineRequest::reconfigure_lines(). +/// +/// In cases where all requested lines are using the one configuration, the +/// line overrides can be entirely ignored when preparing the configuration. + +pub struct LineConfig { + config: *mut bindings::gpiod_line_config , +} + +impl LineConfig { +/// Create a new line config object. +pub fn new () ->Result <Self > { +let config =unsafe { bindings::gpiod_line_config_new () }; + +if config.is_null () { +return Err (Error ::OperationFailed ( +"Gpio LineConfig new" , +IoError ::last (), + )); + } + +Ok (Self { config }) + } + +/// Private helper, Returns gpiod_line_config +pub (crate )fn config (&self) -> *mut bindings::gpiod_line_config { + self.config + } + +/// Resets the entire configuration stored in the object. This is useful if +/// the user wants to reuse the object without reallocating it. +pub fn reset (&mut self) { +unsafe { bindings::gpiod_line_config_reset (self.config) } + } + +/// Set the default line direction. +pub fn set_direction_default (&mut self, direction:Direction ) { +unsafe { + bindings::gpiod_line_config_set_direction_default ( + self.config, + direction.gpiod_direction ()as i32 , + ) + } + } + +/// Set the direction for a line. +pub fn set_direction_override (&mut self, direction:Direction , offset:u32 ) { +unsafe { + bindings::gpiod_line_config_set_direction_override ( + self.config, + direction.gpiod_direction ()as i32 , + offset, + ) + } + } + +/// Clear the direction for a line. +pub fn clear_direction_override (&mut self, offset:u32 ) { +unsafe { bindings::gpiod_line_config_clear_direction_override (self.config, offset) } + } + +/// Check if the direction is overridden for a line. +pub fn direction_is_overridden (&self, offset:u32 ) ->bool { +unsafe { bindings::gpiod_line_config_direction_is_overridden (self.config, offset) } + } + +/// Get the default direction setting. +pub fn get_direction_default (&self) ->Result <Direction > { +Direction ::new ( +unsafe { bindings::gpiod_line_config_get_direction_default (self.config) }as u32 , + ) + } + +/// Get the direction of a given line. +/// +/// Direction setting for the line if the config object were used in a request. +pub fn get_direction_offset (&self, offset:u32 ) ->Result <Direction > { +Direction ::new (unsafe { + bindings::gpiod_line_config_get_direction_offset (self.config, offset) + }as u32 ) + } + +/// Set the default edge event detection setting. +pub fn set_edge_detection_default (&mut self, edge:Edge ) { +unsafe { + bindings::gpiod_line_config_set_edge_detection_default ( + self.config, + edge.gpiod_edge ()as i32 , + ) + } + } + +/// Set the edge event detection for a single line. +pub fn set_edge_detection_override (&mut self, edge:Edge , offset:u32 ) { +unsafe { + bindings::gpiod_line_config_set_edge_detection_override ( + self.config, + edge.gpiod_edge ()as i32 , + offset, + ) + } + } + +/// Clear the edge event detection for a single line. +pub fn clear_edge_detection_override (&mut self, offset:u32 ) { +unsafe { bindings::gpiod_line_config_clear_edge_detection_override (self.config, offset) } + } + +/// Check if the edge event detection is overridden for a line. +pub fn edge_detection_is_overridden (&self, offset:u32 ) ->bool { +unsafe { bindings::gpiod_line_config_edge_detection_is_overridden (self.config, offset) } + } + +/// Get the default edge event detection setting. +pub fn get_edge_detection_default (&self) ->Result <Edge > { +Edge ::new ( +unsafe { bindings::gpiod_line_config_get_edge_detection_default (self.config) }as u32 , + ) + } + +/// Get the edge event detection setting for a given line. +/// +/// Edge event detection setting for the line if the config object were used in a request. +pub fn get_edge_detection_offset (&self, offset:u32 ) ->Result <Edge > { +Edge ::new (unsafe { + bindings::gpiod_line_config_get_edge_detection_offset (self.config, offset) + }as u32 ) + } + +/// Set the default bias setting. +pub fn set_bias_default (&mut self, bias:Bias ) { +unsafe { + bindings::gpiod_line_config_set_bias_default (self.config, bias.gpiod_bias ()as i32 ) + } + } + +/// Set the bias for a single line. +pub fn set_bias_override (&mut self, bias:Bias , offset:u32 ) { +unsafe { + bindings::gpiod_line_config_set_bias_override ( + self.config, + bias.gpiod_bias ()as i32 , + offset, + ) + } + } + +/// Clear the bias for a single line. +pub fn clear_bias_override (&mut self, offset:u32 ) { +unsafe { bindings::gpiod_line_config_clear_bias_override (self.config, offset) } + } + +/// Check if the bias is overridden for a line. +pub fn bias_is_overridden (&self, offset:u32 ) ->bool { +unsafe { bindings::gpiod_line_config_bias_is_overridden (self.config, offset) } + } + +/// Get the default bias setting. +pub fn get_bias_default (&self) ->Result <Bias > { +Bias ::new (unsafe { bindings::gpiod_line_config_get_bias_default (self.config) }as u32 ) + } + +/// Get the bias setting for a given line. +/// +/// Bias setting used for the line if the config object were used in a request. +pub fn get_bias_offset (&self, offset:u32 ) ->Result <Bias > { +Bias ::new ( +unsafe { bindings::gpiod_line_config_get_bias_offset (self.config, offset) }as u32 , + ) + } + +/// Set the default drive setting. +pub fn set_drive_default (&mut self, drive:Drive ) { +unsafe { + bindings::gpiod_line_config_set_drive_default (self.config, drive.gpiod_drive ()as i32 ) + } + } + +/// Set the drive for a single line. +pub fn set_drive_override (&mut self, drive:Drive , offset:u32 ) { +unsafe { + bindings::gpiod_line_config_set_drive_override ( + self.config, + drive.gpiod_drive ()as i32 , + offset, + ) + } + } + +/// clear the drive for a single line. +pub fn clear_drive_override (&mut self, offset:u32 ) { +unsafe { bindings::gpiod_line_config_clear_drive_override (self.config, offset) } + } + +/// Check if the drive is overridden for a line. +pub fn drive_is_overridden (&self, offset:u32 ) ->bool { +unsafe { bindings::gpiod_line_config_drive_is_overridden (self.config, offset) } + } + +/// Get the default drive setting. +pub fn get_drive_default (&self) ->Result <Drive > { +Drive ::new (unsafe { bindings::gpiod_line_config_get_drive_default (self.config) }as u32 ) + } + +/// Get the drive setting for a given line. +/// +/// The offset of the line for which to read the drive setting. Drive setting for the line if +/// the config object were used in a request. +pub fn get_drive_offset (&self, offset:u32 ) ->Result <Drive > { +Drive ::new ( +unsafe { bindings::gpiod_line_config_get_drive_offset (self.config, offset) }as u32 , + ) + } + +/// Set default active-low setting. +pub fn set_active_low_default (&mut self, active_low:bool ) { +unsafe { bindings::gpiod_line_config_set_active_low_default (self.config, active_low) } + } + +/// Set active-low setting for a single line. +pub fn set_active_low_override (&mut self, active_low:bool , offset:u32 ) { +unsafe { + bindings::gpiod_line_config_set_active_low_override (self.config, active_low, offset) + } + } + +/// Clear a single line's active-low setting. +pub fn clear_active_low_override (&mut self, offset:u32 ) { +unsafe { bindings::gpiod_line_config_clear_active_low_override (self.config, offset) } + } + +/// Check if the active-low is overridden for a line. +pub fn active_low_is_overridden (&mut self, offset:u32 ) ->bool { +unsafe { bindings::gpiod_line_config_active_low_is_overridden (self.config, offset) } + } + +/// Check the default active-low setting. +pub fn get_active_low_default (&self) ->bool { +unsafe { bindings::gpiod_line_config_get_active_low_default (self.config) }
bindings/rust/src/line_config.rs line 292
Quoted 10 lines
bindings/rust/src/line_config.rs new file +10
-0+ } + +/// Check the active-low setting of a line. +/// +/// Active-low setting for the line if the config object were used in a request. +pub fn get_active_low_offset (&self, offset:u32 ) ->bool { +unsafe { bindings::gpiod_line_config_get_active_low_offset (self.config, offset) } + } + +/// Set the deafult debounce period setting.
default
Quoted 91 lines
bindings/rust/src/line_config.rs new file +91
-0+pub fn set_debounce_period_default (&mut self, period:Duration ) { +unsafe { + bindings::gpiod_line_config_set_debounce_period_us_default ( + self.config, + period.as_micros ()as u64 , + ) + } + } + +/// Set the debounce period for a single line. +pub fn set_debounce_period_override (&mut self, period:Duration , offset:u32 ) { +unsafe { + bindings::gpiod_line_config_set_debounce_period_us_override ( + self.config, + period.as_micros ()as u64 , + offset, + ) + } + } + +/// Clear the debounce period for a single line. +pub fn clear_debounce_period_override (&mut self, offset:u32 ) { +unsafe { + bindings::gpiod_line_config_clear_debounce_period_us_override (self.config, offset) + } + } + +/// Check if the debounce period setting is overridden. +pub fn debounce_period_is_overridden (&self, offset:u32 ) ->bool { +unsafe { bindings::gpiod_line_config_debounce_period_us_is_overridden (self.config, offset) } + } + +/// Get the default debounce period. +pub fn get_debounce_period_default (&self) ->Result <Duration > { +Ok (Duration ::from_micros (unsafe { + bindings::gpiod_line_config_get_debounce_period_us_default (self.config) + })) + } + +/// Get the debounce period for a given line. +/// +/// Debounce period for the line if the config object were used in a request, 0 if debouncing +/// is disabled. +pub fn get_debounce_period_offset (&self, offset:u32 ) ->Result <Duration > { +Ok (Duration ::from_micros (unsafe { + bindings::gpiod_line_config_get_debounce_period_us_offset (self.config, offset) + })) + } + +/// Set the default event clock setting. +pub fn set_event_clock_default (&mut self, clock:EventClock ) { +unsafe { + bindings::gpiod_line_config_set_event_clock_default ( + self.config, + clock.gpiod_clock ()as i32 , + ) + } + } + +/// Set the event clock for a single line. +pub fn set_event_clock_override (&mut self, clock:EventClock , offset:u32 ) { +unsafe { + bindings::gpiod_line_config_set_event_clock_override ( + self.config, + clock.gpiod_clock ()as i32 , + offset, + ) + } + } + +/// Clear the event clock for a single line. +pub fn clear_event_clock_override (&mut self, offset:u32 ) { +unsafe { bindings::gpiod_line_config_clear_event_clock_override (self.config, offset) } + } + +/// Check if the event clock is overridden for a line. +pub fn event_clock_is_overridden (&mut self, offset:u32 ) ->bool { +unsafe { bindings::gpiod_line_config_event_clock_is_overridden (self.config, offset) } + } + +/// Get the default event clock setting. +pub fn get_event_clock_default (&self) ->Result <EventClock > { +EventClock ::new ( +unsafe { bindings::gpiod_line_config_get_event_clock_default (self.config) }as u32 , + ) + } + +/// Get the event clock setting for a given line. +/// +/// Event clock setting for the line if the config object were used in a request. +pub fn get_event_clock_offset (&self, offset:u32 ) ->Result <EventClock > {
bindings/rust/src/line_config.rs line 393
Quoted 10 lines
bindings/rust/src/line_config.rs new file +10
-0+EventClock ::new (unsafe { + bindings::gpiod_line_config_get_event_clock_offset (self.config, offset) + }as u32 ) + } + +/// Set the default output value setting. +pub fn set_output_value_default (&mut self, value:u32 ) { +unsafe { bindings::gpiod_line_config_set_output_value_default (self.config, valueas i32 ) } + } +
Make value an enum (active/inactive) not a u32 throughout. Perhaps give offsets a type as well.
Quoted 125 lines
bindings/rust/src/line_config.rs new file +100
-0+/// Set the output value for a line. +pub fn set_output_value_override (&mut self, value:u32 , offset:u32 ) { +unsafe { + bindings::gpiod_line_config_set_output_value_override (self.config, valueas i32 , offset) + } + } + +/// Set the output values for a set of lines. +pub fn set_output_values (&mut self, offsets: &[u32 ], values: &[i32 ]) ->Result <()> { +if offsets.len () != values.len () { +return Err (Error ::OperationFailed ( +"Gpio LineConfig array size mismatch" , +IoError ::new (EINVAL ), + )); + } + +unsafe { + bindings::gpiod_line_config_set_output_values ( + self.config, + values.len ()as c_ulong , + offsets.as_ptr (), + values.as_ptr (), + ); + } + +Ok (()) + } + +/// Clear the output value for a line. +pub fn clear_output_value_override (&mut self, offset:u32 ) { +unsafe { bindings::gpiod_line_config_clear_output_value_override (self.config, offset) } + } + +/// Check if the output value is overridden for a line. +pub fn output_value_is_overridden (&self, offset:u32 ) ->bool { +unsafe { bindings::gpiod_line_config_output_value_is_overridden (self.config, offset) } + } + +/// Get the default output value, 0 or 1. +pub fn get_output_value_default (&self) ->Result <u32 > { +let value =unsafe { bindings::gpiod_line_config_get_output_value_default (self.config) }; + +if value !=0 && value !=1 { +Err (Error ::OperationFailed ( +"Gpio LineConfig get-output-value" , +IoError ::last (), + )) + }else { +Ok (valueas u32 ) + } + } + +/// Get the output value configured for a given line, 0 or 1. +pub fn get_output_value_offset (&self, offset:u32 ) ->Result <u32 > { +let value = +unsafe { bindings::gpiod_line_config_get_output_value_offset (self.config, offset) }; + +if value !=0 && value !=1 { +Err (Error ::OperationFailed ( +"Gpio LineConfig get-output-value" , +IoError ::last (), + )) + }else { +Ok (valueas u32 ) + } + } + +/// Get the list of overridden offsets and the corresponding types of overridden settings. +pub fn get_overrides (&self) ->Result <Vec <(u32 ,Config )>> { +let num =unsafe { bindings::gpiod_line_config_get_num_overrides (self.config) }as usize ; +if num ==0 { +return Ok (Vec ::new ()); + } + +let mut overrides =Vec ::new (); +let mut offset =vec! [0_u32 ; num]; +let mut props =vec! [0_i32 ; num]; + +unsafe { + bindings::gpiod_line_config_get_overrides ( + self.config, + offset.as_mut_ptr (), + props.as_mut_ptr (), + ) + }; + +for iin 0 ..num { + overrides.push ((offset[i],Config ::new (props[i]as u32 )?)); + } + +Ok (overrides) + } +} + +impl Drop for LineConfig { +/// Free the line config object and release all associated resources. +fn drop (&mut self) { +unsafe { bindings::gpiod_line_config_free (self.config) } + } +}bindings/rust/src/line_info.rs new file +19
-0@@ -0,0 +1,189 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar <[email protected]> + +use std::convert::TryFrom ; +use std::sync::Arc ; +use std::time::Duration ; +use std::{slice, str}; + +use vmm_sys_util::errno::Error as IoError ; + +use super ::{ + bindings,Bias ,ChipInternal ,Direction ,Drive ,Edge ,Error ,EventClock ,InfoEvent ,Result , +}; + +/// Line info +/// +/// Exposes functions for retrieving kernel information about both requested and
bindings/rust/src/line_info.rs line 29
Quoted 10 lines
bindings/rust/src/line_info.rs new file +10
-0+/// free lines. Line info object contains an immutable snapshot of a line's status. +/// +/// The line info contains all the publicly available information about a +/// line, which does not include the line value. The line must be requested +/// to access the line value. + +pub struct LineInfo { + info: *mut bindings::gpiod_line_info , + ichip:Option <Arc <ChipInternal >>, + free:bool ,
This flag makes no sense - the info always needs to be freed no matter which path, watched or not, was taken to get it from the C API.
Implement a gpiowatch example to demonstrate using this part of the API.
Quoted 15 lines
bindings/rust/src/line_info.rs new file +15
-0+} + +impl LineInfo { + /// Get a snapshot of information about the line and optionally start watching it for changes. + pub(crate) fn new(ichip: Arc<ChipInternal>, offset: u32, watch: bool) -> Result<Self> { + let info = if watch { + unsafe { bindings::gpiod_chip_watch_line_info(ichip.chip(), offset) } + } else { + unsafe { bindings::gpiod_chip_get_line_info(ichip.chip(), offset) } + }; + + if info.is_null() { + return Err(Error::OperationFailed( + "Gpio LineInfo line-info", + IoError::last(),
bindings/rust/src/line_info.rs line 54
Quoted 10 lines
bindings/rust/src/line_info.rs new file +10
-0+ )); + } + +Ok (Self { + info, + ichip:if watch {Some (ichip) }else {None }, + free: watch, + }) + } +
Why bother implementing the get and watch cases in the one function? Move the corresponding path into where it is called in the chip methods and remove this method.
bindings/rust/src/line_info.rs line 64
Quoted 10 lines
bindings/rust/src/line_info.rs new file +10
-0+/// Stop watching the line +pub fn unwatch (&mut self) { +if let Some (ichip) = &self.ichip { +unsafe { + bindings::gpiod_chip_unwatch_line_info (ichip.chip (), self.get_offset ()); + } + self.ichip =None ; + } + } +
This should be a method of the chip, not the info.
Quoted 85 lines
bindings/rust/src/line_info.rs new file +85
-0+/// Get the offset of the line within the GPIO chip. +/// +/// The offset uniquely identifies the line on the chip. The combination of the chip and offset +/// uniquely identifies the line within the system. + +pub fn get_offset (&self) ->u32 { +unsafe { bindings::gpiod_line_info_get_offset (self.info) } + } + +/// Get GPIO line's name. +pub fn get_name (&self) ->Result <&str > { +// SAFETY: The string returned by libgpiod is guaranteed to live as long +// as the `struct LineInfo`. +let name =unsafe { bindings::gpiod_line_info_get_name (self.info) }; +if name.is_null () { +return Err (Error ::NameNotFound ("GPIO line's name" )); + } + +// SAFETY: The string is guaranteed to be valid here. + str::from_utf8 (unsafe { + slice::from_raw_parts (nameas *const u8 , bindings::strlen (name)as usize ) + }) + .map_err (Error ::InvalidString ) + } + +/// Returns True if the line is in use, false otherwise. +/// +/// The user space can't know exactly why a line is busy. It may have been +/// requested by another process or hogged by the kernel. It only matters that +/// the line is used and we can't request it. +pub fn is_used (&self) ->bool { +unsafe { bindings::gpiod_line_info_is_used (self.info) } + } + +/// Get the GPIO line's consumer name. +pub fn get_consumer (&self) ->Result <&str > { +// SAFETY: The string returned by libgpiod is guaranteed to live as long +// as the `struct LineInfo`. +let name =unsafe { bindings::gpiod_line_info_get_consumer (self.info) }; +if name.is_null () { +return Err (Error ::NameNotFound ("GPIO line's consumer name" )); + } + +// SAFETY: The string is guaranteed to be valid here. + str::from_utf8 (unsafe { + slice::from_raw_parts (nameas *const u8 , bindings::strlen (name)as usize ) + }) + .map_err (Error ::InvalidString ) + } + +/// Get the GPIO line's direction. +pub fn get_direction (&self) ->Result <Direction > { +Direction ::new (unsafe { bindings::gpiod_line_info_get_direction (self.info) }as u32 ) + } + +/// Returns true if the line is "active-low", false otherwise. +pub fn is_active_low (&self) ->bool { +unsafe { bindings::gpiod_line_info_is_active_low (self.info) } + } + +/// Get the GPIO line's bias setting. +pub fn get_bias (&self) ->Result <Bias > { +Bias ::new (unsafe { bindings::gpiod_line_info_get_bias (self.info) }as u32 ) + } + +/// Get the GPIO line's drive setting. +pub fn get_drive (&self) ->Result <Drive > { +Drive ::new (unsafe { bindings::gpiod_line_info_get_drive (self.info) }as u32 ) + } + +/// Get the current edge detection setting of the line. +pub fn get_edge_detection (&self) ->Result <Edge > { +Edge ::new (unsafe { bindings::gpiod_line_info_get_edge_detection (self.info) }as u32 ) + } + +/// Get the current event clock setting used for edge event timestamps. +pub fn get_event_clock (&self) ->Result <EventClock > { +EventClock ::new (unsafe { bindings::gpiod_line_info_get_event_clock (self.info) }as u32 ) + } + +/// Returns true if the line is debounced (either by hardware or by the +/// kernel software debouncer), false otherwise. +pub fn is_debounced (&self) ->bool { +unsafe { bindings::gpiod_line_info_is_debounced (self.info) } + }
bindings/rust/src/line_info.rs line 159
Quoted 10 lines
bindings/rust/src/line_info.rs new file +10
-0+ +/// Get the debounce period of the line. +pub fn get_debounce_period (&self) ->Duration { +Duration ::from_micros (unsafe { + bindings::gpiod_line_info_get_debounce_period_us (self.info) + }) + } +} + +impl TryFrom <&InfoEvent >for LineInfo {
Is try_from appropriate for getting a contained object? "from" normally refers to a type conversion.
Quoted 16 lines
bindings/rust/src/line_info.rs new file +16
-0+type Error =Error ; + +/// Get the Line info object associated with a event. +fn try_from (event: &InfoEvent ) ->Result <Self > { +let info =unsafe { bindings::gpiod_info_event_get_line_info (event.event ()) }; +if info.is_null () { +return Err (Error ::OperationFailed ( +"Gpio LineInfo try-from" , +IoError ::last (), + )); + } + +Ok (Self { + info, + ichip:None , + free:false ,
bindings/rust/src/line_info.rs line 185
Quoted 10 lines
bindings/rust/src/line_info.rs new file +10
-0+ }) + } +} + +impl Drop for LineInfo { +fn drop (&mut self) { +// We must not free the Line info object created from `struct InfoEvent` by calling +// libgpiod API. +if self.free { + self.unwatch ();
Why does dropping a LineInfo unwatch the line???
Quoted 78 lines
bindings/rust/src/line_info.rs new file +4
-0+unsafe { bindings::gpiod_line_info_free (self.info) } + } + } +}bindings/rust/src/line_request.rs new file +68
-0@@ -0,0 +1,248 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar <[email protected]> + +use libc::EINVAL ; +use std::os::raw::c_ulong; +use std::sync::Arc ; +use std::time::Duration ; + +use vmm_sys_util::errno::Error as IoError ; + +use super ::{bindings,ChipInternal ,EdgeEventBuffer ,Error ,LineConfig ,RequestConfig ,Result }; + +/// Line request operations +/// +/// Allows interaction with a set of requested lines. +pub struct LineRequest { + request: *mut bindings::gpiod_line_request , +} + +impl LineRequest { +/// Request a set of lines for exclusive usage. +pub (crate )fn new ( + ichip: &Arc <ChipInternal >, + rconfig: &RequestConfig , + lconfig: &LineConfig , + ) ->Result <Self > { +let request =unsafe { + bindings::gpiod_chip_request_lines (ichip.chip (), rconfig.config (), lconfig.config ()) + }; + +if request.is_null () { +return Err (Error ::OperationFailed ( +"Gpio LineRequest request-lines" , +IoError ::last (), + )); + } + +Ok (Self { request }) + } + +/// Get the number of lines in the request. +pub fn get_num_lines (&self) ->u32 { +unsafe { bindings::gpiod_line_request_get_num_lines (self.request)as u32 } + } + +/// Get the offsets of lines in the request. +pub fn get_offsets (&self) ->Vec <u32 > { +let mut offsets =vec! [0 ; self.get_num_lines ()as usize ]; + +unsafe { bindings::gpiod_line_request_get_offsets (self.request, offsets.as_mut_ptr ()) }; + offsets + } + +/// Get the value (0 or 1) of a single line associated with the request. +pub fn get_value (&self, offset:u32 ) ->Result <u32 > { +let value =unsafe { bindings::gpiod_line_request_get_value (self.request, offset) }; + +if value !=0 && value !=1 { +Err (Error ::OperationFailed ( +"Gpio LineRequest get-value" , +IoError ::last (), + )) + }else { +Ok (valueas u32 ) + } + }
bindings/rust/src/line_request.rs line 78
Quoted 10 lines
bindings/rust/src/line_request.rs new file +10
-0+ +/// Get values of a subset of lines associated with the request. +pub fn get_values_subset (&self, offsets: &[u32 ], values: &mut Vec <i32 >) ->Result <()> { +if offsets.len () != values.len () { +return Err (Error ::OperationFailed ( +"Gpio LineRequest array size mismatch" , +IoError ::new (EINVAL ), + )); + } +
Returned values are awkward to use as the user has to index into them using the index corresponding to the offset in offsets. Provide a Values type that maps offset to value, e.g. using an IntMap, and pass that in instead of separate offsets and values arrays.
Same applies to set_values_subset().
Quoted 18 lines
bindings/rust/src/line_request.rs new file +18
-0+let ret =unsafe { + bindings::gpiod_line_request_get_values_subset ( + self.request, + offsets.len ()as c_ulong , + offsets.as_ptr (), + values.as_mut_ptr (), + ) + }; + +if ret == -1 { +Err (Error ::OperationFailed ( +"Gpio LineRequest get-values-subset" , +IoError ::last (), + )) + }else { +Ok (()) + } + }
bindings/rust/src/line_request.rs line 106
Quoted 10 lines
bindings/rust/src/line_request.rs new file +10
-0+ +/// Get values of all lines associated with the request. +pub fn get_values (&self, values: &mut Vec <i32 >) ->Result <()> { +if values.len () != self.get_num_lines ()as usize { +return Err (Error ::OperationFailed ( +"Gpio LineRequest array size mismatch" , +IoError ::new (EINVAL ), + )); + } +
Incorporating the get_values_subset() comments above, this becomes a special case of that, so merge the two into one function.
Similarly the sets.
Quoted 103 lines
bindings/rust/src/line_request.rs new file +103
-0+let ret = +unsafe { bindings::gpiod_line_request_get_values (self.request, values.as_mut_ptr ()) }; + +if ret == -1 { +Err (Error ::OperationFailed ( +"Gpio LineRequest get-values" , +IoError ::last (), + )) + }else { +Ok (()) + } + } + +/// Set the value of a single line associated with the request. +pub fn set_value (&self, offset:u32 , value:i32 ) ->Result <()> { +let ret =unsafe { bindings::gpiod_line_request_set_value (self.request, offset, !!value) }; + +if ret == -1 { +Err (Error ::OperationFailed ( +"Gpio LineRequest set-value" , +IoError ::last (), + )) + }else { +Ok (()) + } + } + +/// Get values of a subset of lines associated with the request. +pub fn set_values_subset (&self, offsets: &[u32 ], values: &[i32 ]) ->Result <()> { +if offsets.len () != values.len () { +return Err (Error ::OperationFailed ( +"Gpio LineRequest array size mismatch" , +IoError ::new (EINVAL ), + )); + } + +let ret =unsafe { + bindings::gpiod_line_request_set_values_subset ( + self.request, + offsets.len ()as c_ulong , + offsets.as_ptr (), + values.as_ptr (), + ) + }; + +if ret == -1 { +Err (Error ::OperationFailed ( +"Gpio LineRequest set-values-subset" , +IoError ::last (), + )) + }else { +Ok (()) + } + } + +/// Get values of all lines associated with the request. +pub fn set_values (&self, values: &[i32 ]) ->Result <()> { +if values.len () != self.get_num_lines ()as usize { +return Err (Error ::OperationFailed ( +"Gpio LineRequest array size mismatch" , +IoError ::new (EINVAL ), + )); + } + +let ret =unsafe { bindings::gpiod_line_request_set_values (self.request, values.as_ptr ()) }; + +if ret == -1 { +Err (Error ::OperationFailed ( +"Gpio LineRequest set-values" , +IoError ::last (), + )) + }else { +Ok (()) + } + } + +/// Update the configuration of lines associated with the line request. +pub fn reconfigure_lines (&self, lconfig: &LineConfig ) ->Result <()> { +let ret =unsafe { + bindings::gpiod_line_request_reconfigure_lines (self.request, lconfig.config ()) + }; + +if ret == -1 { +Err (Error ::OperationFailed ( +"Gpio LineRequest reconfigure-lines" , +IoError ::last (), + )) + }else { +Ok (()) + } + } + +/// Get the file descriptor associated with the line request. +pub fn get_fd (&self) ->u32 { +unsafe { bindings::gpiod_line_request_get_fd (self.request)as u32 } + } + +/// Wait for edge events on any of the lines associated with the request. +pub fn wait_edge_event (&self, timeout:Duration ) ->Result <()> { +let ret =unsafe { + bindings::gpiod_line_request_wait_edge_event (self.request, timeout.as_nanos ()as i64 ) + }; +
bindings/rust/src/line_request.rs line 219
Quoted 10 lines
bindings/rust/src/line_request.rs new file +10
-0+match ret { + -1 =>Err (Error ::OperationFailed ( +"Gpio LineRequest edge-event-wait" , +IoError ::last (), + )), +0 =>Err (Error ::OperationTimedOut ), + _ =>Ok (()), + } + } +
Same comment on Duration/blocking as per info events. Same comment on return type as per info events.
bindings/rust/src/line_request.rs line 223
Quoted 4 lines
bindings/rust/src/line_request.rs new file +4
-0+/// Get a number of edge events from a line request. +/// +/// This function will block if no event was queued for the line. +pub fn read_edge_event (&self, buffer: &EdgeEventBuffer , max_events:u32 ) ->Result <u32 > {
Should be read_edge_events() as it reads multiple. (same comment applies to the C API, btw)
max_events should be derived from buffer len (assuming EdgeEventBuffer has a Vec-like API for len and capacity).
Quoted 130 lines
bindings/rust/src/line_request.rs new file +25
-0+let ret =unsafe { + bindings::gpiod_line_request_read_edge_event ( + self.request, + buffer.buffer (), + max_eventsas c_ulong , + ) + }; + +if ret == -1 { +Err (Error ::OperationFailed ( +"Gpio LineRequest edge-event-read" , +IoError ::last (), + )) + }else { +Ok (retas u32 ) + } + } +} + +impl Drop for LineRequest { +/// Release the requested lines and free all associated resources. +fn drop (&mut self) { +unsafe { bindings::gpiod_line_request_release (self.request) } + } +}bindings/rust/src/request_config.rs new file +99
-0@@ -0,0 +1,122 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar <[email protected]> + +use std::os::raw::{c_char, c_ulong}; +use std::{slice, str}; + +use vmm_sys_util::errno::Error as IoError ; + +use super ::{bindings,Error ,Result }; + +/// Request configuration objects +/// +/// Request config objects are used to pass a set of options to the kernel at +/// the time of the line request. Similarly to the line-config - the mutators +/// don't return error values. If the values are invalid, in general they are +/// silently adjusted to acceptable ranges. + +pub struct RequestConfig { + config: *mut bindings::gpiod_request_config , +} + +impl RequestConfig { +/// Create a new request config object. +pub fn new () ->Result <Self > { +let config =unsafe { bindings::gpiod_request_config_new () }; +if config.is_null () { +return Err (Error ::OperationFailed ( +"Gpio RequestConfig new" , +IoError ::last (), + )); + } + +Ok (Self { config }) + } + +/// Private helper, Returns gpiod_request_config +pub (crate )fn config (&self) -> *mut bindings::gpiod_request_config { + self.config + } + +/// Set the consumer name for the request. +/// +/// If the consumer string is too long, it will be truncated to the max +/// accepted length. +pub fn set_consumer (&self, consumer: &str ) { +// Null-terminate the string +let consumer = consumer.to_owned () +"\0" ; + +unsafe { + bindings::gpiod_request_config_set_consumer ( + self.config, + consumer.as_ptr ()as *const c_char , + ) + } + } + +/// Get the consumer name configured in the request config. +pub fn get_consumer (&self) ->Result <&str > { +// SAFETY: The string returned by libgpiod is guaranteed to live as long +// as the `struct RequestConfig`. +let consumer =unsafe { bindings::gpiod_request_config_get_consumer (self.config) }; +if consumer.is_null () { +return Err (Error ::OperationFailed ( +"Gpio RequestConfig get-consumer" , +IoError ::last (), + )); + } + +// SAFETY: The string is guaranteed to be valid here. + str::from_utf8 (unsafe { + slice::from_raw_parts (consumeras *const u8 , bindings::strlen (consumer)as usize ) + }) + .map_err (Error ::InvalidString ) + } + +/// Set the offsets of the lines to be requested. +/// +/// If too many offsets were specified, the offsets above the limit accepted +/// by the kernel (64 lines) are silently dropped. +pub fn set_offsets (&self, offsets: &[u32 ]) { +unsafe { + bindings::gpiod_request_config_set_offsets ( + self.config, + offsets.len ()as c_ulong , + offsets.as_ptr (), + ) + } + } + +/// Get the offsets of lines in the request config. +pub fn get_offsets (&self) ->Vec <u32 > { +let num =unsafe { bindings::gpiod_request_config_get_num_offsets (self.config) }; +let mut offsets =vec! [0 ; numas usize ]; + +unsafe { bindings::gpiod_request_config_get_offsets (self.config, offsets.as_mut_ptr ()) }; + offsets + }
bindings/rust/src/request_config.rs line 109
Quoted 10 lines
bindings/rust/src/request_config.rs new file +10
-0+ +/// Set the size of the kernel event buffer for the request. +/// +/// The kernel may adjust the value if it's too high. If set to 0, the +/// default value will be used. +pub fn set_event_buffer_size (&self, size:u32 ) { +unsafe { + bindings::gpiod_request_config_set_event_buffer_size (self.config, sizeas c_ulong ) + } + }
The kernel may adjust the value regardless - this value is a tentative suggestion to the kernel (the kernel buffers have to be sized in 2^N, so it generally rounds up to the next power of 2, within limits).
bindings/rust/src/request_config.rs line 114
Quoted 5 lines
bindings/rust/src/request_config.rs new file +5
-0+ +/// Get the edge event buffer size for the request config. +pub fn get_event_buffer_size (&self) ->u32 { +unsafe { bindings::gpiod_request_config_get_event_buffer_size (self.config)as u32 } + }
You might want to note that this just reads the value from the config. The actual value used by the kernel is not made available to user space.
Quoted 11 lines
bindings/rust/src/request_config.rs new file +8
-0+} + +impl Drop for RequestConfig { +/// Free the request config object and release all associated resources. +fn drop (&mut self) { +unsafe { bindings::gpiod_request_config_free (self.config) } + } +}Signature · 1 line
2.31.1.272.g89b43f80a514
Cheers,
Kent.
Re: [PATCH V4 4/8] libgpiod: Add rust wrapper crate
Quoted 5 lines · Kent Gibson
bindings/rust/src/chip.rs line 85
Quoted 2 lines · Viresh Kumar
bindings/rust/src/chip.rs new file +2
-0+/// Get the path used to find the chip. +pub fn get_path (&self) ->Result <&str > {
Quoted 7 lines
It seems absurd that a method that simply returns the path provided to
open()requires a Result, but that is a consequence of wrapping.I was considering suggesting caching a copy in struct Chip, but all the other methods require Results so at least this is consistent :-(.
Yay, more unwrapping than Xmas past.
:)
Quoted 5 lines
bindings/rust/src/chip.rs line 88
Quoted 3 lines
bindings/rust/src/chip.rs new file +3
-0+// SAFETY: The string returned by libgpiod is guaranteed to live as long +// as the `struct Chip`. +let path =unsafe { bindings::gpiod_chip_get_path (self.ichip.chip ()) };Trusting that it is never NULL?
I believe we discussed that early on (few months back) and decided this will never be NULL and since the Rust wrappers are pretty much part of the same repository, we can take that as a guarantee. An out-of-this-repository user can't really assume that.
Quoted 1 line
Add a null check to be sure.
But I am fine with this as well.
Quoted 5 lines
bindings/rust/src/chip.rs line 128
Quoted 2 lines
bindings/rust/src/chip.rs new file +2
-0+/// Wait for line status events on any of the watched lines on the chip. +pub fn wait_info_event (&self, timeout:Duration ) ->Result <()> {Durations cannot be negative, so caller cannot make this block indefinitely. Make timeout an Option? (no timeout => block)
I didn't know we want the always blocking capability as well. Yeah, Option sounds like the right approach.
So in that case we just pass -1 to gpiod_chip_wait_info_event() ?
Quoted 14 lines
bindings/rust/src/chip_info.rs line 17
Quoted 10 lines
bindings/rust/src/chip.rs new file +3
-0+let ret =unsafe { + bindings::gpiod_chip_wait_info_event (self.ichip.chip (), timeout.as_nanos ()as i64 ) + };bindings/rust/src/chip_info.rs +5
-0+/// GPIO chip Information +pub struct ChipInfo { + info: *mut bindings::gpiod_chip_info , +} +Consider modules and namespaces rather than lumping everything in the gpiod space.
Quoted 1 line
e.g.
gpiod::ChipInfo->gpiod::chip::Info
The modules are already there, as file names. So what we really have is gpiod::chip_info::ChipInfo (yeah it isn't great for sure). But then it looked tougher/complex/unnecessary for end users to know the internals of a dependency and so I did this:
pub use crate::chip::*;
pub use crate::edge_event::*;
pub use crate::event_buffer::*;
pub use crate::info_event::*;
pub use crate::line_config::*;
pub use crate::line_info::*;
pub use crate::line_request::*;
pub use crate::request_config::*;
which puts everything under gpiod::*. I think it is easier for users that way. The modules are fine for in-crate management, but for end user they shouldn't be visible.
Quoted 9 lines
bindings/rust/src/lib.rs line 56
Quoted 7 lines
bindings/rust/src/lib.rs +5
-0+/// Error codes for libgpiod operations +#[derive( Copy , Clone , Debug , PartialEq , ThisError )] +pub enum Error { +#[error( "Operation {0} Failed: {1}" )] +OperationFailed (&'staticstr ,IoError ),Use an enum for the operation rather than a string?
Not sure I understood this.
Quoted 14 lines
And if it returns an IoError it must be an IoOperation? Else if the IoError is just an errno then call it that.
Quoted 11 lines
bindings/rust/src/lib.rs +1
-0+}bindings/rust/src/line_info.rs +8
-0+/// Line info +/// +/// Exposes functions for retrieving kernel information about both requested and +/// free lines. Line info object contains an immutable snapshot of a line's status. +/// +/// The line info contains all the publicly available information about a +/// line, which does not include the line value. The line must be requested +/// to access the line value.
Quoted 8 lines
bindings/rust/src/line_info.rs line 29
Quoted 5 lines
bindings/rust/src/line_info.rs +5
-0+ +pub struct LineInfo { + info: *mut bindings::gpiod_line_info , + ichip:Option <Arc <ChipInternal >>, + free:bool ,This flag makes no sense - the info always needs to be freed no matter which path, watched or not, was taken to get it from the C API.
Not the one created with gpiod_info_event_get_line_info(), else it will result in double free.
Quoted 11 lines
bindings/rust/src/line_info.rs line 64
Quoted 10 lines
bindings/rust/src/line_info.rs +10
-0+/// Stop watching the line +pub fn unwatch (&mut self) { +if let Some (ichip) = &self.ichip { +unsafe { + bindings::gpiod_chip_unwatch_line_info (ichip.chip (), self.get_offset ()); + } + self.ichip =None ; + } + } +
Quoted 1 line
This should be a method of the chip, not the info.
I think there were some issues with my understanding of the whole watch thing. Is there any existing example of gpiowatch somewhere ?
Quoted 4 lines
Quoted 1 line
+impl TryFrom<&InfoEvent> for LineInfo {
Is
try_fromappropriate for getting a contained object? "from" normally refers to a type conversion.
Not sure, but as most of new() is going away here, I will likely move this to info_event as well. No try-from with that.
Quoted 8 lines
bindings/rust/src/line_info.rs line 185
Quoted 6 lines
bindings/rust/src/line_info.rs +6
-0+impl Drop for LineInfo { +fn drop (&mut self) { +// We must not free the Line info object created from `struct InfoEvent` by calling +// libgpiod API. +if self.free { + self.unwatch ();Why does dropping a LineInfo unwatch the line???
Because I thought it is related to the Line's info and we won't wanna watch once line info is gone. Again, it is my wrong interpretation.
Quoted 10 lines
bindings/rust/src/line_info.rs
Quoted 9 lines
bindings/rust/src/line_info.rs +9
-0+/// Get values of a subset of lines associated with the request. +pub fn get_values_subset (&self, offsets: &[u32 ], values: &mut Vec <i32 >) ->Result <()> { +if offsets.len () != values.len () { +return Err (Error ::OperationFailed ( +"Gpio LineRequest array size mismatch" , +IoError ::new (EINVAL ), + )); + } +
Quoted 4 lines
Returned values are awkward to use as the user has to index into them using the index corresponding to the offset in offsets. Provide a Values type that maps offset to value, e.g. using an IntMap, and pass that in instead of separate offsets and values arrays.
We would need to separate out the offsets then as that's what gpiod_line_request_get_values_subset() needs. Maybe take offsets, like now, as an argument and return Result<IntMap> ?
Quoted 1 line
Same applies to
set_values_subset().
That would be fine here though.
Quoted 14 lines
bindings/rust/src/line_info.rs
Quoted 9 lines
bindings/rust/src/line_info.rs +9
-0+/// Set the size of the kernel event buffer for the request. +/// +/// The kernel may adjust the value if it's too high. If set to 0, the +/// default value will be used. +pub fn set_event_buffer_size (&self, size:u32 ) { +unsafe { + bindings::gpiod_request_config_set_event_buffer_size (self.config, sizeas c_ulong ) + } + }The kernel may adjust the value regardless - this value is a tentative suggestion to the kernel (the kernel buffers have to be sized in 2^N, so it generally rounds up to the next power of 2, within limits).
Quoted 8 lines
bindings/rust/src/line_info.rs
Quoted 5 lines
bindings/rust/src/line_info.rs +5
-0+ +/// Get the edge event buffer size for the request config. +pub fn get_event_buffer_size (&self) ->u32 { +unsafe { bindings::gpiod_request_config_get_event_buffer_size (self.config)as u32 } + }You might want to note that this just reads the value from the config. The actual value used by the kernel is not made available to user space.
Do you want me to add these two comments for the above two routines ?
Other comments, which I removed, look acceptable to me. Will try to incorporate all that in the next version.
Thanks a lot for the very thorough review.
Signature · 1 line
viresh
Re: [PATCH V4 4/8] libgpiod: Add rust wrapper crate
Quoted 22 lines · Viresh Kumar
Quoted 5 lines · Kent Gibson
bindings/rust/src/chip.rs line 85
Quoted 2 lines · Viresh Kumar
bindings/rust/src/chip.rs new file +2
-0+/// Get the path used to find the chip. +pub fn get_path (&self) ->Result <&str > {Quoted 7 lines
It seems absurd that a method that simply returns the path provided to
open()requires a Result, but that is a consequence of wrapping.I was considering suggesting caching a copy in struct Chip, but all the other methods require Results so at least this is consistent :-(.
Yay, more unwrapping than Xmas past.
:)
Quoted 5 lines
bindings/rust/src/chip.rs line 88
Quoted 3 lines
bindings/rust/src/chip.rs new file +3
-0+// SAFETY: The string returned by libgpiod is guaranteed to live as long +// as the `struct Chip`. +let path =unsafe { bindings::gpiod_chip_get_path (self.ichip.chip ()) };Trusting that it is never NULL?
Quoted 9 lines
I believe we discussed that early on (few months back) and decided this will never be NULL and since the Rust wrappers are pretty much part of the same repository, we can take that as a guarantee. An out-of-this-repository user can't really assume that.
Quoted 1 line
Add a null check to be sure.
But I am fine with this as well.
It should never return NULL. At the moment.
I would prefer to have NULL checks for all cases, not assume anything about the C implementation, and to be consistent with other places where you do NULL checks. As it stands when I see this I need to go check the C to see if this is a reasonable exception or not. And I'm lazy.
Quoted 41 lines
Quoted 5 lines
bindings/rust/src/chip.rs line 128
Quoted 2 lines
bindings/rust/src/chip.rs new file +2
-0+/// Wait for line status events on any of the watched lines on the chip. +pub fn wait_info_event (&self, timeout:Duration ) ->Result <()> {Durations cannot be negative, so caller cannot make this block indefinitely. Make timeout an Option? (no timeout => block)
I didn't know we want the always blocking capability as well. Yeah, Option sounds like the right approach.
So in that case we just pass -1 to
gpiod_chip_wait_info_event()?Quoted 14 lines
bindings/rust/src/chip_info.rs line 17
Quoted 10 lines
bindings/rust/src/chip.rs new file +3
-0+let ret =unsafe { + bindings::gpiod_chip_wait_info_event (self.ichip.chip (), timeout.as_nanos ()as i64 ) + };bindings/rust/src/chip_info.rs +5
-0+/// GPIO chip Information +pub struct ChipInfo { + info: *mut bindings::gpiod_chip_info , +} +Consider modules and namespaces rather than lumping everything in the gpiod space.
Quoted 1 line
e.g.
gpiod::ChipInfo->gpiod::chip::InfoThe modules are already there, as file names. So what we really have is
gpiod::chip_info::ChipInfo(yeah it isn't great for sure). But then it looked tougher/complex/unnecessary for end users to know the internals of a dependency and so I did this:pub use crate::chip::*;
pub use crate::edge_event::*;
pub use crate::event_buffer::*;
pub use crate::info_event::*;
pub use crate::line_config::*;
pub use crate::line_info::*;
pub use crate::line_request::*;
pub use crate::request_config::*;
Quoted 4 lines
which puts everything under gpiod::*. I think it is easier for users that way. The modules are fine for in-crate management, but for end user they shouldn't be visible.
The main problem I have with putting everything in the top level is the generated docs. You get everything dumped on you, so all structs, enums and functions, and it isn't clear to the user what the logical starting point is.
If things are tiered then you can introduce them more gradually, or keep them out of their way if they are unlikely to use them (e.g. ChipInfo, InfoEvent).
Quoted 5 lines
Quoted 4 lines
Quoted 4 lines
bindings/rust/src/lib.rs +3
-0+/// Error codes for libgpiod operations +#[derive( Copy , Clone , Debug , PartialEq , ThisError )] +pub enum Error {
Quoted 7 lines
Quoted 4 lines
bindings/rust/src/lib.rs line 56
Quoted 2 lines
bindings/rust/src/lib.rs +2
-0+#[error( "Operation {0} Failed: {1}" )] + OperationFailed(&'static str, IoError),Use an enum for the operation rather than a string?
Not sure I understood this.
Rather than passing a static str, define an enum with a variant to identify the operation. Like std::io::ErrorKind (not sure that is the best example).
Quoted 23 lines
Quoted 14 lines
And if it returns an IoError it must be an IoOperation? Else if the IoError is just an errno then call it that.
Quoted 11 lines
bindings/rust/src/lib.rs +1
-0+}bindings/rust/src/line_info.rs +8
-0+/// Line info +/// +/// Exposes functions for retrieving kernel information about both requested and +/// free lines. Line info object contains an immutable snapshot of a line's status. +/// +/// The line info contains all the publicly available information about a +/// line, which does not include the line value. The line must be requested +/// to access the line value. Quoted 8 lines
bindings/rust/src/line_info.rs line 29
Quoted 5 lines
bindings/rust/src/line_info.rs +5
-0+ +pub struct LineInfo { + info: *mut bindings::gpiod_line_info , + ichip:Option <Arc <ChipInternal >>, + free:bool ,This flag makes no sense - the info always needs to be freed no matter which path, watched or not, was taken to get it from the C API.
Quoted 3 lines
Not the one created with
gpiod_info_event_get_line_info(), else it will result in double free.
Ah, down in the try_from. Fair enough. Though the other two cases both need to be freed. And I would prefer the field name changed to match the EdgeEvent - which ever way you go with that.
Quoted 11 lines
Quoted 11 lines
Quoted 10 lines
bindings/rust/src/line_info.rs +10
-0+/// Stop watching the line +pub fn unwatch (&mut self) { +if let Some (ichip) = &self.ichip { +unsafe { + bindings::gpiod_chip_unwatch_line_info (ichip.chip (), self.get_offset ()); + } + self.ichip =None ; + } + } +
Quoted 5 lines
Quoted 1 line
This should be a method of the chip, not the info.
I think there were some issues with my understanding of the whole watch thing. Is there any existing example of gpiowatch somewhere ?
There is one in my proposed tool changes for v2[1]
[1] https://lore.kernel.org/linux-gpio/[email protected]/
Quoted 15 lines
Quoted 4 lines
Quoted 1 line
+impl TryFrom<&InfoEvent> for LineInfo {
Is
try_fromappropriate for getting a contained object? "from" normally refers to a type conversion.Not sure, but as most of
new()is going away here, I will likely move this toinfo_eventas well. No try-from with that.Quoted 7 lines
Quoted 6 lines
bindings/rust/src/line_info.rs +6
-0+impl Drop for LineInfo { +fn drop (&mut self) { +// We must not free the Line info object created from `struct InfoEvent` by calling +// libgpiod API. +if self.free { + self.unwatch ();
Quoted 5 lines
Quoted 1 line
Why does dropping a LineInfo unwatch the line???
Because I thought it is related to the Line's info and we won't wanna watch once line info is gone. Again, it is my wrong interpretation.
Yeah, no. You can get LineInfoChange events from the chip similar to the EdgeEvents from a line request. unwatching when here is akin to disabling edge detection when you free an edge event.
Watching and unwatching line info are at chip scope, and your bindings should never unwatch themselves - only wrap the C and provide a method on the Chip for the user to call.
Quoted 10 lines
Quoted 10 lines
Quoted 9 lines
bindings/rust/src/line_info.rs +9
-0+/// Get values of a subset of lines associated with the request. +pub fn get_values_subset (&self, offsets: &[u32 ], values: &mut Vec <i32 >) ->Result <()> { +if offsets.len () != values.len () { +return Err (Error ::OperationFailed ( +"Gpio LineRequest array size mismatch" , +IoError ::new (EINVAL ), + )); + } +
Quoted 9 lines
Quoted 4 lines
Returned values are awkward to use as the user has to index into them using the index corresponding to the offset in offsets. Provide a Values type that maps offset to value, e.g. using an IntMap, and pass that in instead of separate offsets and values arrays.
We would need to separate out the offsets then as that's what
gpiod_line_request_get_values_subset()needs. Maybe take offsets, like now, as an argument and return Result<IntMap> ?
Don't use IntMap raw, use it as the basis of a Values type.
You can pull the keys from the map to determine which lines to get. If the map is empty get them all.
Hmmm, that is assuming you have the requested offsets on hand at that point.
Quoted 24 lines
Quoted 1 line
Same applies to
set_values_subset().That would be fine here though.
Quoted 20 lines
bindings/rust/src/line_info.rs
Quoted 9 lines
bindings/rust/src/line_info.rs +9
-0+/// Set the size of the kernel event buffer for the request. +/// +/// The kernel may adjust the value if it's too high. If set to 0, the +/// default value will be used. +pub fn set_event_buffer_size (&self, size:u32 ) { +unsafe { + bindings::gpiod_request_config_set_event_buffer_size (self.config, sizeas c_ulong ) + } + }The kernel may adjust the value regardless - this value is a tentative suggestion to the kernel (the kernel buffers have to be sized in 2^N, so it generally rounds up to the next power of 2, within limits).
Quoted 5 lines
bindings/rust/src/line_info.rs +5
-0+ +/// Get the edge event buffer size for the request config. +pub fn get_event_buffer_size (&self) ->u32 { +unsafe { bindings::gpiod_request_config_get_event_buffer_size (self.config)as u32 } + }
Quoted 5 lines
Quoted 2 lines
You might want to note that this just reads the value from the config. The actual value used by the kernel is not made available to user space.
Do you want me to add these two comments for the above two routines ?
Not add verbatim. Maybe work them in.
Probably should look at revising the C API comments and then just mirror that.
Quoted 5 lines
Other comments, which I removed, look acceptable to me. Will try to incorporate all that in the next version.
Thanks a lot for the very thorough review.
No problem. Thanks for the effort you've put into them.
Cheers,
Kent.
Re: [PATCH V4 4/8] libgpiod: Add rust wrapper crate
Quoted 6 lines · Kent Gibson
It should never return NULL. At the moment.
I would prefer to have NULL checks for all cases, not assume anything about the C implementation, and to be consistent with other places where you do NULL checks. As it stands when I see this I need to go check the C to see if this is a reasonable exception or not. And I'm lazy.
Ideally the C side would document the guarantees explicitly instead, and then the Rust side can rely on them.
In any case, if a given C API never returned an invalid pointer and suddenly it starts doing so in some cases, I would consider that a breaking change in practice, which would likely break C users too.
A potential compromise meanwhile is debug_assert! to at least test those assumptions.
Cheers,
Miguel
Re: [PATCH V4 4/8] libgpiod: Add rust wrapper crate
Quoted 11 lines · Miguel Ojeda
Quoted 6 lines · Kent Gibson
It should never return NULL. At the moment.
I would prefer to have NULL checks for all cases, not assume anything about the C implementation, and to be consistent with other places where you do NULL checks. As it stands when I see this I need to go check the C to see if this is a reasonable exception or not. And I'm lazy.Ideally the C side would document the guarantees explicitly instead, and then the Rust side can rely on them.
Unfortunately the C header doesn't currently provide any guarantee - except in the cases where it CAN return NULL.
But we can fix that.
Quoted 4 lines
In any case, if a given C API never returned an invalid pointer and suddenly it starts doing so in some cases, I would consider that a breaking change in practice, which would likely break C users too.
Not sure I'm onboard with that. Unless the API has a contract not to return a NULL then it is free to at a later date. The user should always assume that NULL is a possibility, even if they have never seen one.
But in practice you are probably right.
Quoted 3 lines
A potential compromise meanwhile is
debug_assert!to at least test those assumptions.
I'd be fine with that.
I'd also be satisfied with a comment in the Rust that the C guarantees a non-NULL where that is the case. That would at least demonstrate that the possibility has been duly considered.
Cheers,
Kent.
Re: [PATCH V4 4/8] libgpiod: Add rust wrapper crate
Quoted 4 lines · Kent Gibson
Unfortunately the C header doesn't currently provide any guarantee - except in the cases where it CAN return NULL.
But we can fix that.
Yeah, fixing that is what I was suggesting, since it is a possibility here, and would improve things for C users too.
Quoted 6 lines
Not sure I'm onboard with that. Unless the API has a contract not to return a NULL then it is free to at a later date. The user should always assume that NULL is a possibility, even if they have never seen one.
But in practice you are probably right.
I definitely agree that a client should aim to avoid assuming anything.
However, if we are strict, given C pointers are unconstrained, all pointers would be useless unless told otherwise, because checking for NULL is not a guarantee of validity either.
Also, if an C API just says "returns the name", for instance, it is reasonable to assume it is a valid name because it is not said otherwise (e.g. it does not say "returns the name, if available" nor "returns an optional name").
And, of course, eventually consumers will end up relying on your particular implementation no matter what, and returning invalid pointers where there weren't before is a very dangerous idea for a C library.
Quoted 4 lines
I'd be fine with that.
I'd also be satisfied with a comment in the Rust that the C guarantees a non-NULL where that is the case. That would at least demonstrate that the possibility has been duly considered.
I think the current SAFETY comment already intends to imply that, but yeah, it could be clarified.
In any case, I would say it always returns a valid pointer, not "non-NULL", since the latter does not really show it is a valid pointer (it could point to a non-NULL, bad address).
Cheers,
Miguel
Re: [PATCH V4 4/8] libgpiod: Add rust wrapper crate
Quoted 27 lines · Miguel Ojeda
Quoted 4 lines · Kent Gibson
Unfortunately the C header doesn't currently provide any guarantee - except in the cases where it CAN return NULL.
But we can fix that.Yeah, fixing that is what I was suggesting, since it is a possibility here, and would improve things for C users too.
Quoted 6 lines
Not sure I'm onboard with that. Unless the API has a contract not to return a NULL then it is free to at a later date. The user should always assume that NULL is a possibility, even if they have never seen one.
But in practice you are probably right.
I definitely agree that a client should aim to avoid assuming anything.
However, if we are strict, given C pointers are unconstrained, all pointers would be useless unless told otherwise, because checking for NULL is not a guarantee of validity either.
Also, if an C API just says "returns the name", for instance, it is reasonable to assume it is a valid name because it is not said otherwise (e.g. it does not say "returns the name, if available" nor "returns an optional name").
Quoted 5 lines
And, of course, eventually consumers will end up relying on your particular implementation no matter what, and returning invalid pointers where there weren't before is a very dangerous idea for a C library.
All true, but practically speaking the only cases we need to be concerned with here are NULL and valid. NULL is the only one we can detect for certain, and we have no alternative but to assume valid if not.
Quoted 8 lines
Quoted 4 lines
I'd be fine with that.
I'd also be satisfied with a comment in the Rust that the C guarantees a non-NULL where that is the case. That would at least demonstrate that the possibility has been duly considered.I think the current
SAFETYcomment already intends to imply that, but yeah, it could be clarified.
The comment is:
// SAFETY: The string is guaranteed to be valid here.
and that is whether there a NULL check or not, so it isn't clear what the source of the guarantee is.
I would prefer:
// SAFETY: The string is guaranteed to be valid by the C API.
and updating the C header to explicitly state it returns a valid pointer. It currently says "Pointer to a human-readable string" which could be taken to mean valid, but making it "Valid pointer to..." would more clearly place the onus of it actually being valid on the C library.
Cheers,
Kent.
Quoted 6 lines
In any case, I would say it always returns a valid pointer, not "non-NULL", since the latter does not really show it is a valid pointer (it could point to a non-NULL, bad address).
Cheers,
Miguel
Re: [PATCH V4 4/8] libgpiod: Add rust wrapper crate
Quoted 9 lines · Kent Gibson
The comment is:
// SAFETY: The string is guaranteed to be valid here.and that is whether there a NULL check or not, so it isn't clear what the source of the guarantee is.
I would prefer:// SAFETY: The string is guaranteed to be valid by the C API.
I believe this is what we settled with now. I will make updates accordingly.
Quoted 4 lines
and updating the C header to explicitly state it returns a valid pointer. It currently says "Pointer to a human-readable string" which could be taken to mean valid, but making it "Valid pointer to..." would more clearly place the onus of it actually being valid on the C library.
I will let you guys handle the C API :)
Signature · 1 line
viresh
Re: [PATCH V4 4/8] libgpiod: Add rust wrapper crate
In reply to Kent GibsonQuoted 6 lines · Kent Gibson
Quoted 6 lines
Quoted 6 lines
Quoted 5 lines
bindings/rust/src/request_config.rs new file +5
-0+ +/// Get the edge event buffer size for the request config. +pub fn get_event_buffer_size (&self) ->u32 { +unsafe { bindings::gpiod_request_config_get_event_buffer_size (self.config)as u32 } + }
Quoted 9 lines
Quoted 5 lines
Quoted 2 lines
You might want to note that this just reads the value from the config. The actual value used by the kernel is not made available to user space.
Do you want me to add these two comments for the above two routines ?
Not add verbatim. Maybe work them in.
Probably should look at revising the C API comments and then just mirror that.
Just to be clear, I don't think we want to get into the details as to how the kernel interprets that setting, so that is just informational for you in case you find the kernel doesn't buffer exactly the number you expected. The description for the set covers the case where you might get less than you expected. But in some cases you may get more.
The key difference is that the C API description says:
@return Edge event buffer size setting from the request config.
^^^^^^^
which indicates you are getting the setting value back, not whatever the buffer size in the kernel actually is.
That is missing from the one-liner description, which is all you copied. So if you are going to condense it to one line then add "setting" in there.
Cheers,
Kent.
Re: [PATCH V4 4/8] libgpiod: Add rust wrapper crate
In reply to Kent GibsonQuoted 27 lines · Kent Gibson
Quoted 21 lines · Viresh Kumar
Quoted 5 lines · Kent Gibson
On Fri, Jul 08, 2022 at 05:04:57PM +0530, Viresh Kumar wrote:
Consider modules and namespaces rather than lumping everything in the gpiod space.e.g.
gpiod::ChipInfo->gpiod::chip::InfoThe modules are already there, as file names. So what we really have is
gpiod::chip_info::ChipInfo(yeah it isn't great for sure). But then it looked tougher/complex/unnecessary for end users to know the internals of a dependency and so I did this:pub use crate::chip::*;
pub use crate::edge_event::*;
pub use crate::event_buffer::*;
pub use crate::info_event::*;
pub use crate::line_config::*;
pub use crate::line_info::*;
pub use crate::line_request::*;
pub use crate::request_config::*;Quoted 4 lines
which puts everything under gpiod::*. I think it is easier for users that way. The modules are fine for in-crate management, but for end user they shouldn't be visible.
Quoted 7 lines
The main problem I have with putting everything in the top level is the generated docs. You get everything dumped on you, so all structs, enums and functions, and it isn't clear to the user what the logical starting point is.
If things are tiered then you can introduce them more gradually, or keep them out of their way if they are unlikely to use them (e.g. ChipInfo, InfoEvent).
I am not sure what to do about this. I was suggested earlier to dump it all at the top level namespace so it is easier/shorter for users, which also won't need to know the internal namespaces/modules of libgpiod.
Looking at structures, there are just 8 of them which are exposed in docs now, which isn't a lot really. And then we have 12 enums now.
Signature · 1 line
viresh
Re: [PATCH V4 4/8] libgpiod: Add rust wrapper crate
Quoted 36 lines · Viresh Kumar
Quoted 27 lines · Kent Gibson
Quoted 21 lines · Viresh Kumar
Quoted 5 lines · Kent Gibson
On Fri, Jul 08, 2022 at 05:04:57PM +0530, Viresh Kumar wrote:
Consider modules and namespaces rather than lumping everything in the gpiod space.e.g.
gpiod::ChipInfo->gpiod::chip::InfoThe modules are already there, as file names. So what we really have is
gpiod::chip_info::ChipInfo(yeah it isn't great for sure). But then it looked tougher/complex/unnecessary for end users to know the internals of a dependency and so I did this:pub use crate::chip::*;
pub use crate::edge_event::*;
pub use crate::event_buffer::*;
pub use crate::info_event::*;
pub use crate::line_config::*;
pub use crate::line_info::*;
pub use crate::line_request::*;
pub use crate::request_config::*;Quoted 4 lines
which puts everything under gpiod::*. I think it is easier for users that way. The modules are fine for in-crate management, but for end user they shouldn't be visible.
Quoted 7 lines
The main problem I have with putting everything in the top level is the generated docs. You get everything dumped on you, so all structs, enums and functions, and it isn't clear to the user what the logical starting point is.
If things are tiered then you can introduce them more gradually, or keep them out of their way if they are unlikely to use them (e.g. ChipInfo, InfoEvent).
Quoted 4 lines
I am not sure what to do about this. I was suggested earlier to dump it all at the top level namespace so it is easier/shorter for users, which also won't need to know the internal namespaces/modules of libgpiod.
libgpiod has no namespaces/modules - cos it is C.
The users do need to understand how things fit together, and the flat namespace doesn't help there - everything is equal.
True, "_" is shorter than "::". Can't argue with that ;-).
In Rust the user can rename the import whatever they want, so I'm not sure the "easier/shorter" argument holds much water.
Quoted 3 lines
Looking at structures, there are just 8 of them which are exposed in docs now, which isn't a lot really. And then we have 12 enums now.
This isn't a question of numbers, it is a question of whether to indicate structure, or to flatten everything.
I find the structured approach both clearer and more idiomatic in Rust, so that gets my vote.
Cheers,
Kent.
Re: [PATCH V4 4/8] libgpiod: Add rust wrapper crate
Quoted 2 lines · Kent Gibson
True, "_" is shorter than "::". Can't argue with that ;-).
Not sure about that in non-monospace fonts -- just kidding ;P
(Though I know an experienced C programmer that uses non-monospaced fonts for code)
Quoted 2 lines
I find the structured approach both clearer and more idiomatic in Rust, so that gets my vote.
Yeah, we are also following that approach in the kernel.
Cheers,
Miguel
Re: [PATCH V4 4/8] libgpiod: Add rust wrapper crate
In reply to Kent GibsonQuoted 9 lines · Kent Gibson
Quoted 1 line · Viresh Kumar
+ /// Get the number of events the buffers stores.
stores -> contains
The capacity indicates the number that can be stored. This field indicates the number the buffer currently contains.
Quoted 6 lines
And, as mentioned above, rename to
len(), and return value should be usize.Add a
set_len()method to replace themax_eventsparameter inLineRequest::read_edge_event()to handle the unusual case where the user only wants to partially fill the buffer.
So capacity is max that can be stored, len is what is currently present. What read_edge_event() needs is how much we want it to read, which should be <= capacity.
If we remove the parameter to read_edge_event(), then it must fetch the value from the buffer itself, which should be buffer.capacity() if user hasn't called set_len(), else it will be that special value the user sets.
What do you want to call the value (to be added as a field to struct EdgeEventBuffer) and the helper routine to fetch it ?
Maybe we should name it "max_events" and call the helpers as max_events() and set_max_events() instead of set_len()?
Quoted 4 lines
Quoted 4 lines
bindings/rust/src/event_buffer.rs new file +4
-0+pub fn get_num_events (&self) ->u32 { +unsafe { bindings::gpiod_edge_event_buffer_get_num_events (self.buffer ())as u32 } + } +}
Signature · 1 line
viresh
Re: [PATCH V4 4/8] libgpiod: Add rust wrapper crate
Quoted 17 lines · Viresh Kumar
Quoted 9 lines · Kent Gibson
Quoted 1 line · Viresh Kumar
+ /// Get the number of events the buffers stores.
stores -> contains
The capacity indicates the number that can be stored. This field indicates the number the buffer currently contains.
Quoted 6 lines
And, as mentioned above, rename to
len(), and return value should be usize.Add a
set_len()method to replace themax_eventsparameter inLineRequest::read_edge_event()to handle the unusual case where the user only wants to partially fill the buffer.
Quoted 8 lines
So capacity is max that can be stored, len is what is currently present. What
read_edge_event()needs is how much we want it to read, which should be <= capacity.If we remove the parameter to
read_edge_event(), then it must fetch the value from the buffer itself, which should bebuffer.capacity()if user hasn't calledset_len(), else it will be that special value the user sets.
Ah, so the the problem there is after the read_edge_event() the len() is supposed to indicate the number of events in the buffer, so if we use that to size the read then the user will have to re-set_len() on each read
- so we are effectively back to providing the length to read on each call.
So not a good plan - my bad.
Quoted 6 lines
What do you want to call the value (to be added as a field to struct EdgeEventBuffer) and the helper routine to fetch it ?
Maybe we should name it "
max_events" and call the helpers asmax_events()andset_max_events()instead ofset_len()?
I'm not even sure it makes sense to have an option to partially fill the buffer. It is there in the C as we have to provide the buffer size, and so can always elect to provide a smaller size, but I'm not sure there is any use case for it - certainly none that I am aware of. Perhaps we should always fill the buffer to capacity.
Then we can stick with the Vec len/capacity pattern, so capacity() indicates how many can be written during a read and len() indicates how many are available in the buffer. No set_len() required.
The goal was to provide an interface that is more idiomatic than just wrapping the C API verbatim, so the idea was to provide something with a Vec feel.
That is what I tried to do with mine[1], though that totally hides the filling of the buffer from the user - filling it on demand when the user requests an event (the buffer also being an iterator) when the buffer is empty.
Something like that may be doable using the libpgiod API.
Kent. [1]https://warthog618.github.io/gpiocdev-rs/gpiocdev/request/struct.EdgeEventBuffer.html
PATCH v4 5/8 libgpiod: Add rust examples
In reply to Viresh KumarAdd examples for the usage of the rust bindings, quite similar to the ones in cxx bindings.
--- bindings/rust/examples/gpiodetect.rs | 37 ++++++++++++ bindings/rust/examples/gpiofind.rs | 42 +++++++++++++ bindings/rust/examples/gpioget.rs | 42 +++++++++++++ bindings/rust/examples/gpioinfo.rs | 89 ++++++++++++++++++++++++++++ bindings/rust/examples/gpiomon.rs | 68 +++++++++++++++++++++ bindings/rust/examples/gpioset.rs | 52 ++++++++++++++++ 6 files changed, 330 insertions(+) create mode 100644 bindings/rust/examples/gpiodetect.rs create mode 100644 bindings/rust/examples/gpiofind.rs create mode 100644 bindings/rust/examples/gpioget.rs create mode 100644 bindings/rust/examples/gpioinfo.rs create mode 100644 bindings/rust/examples/gpiomon.rs create mode 100644 bindings/rust/examples/gpioset.rs
bindings/rust/examples/gpiodetect.rs new file +37 -0
@@ -0,0 +1,37 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar <[email protected]> +// +// Simplified Rust implementation of gpiodetect tool. + +use std::env; +use std::fs; +use std::path::Path ; + +use libgpiod::{gpiod_is_gpiochip_device,Chip }; + +fn main () { +let args:Vec <String > = env::args ().collect (); +if args.len () >1 { +println! ("Usage: {}" , args[0 ]); +return ; + } + +for entryin fs::read_dir (Path ::new ("/dev" )).unwrap () { +let pathbuf = entry.unwrap ().path (); +let path = pathbuf.to_str ().unwrap (); + +if gpiod_is_gpiochip_device (path) { +let chip =Chip ::open (path).unwrap (); +let ngpio = chip.get_num_lines (); + +println! ( +"{} [{}] ({})" , + chip.get_name ().unwrap (), + chip.get_label ().unwrap (), + ngpio + ); + } + } +}
bindings/rust/examples/gpiofind.rs new file +42 -0
42 lines not on this page · raw message
bindings/rust/examples/gpioget.rs new file +42 -0
42 lines not on this page · raw message
bindings/rust/examples/gpioinfo.rs new file +89 -0
89 lines not on this page · raw message
bindings/rust/examples/gpiomon.rs new file +68 -0
68 lines not on this page · raw message
bindings/rust/examples/gpioset.rs new file +52 -0
52 lines not on this page · raw message
Signature · 1 line
2.31.1.272.g89b43f80a514
Re: [PATCH V4 5/8] libgpiod: Add rust examples
Quoted 36 lines · Viresh Kumar
Add examples for the usage of the rust bindings, quite similar to the ones in cxx bindings.
Signed-off-by Viresh Kumar <[email protected]>--- bindings/rust/examples/gpiodetect.rs | 37 ++++++++++++ bindings/rust/examples/gpiofind.rs | 42 +++++++++++++ bindings/rust/examples/gpioget.rs | 42 +++++++++++++ bindings/rust/examples/gpioinfo.rs | 89 ++++++++++++++++++++++++++++ bindings/rust/examples/gpiomon.rs | 68 +++++++++++++++++++++ bindings/rust/examples/gpioset.rs | 52 ++++++++++++++++ 6 files changed, 330 insertions(+) create mode 100644 bindings/rust/examples/gpiodetect.rs create mode 100644 bindings/rust/examples/gpiofind.rs create mode 100644 bindings/rust/examples/gpioget.rs create mode 100644 bindings/rust/examples/gpioinfo.rs create mode 100644 bindings/rust/examples/gpiomon.rs create mode 100644 bindings/rust/examples/gpioset.rsbindings/rust/examples/gpiodetect.rs new file +11
-0@@ -0,0 +1,37 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar <[email protected]> +// +// Simplified Rust implementation of gpiodetect tool. + +use std::env; +use std::fs; +use std::path::Path ; +
bindings/rust/examples/gpiodetect.rs line 21
Quoted 10 lines
bindings/rust/examples/gpiodetect.rs new file +10
-0+use libgpiod::{gpiod_is_gpiochip_device,Chip }; + +fn main () { +let args:Vec <String > = env::args ().collect (); +if args.len () >1 { +println! ("Usage: {}" , args[0 ]); +return ; + } + +for entryin fs::read_dir (Path ::new ("/dev" )).unwrap () {
use .flatten() to have the iterator unwrap the entry so it is actually an entry, not a Result.
bindings/rust/examples/gpiodetect.rs line 24
Quoted 3 lines
bindings/rust/examples/gpiodetect.rs new file +3
-0+let pathbuf = entry.unwrap ().path (); +let path = pathbuf.to_str ().unwrap (); +
is_gpiochip_device() and Chip::open() (and ChipInternal) should accept anything that can be converted into a &Path, e.g. a PathBuf, so the path variable becomes redundant.
e.g.
pub(crate) fn open<P: AsRef<std::path::Path>>(path: &P) -> Result<Self> {
// Null-terminate the string
let path = path.as_ref().to_string_lossy() + "\0";
...and then example code becomes:
for entry in fs::read_dir(Path::new("/dev")).unwrap().flatten() {
let path = entry.path();
if gpiod_is_gpiochip_device(&path) {
let chip = Chip::open(&path).unwrap();
...(renaming pathbuf to path)
Similarly other examples.
bindings/rust/examples/gpiodetect.rs line 28
Quoted 4 lines
bindings/rust/examples/gpiodetect.rs new file +4
-0+if gpiod_is_gpiochip_device (path) { +let chip =Chip ::open (path).unwrap (); +let ngpio = chip.get_num_lines (); +
Why does ngpio get a variable, unlike name and label?
bindings/rust/examples/gpiodetect.rs line 37
Quoted 9 lines
bindings/rust/examples/gpiodetect.rs new file +9
-0+println! ( +"{} [{}] ({})" , + chip.get_name ().unwrap (), + chip.get_label ().unwrap (), + ngpio + ); + } + } +}
Avoid using unwrap(). Have main return a Result and use ?. Not so important here, but below for helper functions returning Results allows the caller to determine how to handle the error. And it reads better.
Quoted 21 lines
bindings/rust/examples/gpiofind.rs new file +15
-0@@ -0,0 +1,42 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar <[email protected]> +// +// Simplified Rust implementation of gpiofind tool. + +use std::env; +use std::fs; +use std::path::Path ; + +use libgpiod::{gpiod_is_gpiochip_device,Chip }; + +fn main () { +let args:Vec <String > = env::args ().collect ();
bindings/rust/examples/gpiofind.rs line 25
Quoted 10 lines
bindings/rust/examples/gpiofind.rs new file +10
-0+if args.len () !=2 { +println! ("Usage: {} <line-name>" , args[0 ]); +return ; + } + +for entryin fs::read_dir (Path ::new ("/dev" )).unwrap () { +let pathbuf = entry.unwrap ().path (); +let path = pathbuf.to_str ().unwrap (); + +if gpiod_is_gpiochip_device (path) {
Perhaps have the bindings provide an iterator that returns the paths of available gpiochips?
Quoted 202 lines
bindings/rust/examples/gpiofind.rs new file +17
-0+let chip =Chip ::open (path).unwrap (); + +let offset = chip.find_line (&args[1 ]); +if offset.is_ok () { +println! ( +"Line {} found: Chip: {}, offset: {}" , + args[1 ], + chip.get_name ().unwrap (), + offset.unwrap () + ); +return ; + } + } + } + +println! ("Failed to find line: {}" , args[1 ]); +}bindings/rust/examples/gpioget.rs new file +42
-0@@ -0,0 +1,42 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar <[email protected]> +// +// Simplified Rust implementation of gpioget tool. + +use std::env; + +use libgpiod::{Chip ,Direction ,LineConfig ,RequestConfig }; + +fn main () { +let args:Vec <String > = env::args ().collect (); +if args.len () <3 { +println! ("Usage: {} <chip> <line_offset0> ..." , args[0 ]); +return ; + } + +let mut config =LineConfig ::new ().unwrap (); +let mut offsets =Vec ::<u32 >::new (); + +for argin &args[2 ..] { +let offset = arg.parse ::<u32 >().unwrap (); + + offsets.push (offset); + config.set_direction_override (Direction ::Input , offset); + } + +let path =format! ("/dev/gpiochip{}" , args[1 ]); +let chip =Chip ::open (&path).unwrap (); + +let rconfig =RequestConfig ::new ().unwrap (); + rconfig.set_consumer (&args[0 ]); + rconfig.set_offsets (&offsets); + +let request = chip.request_lines (&rconfig, &config).unwrap (); + +let mut values:Vec <i32 > =vec! [0 ; offsets.len ()]; + request.get_values (&mut values).unwrap (); + +println! ("{:?}" , values); +}bindings/rust/examples/gpioinfo.rs new file +89
-0@@ -0,0 +1,89 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar <[email protected]> +// +// Simplified Rust implementation of gpioinfo tool. + +use std::env; +use std::fs; +use std::path::Path ; + +use libgpiod::{gpiod_is_gpiochip_device,Chip ,Direction }; + +fn line_info (chip: &Chip , offset:u32 ) { +let info = chip.line_info (offset).unwrap (); +let off = info.get_offset (); + +let name =match info.get_name () { +Ok (name) => name, + _ =>"unused" , + }; + +let consumer =match info.get_consumer () { +Ok (name) => name, + _ =>"unnamed" , + }; + +let low =if info.is_active_low () { +"active-low" + }else { +"active-high" + }; + +let dir =match info.get_direction ().unwrap () { +Direction ::AsIs =>"None" , +Direction ::Input =>"Input" , +Direction ::Output =>"Output" , + }; + +println! ( +"\tline {:>3}\ +\t{:>10}\ +\t{:>10}\ +\t{:>6}\ +\t{:>14}" , + off, name, consumer, dir, low + ); +} + +fn chip_info (path: &str ) { +if gpiod_is_gpiochip_device (path) { +let chip =Chip ::open (path).unwrap (); +let ngpio = chip.get_num_lines (); + +println! ("GPIO Chip name: {}" , chip.get_name ().unwrap ()); +println! ("\tlabel: {}" , chip.get_label ().unwrap ()); +println! ("\tpath: {}" , chip.get_path ().unwrap ()); +println! ("\tngpio: {}\n" , ngpio); + +println! ("\tLine information:" ); + +for offsetin 0 ..ngpio { +line_info (&chip, offset); + } +println! ("\n" ); + } +} + +fn main () { +let args:Vec <String > = env::args ().collect (); +if args.len () >2 { +println! ("Usage: {}" , args[0 ]); +return ; + } + +if args.len () ==1 { +for entryin fs::read_dir (Path ::new ("/dev" )).unwrap () { +let pathbuf = entry.unwrap ().path (); +let path = pathbuf.to_str ().unwrap (); + +chip_info (path); + } + }else { +let index = args[1 ].parse ::<u32 >().unwrap (); +let path =format! ("/dev/gpiochip{}" , index); + +chip_info (&path); + } +}bindings/rust/examples/gpiomon.rs new file +36
-0@@ -0,0 +1,68 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar <[email protected]> +// +// Simplified Rust implementation of the gpiomon tool. + +use std::env; +use std::time::Duration ; + +use libgpiod::{Chip ,Edge ,EdgeEventBuffer ,Error ,LineConfig ,LineEdgeEvent ,RequestConfig }; + +fn usage (name: &str ) { +println! ("Usage: {} <chip> <offset0> ..." , name); +} + +fn main () { +let args:Vec <String > = env::args ().collect (); +if args.len () <3 { +usage (&args[0 ]); +return ; + } + +let mut config =LineConfig ::new ().unwrap (); +let mut offsets =Vec ::<u32 >::new (); + +for argin &args[2 ..] { +let offset = arg.parse ::<u32 >().unwrap (); + + offsets.push (offset); + } + + config.set_edge_detection_default (Edge ::Both ); + +let path =format! ("/dev/gpiochip{}" , args[1 ]); +let chip =Chip ::open (&path).unwrap ();
bindings/rust/examples/gpiomon.rs line 46
Quoted 10 lines
bindings/rust/examples/gpiomon.rs new file +10
-0+ +let rconfig =RequestConfig ::new ().unwrap (); + rconfig.set_offsets (&offsets); + +let buffer =EdgeEventBuffer ::new (1 ).unwrap (); +let request = chip.request_lines (&rconfig, &config).unwrap (); + +loop { +match request.wait_edge_event (Duration ::new (1 ,0 )) { +Err (Error ::OperationTimedOut ) =>continue ,
timeout/continue required as you can't (currently) block indefinitely?
Quoted 69 lines
bindings/rust/examples/gpiomon.rs new file +22
-0+ Err(x) => { +println! ("{:?}" , x); +return ; + } + Ok(()) => (), + } + + let count = request.read_edge_event(&buffer, 1).unwrap(); +if count ==1 { +let event = buffer.get_event (0 ).unwrap (); +println! ( +"line: {} type: {}, time: {:?}" , + event.get_line_offset (), +match event.get_event_type ().unwrap () { +LineEdgeEvent ::Rising =>"Rising" , +LineEdgeEvent ::Falling =>"Falling" , + }, + event.get_timestamp () + ); + } + } +}bindings/rust/examples/gpioset.rs new file +41
-0@@ -0,0 +1,52 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar <[email protected]> +// +// Simplified Rust implementation of the gpioset tool. + +use std::env; + +use libgpiod::{Chip ,Direction ,LineConfig ,RequestConfig }; + +fn usage (name: &str ) { +println! ("Usage: {} <chip> <line_offset0>=<value0> ..." , name); +} + +fn main () { +let args:Vec <String > = env::args ().collect (); +if args.len () <3 { +usage (&args[0 ]); +return ; + } + +let mut config =LineConfig ::new ().unwrap (); +let mut offsets =Vec ::<u32 >::new (); +let mut values =Vec ::<i32 >::new (); + +for argin &args[2 ..] { +let pair:Vec <&str > = arg.split ('=' ).collect (); +if pair.len () !=2 { +usage (&args[0 ]); +return ; + } + +let offset = pair[0 ].parse ::<u32 >().unwrap (); +let value = pair[1 ].parse ::<u32 >().unwrap (); + + offsets.push (offset); + values.push (valueas i32 ); + } + + config.set_direction_default (Direction ::Output );
bindings/rust/examples/gpioset.rs line 51
Quoted 10 lines
bindings/rust/examples/gpioset.rs new file +10
-0+ config.set_output_values (&offsets, &values).unwrap (); + +let path =format! ("/dev/gpiochip{}" , args[1 ]); +let chip =Chip ::open (&path).unwrap (); + +let rconfig =RequestConfig ::new ().unwrap (); + rconfig.set_consumer (&args[0 ]); + rconfig.set_offsets (&offsets); + + chip.request_lines (&rconfig, &config).unwrap ();
Wait rather than exiting immediately?
Quoted 4 lines
bindings/rust/examples/gpioset.rs new file +1
-0+}Signature · 1 line
2.31.1.272.g89b43f80a514
And, as mentioned elsewhere, add a gpiowatch example.
Cheers,
Kent.
Re: [PATCH V4 5/8] libgpiod: Add rust examples
Quoted 9 lines · Kent Gibson
bindings/rust/examples/gpioset.rs line 51
Quoted 6 lines · Viresh Kumar
bindings/rust/examples/gpioset.rs +5
-0+let rconfig =RequestConfig ::new ().unwrap (); + rconfig.set_consumer (&args[0 ]); + rconfig.set_offsets (&offsets); + + chip.request_lines (&rconfig, &config).unwrap ();Wait rather than exiting immediately?
Wait for what exactly ?
Quoted 1 line
And, as mentioned elsewhere, add a gpiowatch example.
Will do, sure. Any existing example of the same will help though.
Signature · 1 line
viresh
Re: [PATCH V4 5/8] libgpiod: Add rust examples
Quoted 11 lines · Viresh Kumar
Quoted 9 lines · Kent Gibson
bindings/rust/examples/gpioset.rs line 51
Quoted 6 lines · Viresh Kumar
bindings/rust/examples/gpioset.rs +5
-0+let rconfig =RequestConfig ::new ().unwrap (); + rconfig.set_consumer (&args[0 ]); + rconfig.set_offsets (&offsets); + + chip.request_lines (&rconfig, &config).unwrap ();Wait rather than exiting immediately?
Quoted 2 lines
Wait for what exactly ?
For long enough for the user to check that the line is set, as there is the possibility that when the line is released it will be set back to its default value and you will get users reporting that your set doesn't work ;-).
So wait for a keypress?
Quoted 4 lines
Quoted 1 line
And, as mentioned elsewhere, add a gpiowatch example.
Will do, sure. Any existing example of the same will help though.
There is an example in my proposed tool changes for v2[1]. It is very similar to gpiomon, just on the chip and with line info instead of edge events.
Though that example is more complicated than you need as it supports named lines and operating over multiple chips.
Hopefully you can see past that.
The basic operation is watch the line, then wait for the chip fd to be readable and read the info changed event from it.
You would only unwatch if you no longer had an interest in the line, which I expect will be the case in the D-BUS daemon that Bart has suggested.
Cheers,
Kent.
Re: [PATCH V4 5/8] libgpiod: Add rust examples
Quoted 6 lines · Kent Gibson
For long enough for the user to check that the line is set, as there is the possibility that when the line is released it will be set back to its default value and you will get users reporting that your set doesn't work ;-).
So wait for a keypress?
I thought we wanted to avoid human-intervention to the tests :)
Can't we just read the values of the lines again here somehow and match ? I don't expect/want the user to do this for running the test. Not to mention, the user may eventually be a bot running the tests for each commit added to the tree.
Signature · 1 line
viresh
Re: [PATCH V4 5/8] libgpiod: Add rust examples
Quoted 10 lines · Viresh Kumar
Quoted 6 lines · Kent Gibson
For long enough for the user to check that the line is set, as there is the possibility that when the line is released it will be set back to its default value and you will get users reporting that your set doesn't work ;-).
So wait for a keypress?
I thought we wanted to avoid human-intervention to the tests :)
This is for an example gpioset, right?
You have tests for your examples?
And even then you could either inject a keypress or just kill the process. (e.g. the tests for the proposed tool changes do that)
Cheers,
Kent.
Quoted 5 lines
Can't we just read the values of the lines again here somehow and match ? I don't expect/want the user to do this for running the test. Not to mention, the user may eventually be a bot running the tests for each commit added to the tree.
Re: [PATCH V4 5/8] libgpiod: Add rust examples
Quoted 1 line · Kent Gibson
This is for an example gpioset, right?
Damn, got confused :(
Signature · 1 line
viresh
PATCH v4 6/8 libgpiod: Derive debug traits for few definitions
In reply to Viresh KumarThese are required for tests, which will be added by a later commit.
--- bindings/rust/src/chip.rs | 5 +++++ bindings/rust/src/chip_info.rs | 1 + bindings/rust/src/lib.rs | 8 +++++++- bindings/rust/src/line_info.rs | 1 + 4 files changed, 14 insertions(+), 1 deletion(-)
bindings/rust/src/chip.rs +5 -0
@@ -21,6 +21,7 @@ use super::{/// character device. It exposes basic information about the chip and allows /// callers to retrieve information about each line, watch lines for state /// changes and make line requests. +#[derive( Debug )] pub (crate )struct ChipInternal { chip: *mut bindings::gpiod_chip , }
@@ -52,11 +53,15 @@ impl Drop for ChipInternal { } } +#[derive( Debug )] pub struct Chip { ichip:Arc <ChipInternal >, info:ChipInfo , } +unsafe impl Send for Chip {} +unsafe impl Sync for Chip {} +impl Chip {/// Find a chip by path. pub fn open (path: &str ) ->Result <Self > {
bindings/rust/src/chip_info.rs +1 -0
@@ -11,6 +11,7 @@ use vmm_sys_util::errno::Error as IoError;use super ::{bindings,ChipInternal ,Error ,Result };/// GPIO chip Information +#[derive( Debug )] pub struct ChipInfo { info: *mut bindings::gpiod_chip_info , }
bindings/rust/src/lib.rs +7 -1
@@ -59,6 +59,7 @@ pub enum Error { }/// Direction settings. +#[derive( Debug , PartialEq )] pub enum Direction {/// Request the line(s), but don't change direction. AsIs ,
@@ -88,6 +89,7 @@ impl Direction { }/// Internal bias settings. +#[derive( Debug , PartialEq )] pub enum Bias {/// Don't change the bias setting when applying line config. AsIs ,
@@ -125,6 +127,7 @@ impl Bias { }/// Drive settings. +#[derive( Debug , PartialEq )] pub enum Drive {/// Drive setting is push-pull. PushPull ,
@@ -154,6 +157,7 @@ impl Drive { }/// Edge detection settings. +#[derive( Debug , PartialEq )] pub enum Edge {/// Line edge detection is disabled. None ,
@@ -223,6 +227,7 @@ impl Config { }/// Event clock settings. +#[derive( Debug , PartialEq )] pub enum EventClock {/// Line uses the monotonic clock for edge event timestamps. Monotonic ,
@@ -248,6 +253,7 @@ impl EventClock { }/// Line status change event types. +#[derive( Debug , PartialEq )] pub enum Event {/// Line has been requested. LineRequested ,
@@ -268,7 +274,7 @@ impl Event { } } -#[derive( Copy , Clone )] +#[derive( Copy , Clone , Debug , PartialEq )] /// Edge event types. pub enum LineEdgeEvent {/// Rising edge event.
bindings/rust/src/line_info.rs +1 -0
@@ -23,6 +23,7 @@ use super::{/// line, which does not include the line value. The line must be requested /// to access the line value. +#[derive( Debug )] pub struct LineInfo { info: *mut bindings::gpiod_line_info , ichip:Option <Arc <ChipInternal >>,
Signature · 1 line
2.31.1.272.g89b43f80a514
Re: [PATCH V4 6/8] libgpiod: Derive debug traits for few definitions
Quoted 2 lines · Viresh Kumar
These are required for tests, which will be added by a later commit.
Squash this patch into patch 4, as you know you will need them eventually. All public types should implement Debug[1].
They should also implement Clone, Eq, PartialEq and Default where that makes sense. And Copy if you are sure you wont add something non-Copyable to them in the future.
Cheers,
Kent.
[1] https://rust-lang.github.io/api-guidelines/debuggability.html
Quoted 124 lines
Signed-off-by Viresh Kumar <[email protected]>--- bindings/rust/src/chip.rs | 5 +++++ bindings/rust/src/chip_info.rs | 1 + bindings/rust/src/lib.rs | 8 +++++++- bindings/rust/src/line_info.rs | 1 + 4 files changed, 14 insertions(+), 1 deletion(-)bindings/rust/src/chip.rs +5
-0@@ -21,6 +21,7 @@ use super::{/// character device. It exposes basic information about the chip and allows /// callers to retrieve information about each line, watch lines for state /// changes and make line requests. +#[derive( Debug )] pub (crate )struct ChipInternal { chip: *mut bindings::gpiod_chip , }@@ -52,11 +53,15 @@ impl Drop for ChipInternal { } } +#[derive( Debug )] pub struct Chip { ichip:Arc <ChipInternal >, info:ChipInfo , } +unsafe impl Send for Chip {} +unsafe impl Sync for Chip {} +impl Chip {/// Find a chip by path. pub fn open (path: &str ) ->Result <Self > {bindings/rust/src/chip_info.rs +1
-0@@ -11,6 +11,7 @@ use vmm_sys_util::errno::Error as IoError;use super ::{bindings,ChipInternal ,Error ,Result };/// GPIO chip Information +#[derive( Debug )] pub struct ChipInfo { info: *mut bindings::gpiod_chip_info , }bindings/rust/src/lib.rs +7
-1@@ -59,6 +59,7 @@ pub enum Error { }/// Direction settings. +#[derive( Debug , PartialEq )] pub enum Direction {/// Request the line(s), but don't change direction. AsIs ,@@ -88,6 +89,7 @@ impl Direction { }/// Internal bias settings. +#[derive( Debug , PartialEq )] pub enum Bias {/// Don't change the bias setting when applying line config. AsIs ,@@ -125,6 +127,7 @@ impl Bias { }/// Drive settings. +#[derive( Debug , PartialEq )] pub enum Drive {/// Drive setting is push-pull. PushPull ,@@ -154,6 +157,7 @@ impl Drive { }/// Edge detection settings. +#[derive( Debug , PartialEq )] pub enum Edge {/// Line edge detection is disabled. None ,@@ -223,6 +227,7 @@ impl Config { }/// Event clock settings. +#[derive( Debug , PartialEq )] pub enum EventClock {/// Line uses the monotonic clock for edge event timestamps. Monotonic ,@@ -248,6 +253,7 @@ impl EventClock { }/// Line status change event types. +#[derive( Debug , PartialEq )] pub enum Event {/// Line has been requested. LineRequested ,@@ -268,7 +274,7 @@ impl Event { } } -#[derive( Copy , Clone )] +#[derive( Copy , Clone , Debug , PartialEq )] /// Edge event types. pub enum LineEdgeEvent {/// Rising edge event. bindings/rust/src/line_info.rs +1
-0@@ -23,6 +23,7 @@ use super::{/// line, which does not include the line value. The line must be requested /// to access the line value. +#[derive( Debug )] pub struct LineInfo { info: *mut bindings::gpiod_line_info , ichip:Option <Arc <ChipInternal >>,Signature · 1 line
2.31.1.272.g89b43f80a514
Re: [PATCH V4 6/8] libgpiod: Derive debug traits for few definitions
Quoted 10 lines · Kent Gibson
Quoted 2 lines · Viresh Kumar
These are required for tests, which will be added by a later commit.
Squash this patch into patch 4, as you know you will need them eventually. All public types should implement Debug[1].
They should also implement Clone, Eq, PartialEq and Default where that makes sense. And Copy if you are sure you wont add something non-Copyable to them in the future.
Sure, will define few more for all public structures.
Signature · 1 line
viresh
PATCH v4 7/8 libgpiod: Add rust tests
In reply to Viresh KumarAdd tests for the rust bindings, quite similar to the ones in cxx bindings.
This is how "cargo test" prints results:
Running tests/chip.rs (target/debug/deps/chip-b19008e6b9a10d2f)
running 6 tests
test chip::create::nonexistent_file_failure ... ok test chip::create::no_dev_file_failure ... ok test chip::create::non_gpio_char_dev_file_failure ... ok test chip::create::existing ... ok
test chip::configure::line_lookup ... ok
test chip::configure::verify ... ok
test result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.14s
Running tests/edge_event.rs (target/debug/deps/edge_event-5897a36c91efe0d4)
running 11 tests
test edge_event::buffer_settings::default_capacity ... ok test edge_event::buffer_settings::user_defined_capacity ... ok test edge_event::buffer_settings::max_capacity ... ok test edge_event::failure::dir_out_edge_failure ... ok test edge_event::failure::wait_timeout ... ok
test edge_event::verify::both_edges ... ok
test edge_event::verify::multiple_events ... ok
test edge_event::verify::edge_sequence ... ok
test edge_event::verify::falling_edge ... ok
test edge_event::verify::over_capacity ... ok
test edge_event::verify::rising_edge ... ok
test result: ok. 11 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.98s
Running tests/info_event.rs (target/debug/deps/info_event-707c734e0820381c)
running 3 tests
test info_event::watch::verify ... ok
test info_event::watch::failure ... ok
test info_event::watch::reconfigure ... ok
test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.26s
Running tests/line_config.rs (target/debug/deps/line_config-d376966750bf38a0)
running 9 tests
test line_config::overrides::active_low ... ok test line_config::overrides::bias ... ok test line_config::default::verify ... ok test line_config::overrides::debounce_period ... ok test line_config::overrides::direction ... ok test line_config::overrides::drive ... ok test line_config::overrides::edge_detection ... ok test line_config::overrides::event_clock ... ok test line_config::overrides::output_value ... ok
test result: ok. 9 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.06s
Running tests/line_info.rs (target/debug/deps/line_info-5069196cfaa74ab7)
running 2 tests
[40570.967204] gpio-1992 (hog): hogged as output/high [40570.974774] gpio-1956 (hog4): hogged as output/low [40570.975314] gpio-1955 (hog3): hogged as output/high
test line_info::properties::verify ... ok
test line_info::basic::verify ... ok
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.31s
Running tests/line_request.rs (target/debug/deps/line_request-914b9fd8c84e2c4b)
running 7 tests
test line_request::invalid_arguments::no_offsets ... ok test line_request::verify::read_values ... ok
test line_request::verify::set_bias ... ok
test line_request::verify::reconfigure_output_values ... ok test line_request::verify::empty_consumer ... ok test line_request::verify::set_output_values ... ok test line_request::verify::custom_consumer ... ok
test result: ok. 7 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.48s
Running tests/request_config.rs (target/debug/deps/request_config-99e366517cc0feda)
running 2 tests
test request_config::verify::default ... ok
test request_config::verify::initialized ... ok
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.03s
Doc-tests libgpiod
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
--- bindings/rust/Cargo.toml | 3 + bindings/rust/src/line_request.rs | 1 + bindings/rust/tests/chip.rs | 96 +++++++ bindings/rust/tests/common/config.rs | 117 ++++++++ bindings/rust/tests/common/mod.rs | 16 ++ bindings/rust/tests/common/sim.rs | 306 ++++++++++++++++++++ bindings/rust/tests/edge_event.rs | 389 ++++++++++++++++++++++++++ bindings/rust/tests/info_event.rs | 126 +++++++++ bindings/rust/tests/line_config.rs | 187 +++++++++++++ bindings/rust/tests/line_info.rs | 90 ++++++ bindings/rust/tests/line_request.rs | 234 ++++++++++++++++ bindings/rust/tests/request_config.rs | 42 +++ 12 files changed, 1607 insertions(+) create mode 100644 bindings/rust/tests/chip.rs create mode 100644 bindings/rust/tests/common/config.rs create mode 100644 bindings/rust/tests/common/mod.rs create mode 100644 bindings/rust/tests/common/sim.rs create mode 100644 bindings/rust/tests/edge_event.rs create mode 100644 bindings/rust/tests/info_event.rs create mode 100644 bindings/rust/tests/line_config.rs create mode 100644 bindings/rust/tests/line_info.rs create mode 100644 bindings/rust/tests/line_request.rs create mode 100644 bindings/rust/tests/request_config.rs
bindings/rust/Cargo.toml +3 -0
@@ -10,3 +10,6 @@ libc = ">=0.2.39" libgpiod-sys = { path = "libgpiod-sys" } thiserror = "1.0" vmm-sys-util = "=0.9.0" + +[dev-dependencies] +libgpiod-sys = { path = "libgpiod-sys", features = ["gpiosim"] }
bindings/rust/src/line_request.rs +1 -0
@@ -15,6 +15,7 @@ use super::{bindings, ChipInternal, EdgeEventBuffer, Error, LineConfig, RequestC/// Line request operations /// /// Allows interaction with a set of requested lines. +#[derive( Debug )] pub struct LineRequest { request: *mut bindings::gpiod_line_request , }
bindings/rust/tests/chip.rs new file +96 -0
96 lines not on this page · raw message
bindings/rust/tests/common/config.rs new file +117 -0
117 lines not on this page · raw message
bindings/rust/tests/common/mod.rs new file +16 -0
@@ -0,0 +1,16 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar <[email protected]> + +#[allow(dead_code)] +mod sim; + +#[allow(unused_imports)] +pub (crate )use sim::*; + +#[allow(dead_code)] +mod config; + +#[allow(unused_imports)] +pub (crate )use config::*;
bindings/rust/tests/common/sim.rs new file +306 -0
306 lines not on this page · raw message
bindings/rust/tests/edge_event.rs new file +389 -0
389 lines not on this page · raw message
bindings/rust/tests/info_event.rs new file +126 -0
126 lines not on this page · raw message
bindings/rust/tests/line_config.rs new file +187 -0
187 lines not on this page · raw message
bindings/rust/tests/line_info.rs new file +90 -0
90 lines not on this page · raw message
bindings/rust/tests/line_request.rs new file +234 -0
234 lines not on this page · raw message
bindings/rust/tests/request_config.rs new file +42 -0
42 lines not on this page · raw message
Signature · 1 line
2.31.1.272.g89b43f80a514
Re: [PATCH V4 7/8] libgpiod: Add rust tests
Quoted 5 lines · Viresh Kumar
Add tests for the rust bindings, quite similar to the ones in cxx bindings.
This is how "cargo test" prints results:
Don't include the test results in the commit message.
Those are more of a release artifact than a commit artifact. It is assumed the tests pass for you or you wouldn't be submitting them.
Quoted 133 lines
Running tests/chip.rs (target/debug/deps/chip-b19008e6b9a10d2f)running 6 tests
testchip::create::nonexistent_file_failure... ok testchip::create::no_dev_file_failure... ok testchip::create::non_gpio_char_dev_file_failure... ok testchip::create::existing... ok
testchip::configure::line_lookup... ok
testchip::configure::verify... oktest result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.14s
Running tests/edge_event.rs (target/debug/deps/edge_event-5897a36c91efe0d4)running 11 tests
testedge_event::buffer_settings::default_capacity... ok testedge_event::buffer_settings::user_defined_capacity... ok testedge_event::buffer_settings::max_capacity... ok testedge_event::failure::dir_out_edge_failure... ok testedge_event::failure::wait_timeout... ok
testedge_event::verify::both_edges... ok
testedge_event::verify::multiple_events... ok
testedge_event::verify::edge_sequence... ok
testedge_event::verify::falling_edge... ok
testedge_event::verify::over_capacity... ok
testedge_event::verify::rising_edge... oktest result: ok. 11 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.98s
Running tests/info_event.rs (target/debug/deps/info_event-707c734e0820381c)running 3 tests
testinfo_event::watch::verify... ok
testinfo_event::watch::failure... ok
testinfo_event::watch::reconfigure... oktest result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.26s
Running tests/line_config.rs (target/debug/deps/line_config-d376966750bf38a0)running 9 tests
testline_config::overrides::active_low... ok testline_config::overrides::bias... ok testline_config::default::verify... ok testline_config::overrides::debounce_period... ok testline_config::overrides::direction... ok testline_config::overrides::drive... ok testline_config::overrides::edge_detection... ok testline_config::overrides::event_clock... ok testline_config::overrides::output_value... oktest result: ok. 9 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.06s
Running tests/line_info.rs (target/debug/deps/line_info-5069196cfaa74ab7)running 2 tests
[40570.967204] gpio-1992 (hog): hogged as output/high [40570.974774] gpio-1956 (hog4): hogged as output/low [40570.975314] gpio-1955 (hog3): hogged as output/hightest
line_info::properties::verify... ok
testline_info::basic::verify... oktest result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.31s
Running
tests/line_request.rs(target/debug/deps/line_request-914b9fd8c84e2c4b)running 7 tests
testline_request::invalid_arguments::no_offsets... ok testline_request::verify::read_values... ok
testline_request::verify::set_bias... ok
testline_request::verify::reconfigure_output_values... ok testline_request::verify::empty_consumer... ok testline_request::verify::set_output_values... ok testline_request::verify::custom_consumer... oktest result: ok. 7 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.48s
Running
tests/request_config.rs(target/debug/deps/request_config-99e366517cc0feda)running 2 tests
testrequest_config::verify::default... ok
testrequest_config::verify::initialized... oktest result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.03s
Doc-tests libgpiod
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Signed-off-by Viresh Kumar <[email protected]>--- bindings/rust/Cargo.toml | 3 + bindings/rust/src/line_request.rs | 1 + bindings/rust/tests/chip.rs | 96 +++++++ bindings/rust/tests/common/config.rs | 117 ++++++++ bindings/rust/tests/common/mod.rs | 16 ++ bindings/rust/tests/common/sim.rs | 306 ++++++++++++++++++++ bindings/rust/tests/edge_event.rs | 389 ++++++++++++++++++++++++++ bindings/rust/tests/info_event.rs | 126 +++++++++ bindings/rust/tests/line_config.rs | 187 +++++++++++++ bindings/rust/tests/line_info.rs | 90 ++++++ bindings/rust/tests/line_request.rs | 234 ++++++++++++++++ bindings/rust/tests/request_config.rs | 42 +++ 12 files changed, 1607 insertions(+) create mode 100644 bindings/rust/tests/chip.rs create mode 100644 bindings/rust/tests/common/config.rs create mode 100644 bindings/rust/tests/common/mod.rs create mode 100644 bindings/rust/tests/common/sim.rs create mode 100644 bindings/rust/tests/edge_event.rs create mode 100644 bindings/rust/tests/info_event.rs create mode 100644 bindings/rust/tests/line_config.rs create mode 100644 bindings/rust/tests/line_info.rs create mode 100644 bindings/rust/tests/line_request.rs create mode 100644 bindings/rust/tests/request_config.rsbindings/rust/Cargo.toml +3
-0@@ -10,3 +10,6 @@ libc = ">=0.2.39" libgpiod-sys = { path = "libgpiod-sys" } thiserror = "1.0" vmm-sys-util = "=0.9.0" + +[dev-dependencies] +libgpiod-sys = { path = "libgpiod-sys", features = ["gpiosim"] }bindings/rust/src/line_request.rs +0
-0
bindings/rust/src/line_request.rs use super::{bindings, ChipInternal, EdgeEventBuffer, Error, LineConfig, RequestC · line 21
Quoted 7 lines
bindings/rust/src/line_request.rs +1
-0@@ -15,6 +15,7 @@ use super::{bindings, ChipInternal, EdgeEventBuffer, Error, LineConfig, RequestC/// Line request operations /// /// Allows interaction with a set of requested lines. +#[derive( Debug )] pub struct LineRequest { request: *mut bindings::gpiod_line_request , }
Overlooked in patch 6? Squash that into patch 4 as well.
Quoted 12 lines
bindings/rust/tests/chip.rs new file +6
-0@@ -0,0 +1,96 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar <[email protected]> + +mod common;
bindings/rust/tests/chip.rs line 16
Quoted 10 lines
bindings/rust/tests/chip.rs new file +10
-0+ +mod chip { +use libc::{ENODEV ,ENOENT ,ENOTTY }; + +use vmm_sys_util::errno::Error as IoError ; + +use crate ::common::*; +use libgpiod::{Chip ,Error as ChipError }; + +mod create {
If it is testing the open method then call it "open"?
bindings/rust/tests/chip.rs line 25
Quoted 9 lines
bindings/rust/tests/chip.rs new file +9
-0+use super ::*; + +#[test] +fn nonexistent_file_failure () { +assert_eq! ( +Chip ::open ("/dev/nonexistent" ).unwrap_err (), +ChipError ::OperationFailed ("Gpio Chip open" ,IoError ::new (ENOENT )) + ); + }
The "_failure" suffix doesn't add anything, so remove it.
Quoted 11 lines
bindings/rust/tests/chip.rs new file +11
-0+ +#[test] +fn no_dev_file_failure () { +assert_eq! ( +Chip ::open ("/tmp" ).unwrap_err (), +ChipError ::OperationFailed ("Gpio Chip open" ,IoError ::new (ENOTTY )) + ); + } + +#[test] +fn non_gpio_char_dev_file_failure () {
bindings/rust/tests/chip.rs line 46
Quoted 10 lines
bindings/rust/tests/chip.rs new file +10
-0+assert_eq! ( +Chip ::open ("/dev/null" ).unwrap_err (), +ChipError ::OperationFailed ("Gpio Chip open" ,IoError ::new (ENODEV )) + ); + } + +#[test] +fn existing () { +let sim =Sim ::new (None ,None ,true ).unwrap (); +Chip ::open (sim.dev_path ()).unwrap ();
Test names should indicate what the test covers. "existing" doesn't quite do that for me, though last I checked I passed. YMMV.
Use an assert variant for explicit tests.
Only use unwrap() for intermediate steps that are part of the fixture and are expected to pass unless something is very broken in the test setup. That makes it easier to distinguish the meat from the skeleton.
Quoted 10 lines
bindings/rust/tests/chip.rs new file +10
-0+ } + } + +mod configure { +use super ::*; +const NGPIO :u64 =16 ; +const LABEL : &str ="foobar" ; + +#[test] +fn verify () {
bindings/rust/tests/chip.rs line 66
Quoted 10 lines
bindings/rust/tests/chip.rs new file +10
-0+let sim =Sim ::new (Some (NGPIO ),Some (LABEL ),true ).unwrap (); +let chip =Chip ::open (sim.dev_path ()).unwrap (); + +assert_eq! (chip.get_label ().unwrap (),LABEL ); +assert_eq! (chip.get_name ().unwrap (), sim.chip_name ()); +assert_eq! (chip.get_path ().unwrap (), sim.dev_path ()); +assert_eq! (chip.get_num_lines (),NGPIO as u32 ); + chip.get_fd ().unwrap (); + } +
A test for a basic open on an existing chip and a smoke test of some chip methods is called "verify", so chip::configure::verify?
Quoted 18 lines
bindings/rust/tests/chip.rs new file +18
-0+#[test] +fn line_lookup () { +let sim =Sim ::new (Some (NGPIO ),None ,false ).unwrap (); + sim.set_line_name (0 ,"zero" ).unwrap (); + sim.set_line_name (2 ,"two" ).unwrap (); + sim.set_line_name (3 ,"three" ).unwrap (); + sim.set_line_name (5 ,"five" ).unwrap (); + sim.set_line_name (10 ,"ten" ).unwrap (); + sim.set_line_name (11 ,"ten" ).unwrap (); + sim.enable ().unwrap (); + +let chip =Chip ::open (sim.dev_path ()).unwrap (); + +// Success case +assert_eq! (chip.find_line ("zero" ).unwrap (),0 ); +assert_eq! (chip.find_line ("two" ).unwrap (),2 ); +assert_eq! (chip.find_line ("three" ).unwrap (),3 ); +assert_eq! (chip.find_line ("five" ).unwrap (),5 );
bindings/rust/tests/chip.rs line 94
Quoted 10 lines
bindings/rust/tests/chip.rs new file +10
-0+ +// Success with duplicate names, should return first entry +assert_eq! (chip.find_line ("ten" ).unwrap (),10 ); + +// Failure +assert_eq! ( + chip.find_line ("nonexistent" ).unwrap_err (), +ChipError ::OperationFailed ("Gpio Chip find-line" ,IoError ::new (ENOENT )) + ); + }
If it is testing find_line() then why not call it find_line?
Quoted 10 lines
bindings/rust/tests/chip.rs new file +2
-0+ } +}bindings/rust/tests/common/config.rs new file +2
-0@@ -0,0 +1,117 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +//
bindings/rust/tests/common/config.rs line 12
Quoted 10 lines
bindings/rust/tests/common/config.rs new file +10
-0+// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar <[email protected]> + +use std::sync::Arc ; + +use crate ::common::*; + +use libgpiod::{Bias ,Chip ,Direction ,Edge ,LineConfig ,LineRequest ,RequestConfig ,Result }; + +//#[derive(Debug)]
Uncomment or remove.
Quoted 28 lines
bindings/rust/tests/common/config.rs new file +28
-0+pub (crate )struct TestConfig { + sim:Arc <Sim >, + chip:Option <Chip >, + request:Option <LineRequest >, + rconfig:RequestConfig , + lconfig:LineConfig , +} + +impl TestConfig { +pub (crate )fn new (ngpio:u64 ) ->Result <Self > { +Ok (Self { + sim:Arc ::new (Sim ::new (Some (ngpio),None ,true )?), + chip:None , + request:None , + rconfig:RequestConfig ::new ().unwrap (), + lconfig:LineConfig ::new ().unwrap (), + }) + } + +pub (crate )fn set_pull (&self, offsets: &[u32 ], pulls: &[u32 ]) { +for iin 0 ..pulls.len () { + self.sim.set_pull (offsets[i], pulls[i]as i32 ).unwrap (); + } + } + +pub (crate )fn rconfig_consumer (&self, offsets:Option <&[u32 ]>, consumer:Option <&str >) { +if let Some (offsets) = offsets { + self.rconfig.set_offsets (offsets);
bindings/rust/tests/common/config.rs line 50
Quoted 10 lines
bindings/rust/tests/common/config.rs new file +10
-0+ } + +if let Some (consumer) = consumer { + self.rconfig.set_consumer (consumer); + } + } + +pub (crate )fn rconfig (&self, offsets:Option <&[u32 ]>) { + self.rconfig_consumer (offsets,None ); + }
Almost always called with Some(&offsets), and calling it with None is a NOP.
Where called with Some just call rconfig.set_offsets(). Remove instances of calling with None.
Quoted 73 lines
bindings/rust/tests/common/config.rs new file +67
-0+ +pub (crate )fn lconfig ( + &mut self, + dir:Option <Direction >, + val:Option <u32 >, + val_override:Option <(u32 ,u32 )>, + edge:Option <Edge >, + bias:Option <Bias >, + ) { +if let Some (bias) = bias { + self.lconfig.set_bias_default (bias); + } + +if let Some (edge) = edge { + self.lconfig.set_edge_detection_default (edge); + } + +if let Some (dir) = dir { + self.lconfig.set_direction_default (dir); + } + +if let Some (val) = val { + self.lconfig.set_output_value_default (val); + } + +if let Some ((offset, val)) = val_override { + self.lconfig.set_output_value_override (val, offset); + } + } + +pub (crate )fn lconfig_raw (&mut self) { + self.lconfig (None ,None ,None ,None ,None ); + } + +pub (crate )fn lconfig_edge (&mut self, edge:Option <Edge >) { + self.lconfig (None ,None ,None , edge,None ); + } + +pub (crate )fn request_lines (&mut self) ->Result <()> { +let chip =Chip ::open (self.sim.dev_path ())?; + + self.request =Some (chip.request_lines (&self.rconfig, &self.lconfig)?); + self.chip =Some (chip); + +Ok (()) + } + +pub (crate )fn sim (&self) ->Arc <Sim > { + self.sim.clone () + } + +pub (crate )fn chip (&self) -> &Chip { + &self.chip.as_ref ().unwrap () + } + +pub (crate )fn request (&self) -> &LineRequest { + &self.request.as_ref ().unwrap () + } +} + +impl Drop for TestConfig { +fn drop (&mut self) { +// Explicit freeing is important to make sure "request" get freed +// before "sim" and "chip". + self.request =None ; + } +}bindings/rust/tests/common/mod.rs new file +0
-0
bindings/rust/tests/common/mod.rs line 10
Quoted 10 lines
bindings/rust/tests/common/mod.rs new file +10
-0@@ -0,0 +1,16 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar <[email protected]> + +#[allow(dead_code)] +mod sim; + +#[allow(unused_imports)] +pub (crate )use sim::*;
This allow seems unnecessary.
bindings/rust/tests/common/mod.rs line 16
Quoted 6 lines
bindings/rust/tests/common/mod.rs new file +6
-0+ +#[allow(dead_code)] +mod config; + +#[allow(unused_imports)] +pub (crate )use config::*;
The dead_code allows make me think there is a problem with the way the common test code is structured or there is actual dead code in there.
common/sim.rs should be part of a gpiosim crate??
Quoted 351 lines
bindings/rust/tests/common/sim.rs new file +306
-0@@ -0,0 +1,306 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar <[email protected]> + +use std::os::raw::c_char; +use std::{slice, str}; + +use vmm_sys_util::errno::Error as IoError ; + +use libgpiod::{Error ,Result }; +use libgpiod_sysas bindings; + +/// Sim Ctx +#[derive( Debug )] +struct SimCtx { + ctx: *mut bindings::gpiosim_ctx , +} + +unsafe impl Send for SimCtx {} +unsafe impl Sync for SimCtx {} + +impl SimCtx { +fn new () ->Result <Self > { +let ctx =unsafe { bindings::gpiosim_ctx_new () }; +if ctx.is_null () { +return Err (Error ::OperationFailed ("gpio-sim ctx new" ,IoError ::last ())); + } + +Ok (Self { ctx }) + } + +fn ctx (&self) -> *mut bindings::gpiosim_ctx { + self.ctx + } +} + +impl Drop for SimCtx { +fn drop (&mut self) { +unsafe { bindings::gpiosim_ctx_unref (self.ctx) } + } +} + +/// Sim Dev +#[derive( Debug )] +struct SimDev { + dev: *mut bindings::gpiosim_dev , +} + +unsafe impl Send for SimDev {} +unsafe impl Sync for SimDev {} + +impl SimDev { +fn new (ctx: &SimCtx ) ->Result <Self > { +let dev =unsafe { bindings::gpiosim_dev_new (ctx.ctx ()) }; +if dev.is_null () { +return Err (Error ::OperationFailed ("gpio-sim dev new" ,IoError ::last ())); + } + +Ok (Self { dev }) + } + +fn dev (&self) -> *mut bindings::gpiosim_dev { + self.dev + } + +fn enable (&self) ->Result <()> { +let ret =unsafe { bindings::gpiosim_dev_enable (self.dev) }; + +if ret == -1 { +Err (Error ::OperationFailed ( +"gpio-sim dev-enable" , +IoError ::last (), + )) + }else { +Ok (()) + } + } + +fn disable (&self) ->Result <()> { +let ret =unsafe { bindings::gpiosim_dev_disable (self.dev) }; + +if ret == -1 { +Err (Error ::OperationFailed ( +"gpio-sim dev-disable" , +IoError ::last (), + )) + }else { +Ok (()) + } + } +} + +impl Drop for SimDev { +fn drop (&mut self) { +unsafe { bindings::gpiosim_dev_unref (self.dev) } + } +} + +/// Sim Bank +#[derive( Debug )] +struct SimBank { + bank: *mut bindings::gpiosim_bank , +} + +unsafe impl Send for SimBank {} +unsafe impl Sync for SimBank {} + +impl SimBank { +fn new (dev: &SimDev ) ->Result <Self > { +let bank =unsafe { bindings::gpiosim_bank_new (dev.dev ()) }; +if bank.is_null () { +return Err (Error ::OperationFailed ("gpio-sim Bank new" ,IoError ::last ())); + } + +Ok (Self { bank }) + } + +fn chip_name (&self) ->Result <&str > { +// SAFETY: The string returned by gpiosim is guaranteed to live as long +// as the `struct SimBank`. +let name =unsafe { bindings::gpiosim_bank_get_chip_name (self.bank) }; + +// SAFETY: The string is guaranteed to be valid here. + str::from_utf8 (unsafe { + slice::from_raw_parts (nameas *const u8 , bindings::strlen (name)as usize ) + }) + .map_err (Error ::InvalidString ) + } + +fn dev_path (&self) ->Result <&str > { +// SAFETY: The string returned by gpiosim is guaranteed to live as long +// as the `struct SimBank`. +let path =unsafe { bindings::gpiosim_bank_get_dev_path (self.bank) }; + +// SAFETY: The string is guaranteed to be valid here. + str::from_utf8 (unsafe { + slice::from_raw_parts (pathas *const u8 , bindings::strlen (path)as usize ) + }) + .map_err (Error ::InvalidString ) + } + +fn val (&self, offset:u32 ) ->Result <u32 > { +let ret =unsafe { bindings::gpiosim_bank_get_value (self.bank, offset) }; + +if ret == -1 { +Err (Error ::OperationFailed ( +"gpio-sim get-value" , +IoError ::last (), + )) + }else { +Ok (retas u32 ) + } + } + +fn set_label (&self, label: &str ) ->Result <()> { +// Null-terminate the string +let label = label.to_owned () +"\0" ; + +let ret = +unsafe { bindings::gpiosim_bank_set_label (self.bank, label.as_ptr ()as *const c_char ) }; + +if ret == -1 { +Err (Error ::OperationFailed ( +"gpio-sim set-label" , +IoError ::last (), + )) + }else { +Ok (()) + } + } + +fn set_num_lines (&self, num:u64 ) ->Result <()> { +let ret =unsafe { bindings::gpiosim_bank_set_num_lines (self.bank, num) }; +if ret == -1 { +Err (Error ::OperationFailed ( +"gpio-sim set-num-lines" , +IoError ::last (), + )) + }else { +Ok (()) + } + } + +fn set_line_name (&self, offset:u32 , name: &str ) ->Result <()> { +// Null-terminate the string +let name = name.to_owned () +"\0" ; + +let ret =unsafe { + bindings::gpiosim_bank_set_line_name (self.bank, offset, name.as_ptr ()as *const c_char ) + }; + +if ret == -1 { +Err (Error ::OperationFailed ( +"gpio-sim set-line-name" , +IoError ::last (), + )) + }else { +Ok (()) + } + } + +fn set_pull (&self, offset:u32 , pull:i32 ) ->Result <()> { +let ret =unsafe { bindings::gpiosim_bank_set_pull (self.bank, offset, pull) }; + +if ret == -1 { +Err (Error ::OperationFailed ("gpio-sim set-pull" ,IoError ::last ())) + }else { +Ok (()) + } + } + +fn hog_line (&self, offset:u32 , name: &str , dir:i32 ) ->Result <()> { +// Null-terminate the string +let name = name.to_owned () +"\0" ; + +let ret =unsafe { + bindings::gpiosim_bank_hog_line (self.bank, offset, name.as_ptr ()as *const c_char , dir) + }; + +if ret == -1 { +Err (Error ::OperationFailed ("gpio-sim hog-line" ,IoError ::last ())) + }else { +Ok (()) + } + } +} + +impl Drop for SimBank { +fn drop (&mut self) { +unsafe { bindings::gpiosim_bank_unref (self.bank) } + } +} + +/// GPIO SIM +#[derive( Debug )] +pub (crate )struct Sim { + ctx:SimCtx , + dev:SimDev , + bank:SimBank , +} + +unsafe impl Send for Sim {} +unsafe impl Sync for Sim {} + +impl Sim { +pub (crate )fn new (ngpio:Option <u64 >, label:Option <&str >, enable:bool ) ->Result <Self > { +let ctx =SimCtx ::new ()?; +let dev =SimDev ::new (&ctx)?; +let bank =SimBank ::new (&dev)?; + +if let Some (ngpio) = ngpio { + bank.set_num_lines (ngpio)?; + } + +if let Some (label) = label { + bank.set_label (label)?; + } + +if enable { + dev.enable ()?; + } + +Ok (Self { ctx, dev, bank }) + } + +pub (crate )fn chip_name (&self) -> &str { + self.bank.chip_name ().unwrap () + } + +pub fn dev_path (&self) -> &str { + self.bank.dev_path ().unwrap () + } + +pub (crate )fn val (&self, offset:u32 ) ->Result <u32 > { + self.bank.val (offset) + } + +pub (crate )fn set_label (&self, label: &str ) ->Result <()> { + self.bank.set_label (label) + } + +pub (crate )fn set_num_lines (&self, num:u64 ) ->Result <()> { + self.bank.set_num_lines (num) + } + +pub (crate )fn set_line_name (&self, offset:u32 , name: &str ) ->Result <()> { + self.bank.set_line_name (offset, name) + } + +pub (crate )fn set_pull (&self, offset:u32 , pull:i32 ) ->Result <()> { + self.bank.set_pull (offset, pull) + } + +pub (crate )fn hog_line (&self, offset:u32 , name: &str , dir:i32 ) ->Result <()> { + self.bank.hog_line (offset, name, dir) + } + +pub (crate )fn enable (&self) ->Result <()> { + self.dev.enable () + } + +pub (crate )fn disable (&self) ->Result <()> { + self.dev.disable () + } +}bindings/rust/tests/edge_event.rs new file +33
-0@@ -0,0 +1,389 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar <[email protected]> + +mod common; + +mod edge_event { +use libc::EINVAL ; +use std::sync::Arc ; +use std::thread::{sleep, spawn}; +use std::time::Duration ; + +use vmm_sys_util::errno::Error as IoError ; + +use crate ::common::*; +use libgpiod::{Direction ,Edge ,EdgeEventBuffer ,Error as ChipError ,LineEdgeEvent }; +use libgpiod_sys::{GPIOSIM_PULL_DOWN ,GPIOSIM_PULL_UP }; + +const NGPIO :u64 =8 ; + +mod buffer_settings { +use super ::*; + +#[test] +fn default_capacity () { +assert_eq! (EdgeEventBuffer ::new (0 ).unwrap ().get_capacity (),64 ); + } + +#[test] +fn user_defined_capacity () { +assert_eq! (EdgeEventBuffer ::new (123 ).unwrap ().get_capacity (),123 ); + }
bindings/rust/tests/edge_event.rs line 43
Quoted 10 lines
bindings/rust/tests/edge_event.rs new file +10
-0+ +#[test] +fn max_capacity () { +assert_eq! (EdgeEventBuffer ::new (1024 *2 ).unwrap ().get_capacity (),1024 ); + } + } + +mod failure { +use super ::*; +
Don't see the point of the failure/verify namespace level here.
Quoted 6 lines
bindings/rust/tests/edge_event.rs new file +6
-0+#[test] +fn wait_timeout () { +let mut config =TestConfig ::new (NGPIO ).unwrap (); + config.rconfig (Some (&[0 ])); + config.lconfig_edge (Some (Edge ::Both )); + config.request_lines ().unwrap ();
bindings/rust/tests/edge_event.rs line 59
Quoted 10 lines
bindings/rust/tests/edge_event.rs new file +10
-0+ +// No events available +assert_eq! ( + config + .request () + .wait_edge_event (Duration ::from_millis (100 )) + .unwrap_err (), +ChipError ::OperationTimedOut + ); + }
Is a timeout really a "failure"?
It is testing wait_edge_event(), which is a method of line_request, and so should be in the line_request test suite.
Quoted 4 lines
bindings/rust/tests/edge_event.rs new file +4
-0+ +#[test] +fn dir_out_edge_failure () { +let mut config =TestConfig ::new (NGPIO ).unwrap ();
bindings/rust/tests/edge_event.rs line 73
Quoted 10 lines
bindings/rust/tests/edge_event.rs new file +10
-0+ config.rconfig (Some (&[0 ])); + config.lconfig (Some (Direction ::Output ),None ,None ,Some (Edge ::Both ),None ); + +assert_eq! ( + config.request_lines ().unwrap_err (), +ChipError ::OperationFailed ("Gpio LineRequest request-lines" ,IoError ::new (EINVAL )) + ); + } + } +
This is testing a failure mode of request_lines(), not edge_events. Where is the edge_event here?
Quoted 351 lines
bindings/rust/tests/edge_event.rs new file +316
-0+mod verify { +use super ::*; + +// Helpers to generate events +fn trigger_falling_and_rising_edge (sim:Arc <Sim >, offset:u32 ) { +spawn (move || { +sleep (Duration ::from_millis (30 )); + sim.set_pull (offset,GPIOSIM_PULL_UP as i32 ).unwrap (); + +sleep (Duration ::from_millis (30 )); + sim.set_pull (offset,GPIOSIM_PULL_DOWN as i32 ).unwrap (); + }); + } + +fn trigger_rising_edge_events_on_two_offsets (sim:Arc <Sim >, offset: [u32 ;2 ]) { +spawn (move || { +sleep (Duration ::from_millis (30 )); + sim.set_pull (offset[0 ],GPIOSIM_PULL_UP as i32 ).unwrap (); + +sleep (Duration ::from_millis (30 )); + sim.set_pull (offset[1 ],GPIOSIM_PULL_UP as i32 ).unwrap (); + }); + } + +fn trigger_multiple_events (sim:Arc <Sim >, offset:u32 ) { + sim.set_pull (offset,GPIOSIM_PULL_UP as i32 ).unwrap (); +sleep (Duration ::from_millis (10 )); + + sim.set_pull (offset,GPIOSIM_PULL_DOWN as i32 ).unwrap (); +sleep (Duration ::from_millis (10 )); + + sim.set_pull (offset,GPIOSIM_PULL_UP as i32 ).unwrap (); +sleep (Duration ::from_millis (10 )); + } + +#[test] +fn both_edges () { +const GPIO :u32 =2 ; +let buf =EdgeEventBuffer ::new (0 ).unwrap (); +let mut config =TestConfig ::new (NGPIO ).unwrap (); + config.rconfig (Some (&[GPIO ])); + config.lconfig_edge (Some (Edge ::Both )); + config.request_lines ().unwrap (); + +// Generate events +trigger_falling_and_rising_edge (config.sim (),GPIO ); + +// Rising event + config + .request () + .wait_edge_event (Duration ::from_secs (1 )) + .unwrap (); + +assert_eq! ( + config + .request () + .read_edge_event (&buf, buf.get_capacity ()) + .unwrap (), +1 + ); +assert_eq! (buf.get_num_events (),1 ); + +let event = buf.get_event (0 ).unwrap (); +let ts_rising = event.get_timestamp (); +assert_eq! (event.get_event_type ().unwrap (),LineEdgeEvent ::Rising ); +assert_eq! (event.get_line_offset (),GPIO ); + +// Falling event + config + .request () + .wait_edge_event (Duration ::from_secs (1 )) + .unwrap (); + +assert_eq! ( + config + .request () + .read_edge_event (&buf, buf.get_capacity ()) + .unwrap (), +1 + ); +assert_eq! (buf.get_num_events (),1 ); + +let event = buf.get_event (0 ).unwrap (); +let ts_falling = event.get_timestamp (); +assert_eq! (event.get_event_type ().unwrap (),LineEdgeEvent ::Falling ); +assert_eq! (event.get_line_offset (),GPIO ); + +// No events available +assert_eq! ( + config + .request () + .wait_edge_event (Duration ::from_millis (100 )) + .unwrap_err (), +ChipError ::OperationTimedOut + ); + +assert! (ts_falling > ts_rising); + } + +#[test] +fn rising_edge () { +const GPIO :u32 =6 ; +let buf =EdgeEventBuffer ::new (0 ).unwrap (); +let mut config =TestConfig ::new (NGPIO ).unwrap (); + config.rconfig (Some (&[GPIO ])); + config.lconfig_edge (Some (Edge ::Rising )); + config.request_lines ().unwrap (); + +// Generate events +trigger_falling_and_rising_edge (config.sim (),GPIO ); + +// Rising event + config + .request () + .wait_edge_event (Duration ::from_secs (1 )) + .unwrap (); + +assert_eq! ( + config + .request () + .read_edge_event (&buf, buf.get_capacity ()) + .unwrap (), +1 + ); +assert_eq! (buf.get_num_events (),1 ); + +let event = buf.get_event (0 ).unwrap (); +assert_eq! (event.get_event_type ().unwrap (),LineEdgeEvent ::Rising ); +assert_eq! (event.get_line_offset (),GPIO ); + +// No events available +assert_eq! ( + config + .request () + .wait_edge_event (Duration ::from_millis (100 )) + .unwrap_err (), +ChipError ::OperationTimedOut + ); + } + +#[test] +fn falling_edge () { +const GPIO :u32 =7 ; +let buf =EdgeEventBuffer ::new (0 ).unwrap (); +let mut config =TestConfig ::new (NGPIO ).unwrap (); + config.rconfig (Some (&[GPIO ])); + config.lconfig_edge (Some (Edge ::Falling )); + config.request_lines ().unwrap (); + +// Generate events +trigger_falling_and_rising_edge (config.sim (),GPIO ); + +// Falling event + config + .request () + .wait_edge_event (Duration ::from_secs (1 )) + .unwrap (); + +assert_eq! ( + config + .request () + .read_edge_event (&buf, buf.get_capacity ()) + .unwrap (), +1 + ); +assert_eq! (buf.get_num_events (),1 ); + +let event = buf.get_event (0 ).unwrap (); +assert_eq! (event.get_event_type ().unwrap (),LineEdgeEvent ::Falling ); +assert_eq! (event.get_line_offset (),GPIO ); + +// No events available +assert_eq! ( + config + .request () + .wait_edge_event (Duration ::from_millis (100 )) + .unwrap_err (), +ChipError ::OperationTimedOut + ); + } + +#[test] +fn edge_sequence () { +const GPIO : [u32 ;2 ] = [0 ,1 ]; +let mut config =TestConfig ::new (NGPIO ).unwrap (); + config.rconfig (Some (&GPIO )); + config.lconfig_edge (Some (Edge ::Both )); + config.request_lines ().unwrap (); + +// Generate events +trigger_rising_edge_events_on_two_offsets (config.sim (),GPIO ); + +// Rising event GPIO 0 +let buf =EdgeEventBuffer ::new (0 ).unwrap (); + config + .request () + .wait_edge_event (Duration ::from_secs (1 )) + .unwrap (); + +assert_eq! ( + config + .request () + .read_edge_event (&buf, buf.get_capacity ()) + .unwrap (), +1 + ); +assert_eq! (buf.get_num_events (),1 ); + +let event = buf.get_event (0 ).unwrap (); +assert_eq! (event.get_event_type ().unwrap (),LineEdgeEvent ::Rising ); +assert_eq! (event.get_line_offset (),GPIO [0 ]); +assert_eq! (event.get_global_seqno (),1 ); +assert_eq! (event.get_line_seqno (),1 ); + +// Rising event GPIO 1 + config + .request () + .wait_edge_event (Duration ::from_secs (1 )) + .unwrap (); + +assert_eq! ( + config + .request () + .read_edge_event (&buf, buf.get_capacity ()) + .unwrap (), +1 + ); +assert_eq! (buf.get_num_events (),1 ); + +let event = buf.get_event (0 ).unwrap (); +assert_eq! (event.get_event_type ().unwrap (),LineEdgeEvent ::Rising ); +assert_eq! (event.get_line_offset (),GPIO [1 ]); +assert_eq! (event.get_global_seqno (),2 ); +assert_eq! (event.get_line_seqno (),1 ); + +// No events available +assert_eq! ( + config + .request () + .wait_edge_event (Duration ::from_millis (100 )) + .unwrap_err (), +ChipError ::OperationTimedOut + ); + } + +#[test] +fn multiple_events () { +const GPIO :u32 =1 ; +let buf =EdgeEventBuffer ::new (0 ).unwrap (); +let mut config =TestConfig ::new (NGPIO ).unwrap (); + config.rconfig (Some (&[GPIO ])); + config.lconfig_edge (Some (Edge ::Both )); + config.request_lines ().unwrap (); + +// Generate events +trigger_multiple_events (config.sim (),GPIO ); + +// Read multiple events + config + .request () + .wait_edge_event (Duration ::from_secs (1 )) + .unwrap (); + +assert_eq! ( + config + .request () + .read_edge_event (&buf, buf.get_capacity ()) + .unwrap (), +3 + ); +assert_eq! (buf.get_num_events (),3 ); + +let mut global_seqno =1 ; +let mut line_seqno =1 ; + +// Verify sequence number of events +for iin 0 ..3 { +let event = buf.get_event (i).unwrap (); +assert_eq! (event.get_line_offset (),GPIO ); +assert_eq! (event.get_global_seqno (), global_seqno); +assert_eq! (event.get_line_seqno (), line_seqno); + + global_seqno +=1 ; + line_seqno +=1 ; + } + } + +#[test] +fn over_capacity () { +const GPIO :u32 =2 ; +let buf =EdgeEventBuffer ::new (2 ).unwrap (); +let mut config =TestConfig ::new (NGPIO ).unwrap (); + config.rconfig (Some (&[GPIO ])); + config.lconfig_edge (Some (Edge ::Both )); + config.request_lines ().unwrap (); + +// Generate events +trigger_multiple_events (config.sim (),GPIO ); + +// Read multiple events + config + .request () + .wait_edge_event (Duration ::from_secs (1 )) + .unwrap (); + +assert_eq! ( + config + .request () + .read_edge_event (&buf, buf.get_capacity ()) + .unwrap (), +2 + ); +assert_eq! (buf.get_num_events (),2 ); + } + } +}bindings/rust/tests/info_event.rs new file +29
-0@@ -0,0 +1,126 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar <[email protected]> + +mod common; + +mod info_event { +use libc::EINVAL ; +use std::sync::Arc ; +use std::thread::{sleep, spawn}; +use std::time::Duration ; + +use vmm_sys_util::errno::Error as IoError ; + +use crate ::common::*; +use libgpiod::{Chip ,Direction ,Error as ChipError ,Event ,LineConfig ,RequestConfig }; + +fn request_reconfigure_line (chip:Arc <Chip >) { +spawn (move || { +sleep (Duration ::from_millis (10 )); + +let lconfig1 =LineConfig ::new ().unwrap (); +let rconfig =RequestConfig ::new ().unwrap (); + rconfig.set_offsets (&[7 ]); + +let request = chip.request_lines (&rconfig, &lconfig1).unwrap (); + +sleep (Duration ::from_millis (10 ));
bindings/rust/tests/info_event.rs line 39
Quoted 10 lines
bindings/rust/tests/info_event.rs new file +10
-0+ +let mut lconfig2 =LineConfig ::new ().unwrap (); + lconfig2.set_direction_default (Direction ::Output ); + + request.reconfigure_lines (&lconfig2).unwrap (); + +sleep (Duration ::from_millis (10 )); + }); + } +
Benefit of the background thread?
Quoted 119 lines
bindings/rust/tests/info_event.rs new file +87
-0+mod watch { +use super ::*; +const NGPIO :u64 =8 ; +const GPIO :u32 =7 ; + +#[test] +fn failure () { +let sim =Sim ::new (Some (NGPIO ),None ,true ).unwrap (); +let chip =Chip ::open (sim.dev_path ()).unwrap (); + +assert_eq! ( + chip.watch_line_info (NGPIO as u32 ).unwrap_err (), +ChipError ::OperationFailed ("Gpio LineInfo line-info" ,IoError ::new (EINVAL )) + ); + + chip.watch_line_info (3 ).unwrap (); + +// No events available +assert_eq! ( + chip.wait_info_event (Duration ::from_millis (100 )) + .unwrap_err (), +ChipError ::OperationTimedOut + ); + } + +#[test] +fn verify () { +let sim =Sim ::new (Some (NGPIO ),None ,true ).unwrap (); +let chip =Chip ::open (sim.dev_path ()).unwrap (); +let info = chip.watch_line_info (GPIO ).unwrap (); + +assert_eq! (info.get_offset (),GPIO ); + } + +#[test] +fn reconfigure () { +let sim =Sim ::new (Some (NGPIO ),None ,true ).unwrap (); +let chip =Arc ::new (Chip ::open (sim.dev_path ()).unwrap ()); +let info = chip.watch_line_info (GPIO ).unwrap (); + +assert_eq! (info.get_direction ().unwrap (),Direction ::Input ); + +// Generate events +request_reconfigure_line (chip.clone ()); + +// Line requested event + chip.wait_info_event (Duration ::from_secs (1 )).unwrap (); +let event = chip.read_info_event ().unwrap (); +let ts_req = event.get_timestamp (); + +assert_eq! (event.get_event_type ().unwrap (),Event ::LineRequested ); +assert_eq! ( + event.line_info ().unwrap ().get_direction ().unwrap (), +Direction ::Input + ); + +// Line changed event + chip.wait_info_event (Duration ::from_secs (1 )).unwrap (); +let event = chip.read_info_event ().unwrap (); +let ts_rec = event.get_timestamp (); + +assert_eq! (event.get_event_type ().unwrap (),Event ::LineConfigChanged ); +assert_eq! ( + event.line_info ().unwrap ().get_direction ().unwrap (), +Direction ::Output + ); + +// Line released event + chip.wait_info_event (Duration ::from_secs (1 )).unwrap (); +let event = chip.read_info_event ().unwrap (); +let ts_rel = event.get_timestamp (); + +assert_eq! (event.get_event_type ().unwrap (),Event ::LineReleased ); + +// No events available +assert_eq! ( + chip.wait_info_event (Duration ::from_millis (100 )) + .unwrap_err (), +ChipError ::OperationTimedOut + ); + +// Check timestamps are really monotonic. +assert! (ts_rel > ts_rec); +assert! (ts_rec > ts_req); + } + } +}bindings/rust/tests/line_config.rs new file +26
-0@@ -0,0 +1,187 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar <[email protected]> + +mod common; + +mod line_config { +use std::time::Duration ; + +use libgpiod::{Bias ,Direction ,Drive ,Edge ,EventClock ,LineConfig }; + +mod default { +use super ::*; + +#[test] +fn verify () { +let lconfig =LineConfig ::new ().unwrap (); + +assert_eq! (lconfig.get_direction_default ().unwrap (),Direction ::AsIs ); +assert_eq! (lconfig.get_edge_detection_default ().unwrap (),Edge ::None ); +assert_eq! (lconfig.get_bias_default ().unwrap (),Bias ::AsIs ); +assert_eq! (lconfig.get_drive_default ().unwrap (),Drive ::PushPull ); +assert_eq! (lconfig.get_active_low_default (),false ); +assert_eq! ( + lconfig.get_debounce_period_default ().unwrap (),
bindings/rust/tests/line_config.rs line 36
Quoted 10 lines
bindings/rust/tests/line_config.rs new file +10
-0+Duration ::from_millis (0 ) + ); +assert_eq! ( + lconfig.get_event_clock_default ().unwrap (), +EventClock ::Monotonic + ); +assert_eq! (lconfig.get_output_value_default ().unwrap (),0 ); +assert_eq! (lconfig.get_overrides ().unwrap ().len (),0 ); + } + }
The only test in the default mod is verify.
Drop the mod and rename the test to default.
Quoted 236 lines
bindings/rust/tests/line_config.rs new file +151
-0+ +mod overrides { +use super ::*; + +#[test] +fn direction () { +const GPIO :u32 =0 ; +let mut lconfig =LineConfig ::new ().unwrap (); + + lconfig.set_direction_default (Direction ::AsIs ); + lconfig.set_direction_override (Direction ::Input ,GPIO ); + +assert_eq! (lconfig.direction_is_overridden (GPIO ),true ); +assert_eq! ( + lconfig.get_direction_offset (GPIO ).unwrap (), +Direction ::Input + ); + + lconfig.clear_direction_override (GPIO ); +assert_eq! (lconfig.direction_is_overridden (GPIO ),false ); +assert_eq! (lconfig.get_direction_offset (GPIO ).unwrap (),Direction ::AsIs ); + } + +#[test] +fn edge_detection () { +const GPIO :u32 =1 ; +let mut lconfig =LineConfig ::new ().unwrap (); + + lconfig.set_edge_detection_default (Edge ::None ); + lconfig.set_edge_detection_override (Edge ::Both ,GPIO ); + +assert_eq! (lconfig.edge_detection_is_overridden (GPIO ),true ); +assert_eq! (lconfig.get_edge_detection_offset (GPIO ).unwrap (),Edge ::Both ); + + lconfig.clear_edge_detection_override (GPIO ); +assert_eq! (lconfig.edge_detection_is_overridden (GPIO ),false ); +assert_eq! (lconfig.get_edge_detection_offset (GPIO ).unwrap (),Edge ::None ); + } + +#[test] +fn bias () { +const GPIO :u32 =2 ; +let mut lconfig =LineConfig ::new ().unwrap (); + + lconfig.set_bias_default (Bias ::AsIs ); + lconfig.set_bias_override (Bias ::PullDown ,GPIO ); + +assert_eq! (lconfig.bias_is_overridden (GPIO ),true ); +assert_eq! (lconfig.get_bias_offset (GPIO ).unwrap (),Bias ::PullDown ); + + lconfig.clear_bias_override (GPIO ); +assert_eq! (lconfig.bias_is_overridden (GPIO ),false ); +assert_eq! (lconfig.get_bias_offset (GPIO ).unwrap (),Bias ::AsIs ); + } + +#[test] +fn drive () { +const GPIO :u32 =3 ; +let mut lconfig =LineConfig ::new ().unwrap (); + + lconfig.set_drive_default (Drive ::PushPull ); + lconfig.set_drive_override (Drive ::OpenDrain ,GPIO ); + +assert_eq! (lconfig.drive_is_overridden (GPIO ),true ); +assert_eq! (lconfig.get_drive_offset (GPIO ).unwrap (),Drive ::OpenDrain ); + + lconfig.clear_drive_override (GPIO ); +assert_eq! (lconfig.drive_is_overridden (GPIO ),false ); +assert_eq! (lconfig.get_drive_offset (GPIO ).unwrap (),Drive ::PushPull ); + } + +#[test] +fn active_low () { +const GPIO :u32 =4 ; +let mut lconfig =LineConfig ::new ().unwrap (); + + lconfig.set_active_low_default (false ); + lconfig.set_active_low_override (true ,GPIO ); + +assert_eq! (lconfig.active_low_is_overridden (GPIO ),true ); +assert_eq! (lconfig.get_active_low_offset (GPIO ),true ); + + lconfig.clear_active_low_override (GPIO ); +assert_eq! (lconfig.active_low_is_overridden (GPIO ),false ); +assert_eq! (lconfig.get_active_low_offset (GPIO ),false ); + } + +#[test] +fn debounce_period () { +const GPIO :u32 =5 ; +let mut lconfig =LineConfig ::new ().unwrap (); + + lconfig.set_debounce_period_default (Duration ::from_millis (5 )); + lconfig.set_debounce_period_override (Duration ::from_millis (3 ),GPIO ); + +assert_eq! (lconfig.debounce_period_is_overridden (GPIO ),true ); +assert_eq! ( + lconfig.get_debounce_period_offset (GPIO ).unwrap (), +Duration ::from_millis (3 ) + ); + + lconfig.clear_debounce_period_override (GPIO ); +assert_eq! (lconfig.debounce_period_is_overridden (GPIO ),false ); +assert_eq! ( + lconfig.get_debounce_period_offset (GPIO ).unwrap (), +Duration ::from_millis (5 ) + ); + } + +#[test] +fn event_clock () { +const GPIO :u32 =6 ; +let mut lconfig =LineConfig ::new ().unwrap (); + + lconfig.set_event_clock_default (EventClock ::Monotonic ); + lconfig.set_event_clock_override (EventClock ::Realtime ,GPIO ); + +assert_eq! (lconfig.event_clock_is_overridden (GPIO ),true ); +assert_eq! ( + lconfig.get_event_clock_offset (GPIO ).unwrap (), +EventClock ::Realtime + ); + + lconfig.clear_event_clock_override (GPIO ); +assert_eq! (lconfig.event_clock_is_overridden (GPIO ),false ); +assert_eq! ( + lconfig.get_event_clock_offset (GPIO ).unwrap (), +EventClock ::Monotonic + ); + } + +#[test] +fn output_value () { +const GPIO :u32 =0 ; +let mut lconfig =LineConfig ::new ().unwrap (); + + lconfig.set_output_value_default (0 ); + lconfig.set_output_value_override (1 ,GPIO ); + lconfig.set_output_values (&[1 ,2 ,8 ], &[1 ,1 ,1 ]).unwrap (); + +for linein [0 ,1 ,2 ,8 ] { +assert_eq! (lconfig.output_value_is_overridden (line),true ); +assert_eq! (lconfig.get_output_value_offset (line).unwrap (),1 ); + + lconfig.clear_output_value_override (line); +assert_eq! (lconfig.output_value_is_overridden (line),false ); +assert_eq! (lconfig.get_output_value_offset (line).unwrap (),0 ); + } + } + } +}bindings/rust/tests/line_info.rs new file +79
-0@@ -0,0 +1,90 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar <[email protected]> + +mod common; + +mod line_info { +use libc::EINVAL ; +use std::time::Duration ; + +use vmm_sys_util::errno::Error as IoError ; + +use crate ::common::*; +use libgpiod::{Bias ,Chip ,Direction ,Drive ,Edge ,Error as ChipError ,EventClock }; +use libgpiod_sys::{GPIOSIM_HOG_DIR_OUTPUT_HIGH ,GPIOSIM_HOG_DIR_OUTPUT_LOW }; + +const NGPIO :u64 =8 ; + +mod basic { +use super ::*; + +#[test] +fn verify () { +const GPIO :u32 =0 ; +const LABEL : &str ="foobar" ; +let sim =Sim ::new (Some (NGPIO ),None ,false ).unwrap (); + sim.set_line_name (GPIO ,LABEL ).unwrap (); + sim.hog_line (GPIO ,"hog" ,GPIOSIM_HOG_DIR_OUTPUT_HIGH as i32 ) + .unwrap (); + sim.enable ().unwrap (); + +let chip =Chip ::open (sim.dev_path ()).unwrap (); +let info = chip.line_info (GPIO ).unwrap (); + +assert_eq! (info.get_offset (),GPIO ); +assert_eq! (info.get_name ().unwrap (),LABEL ); +assert_eq! (info.is_used (),true ); +assert_eq! (info.get_consumer ().unwrap (),"hog" ); +assert_eq! (info.get_direction ().unwrap (),Direction ::Output ); +assert_eq! (info.is_active_low (),false ); +assert_eq! (info.get_bias ().unwrap (),Bias ::Unknown ); +assert_eq! (info.get_drive ().unwrap (),Drive ::PushPull ); +assert_eq! (info.get_edge_detection ().unwrap (),Edge ::None ); +assert_eq! (info.get_event_clock ().unwrap (),EventClock ::Monotonic ); +assert_eq! (info.is_debounced (),false ); +assert_eq! (info.get_debounce_period (),Duration ::from_millis (0 )); + +assert_eq! ( + chip.line_info (NGPIO as u32 ).unwrap_err (), +ChipError ::OperationFailed ("Gpio LineInfo line-info" ,IoError ::new (EINVAL )) + ); + } + } + +mod properties { +use super ::*; + +#[test] +fn verify () { +let sim =Sim ::new (Some (NGPIO ),None ,false ).unwrap (); + sim.set_line_name (1 ,"one" ).unwrap (); + sim.set_line_name (2 ,"two" ).unwrap (); + sim.set_line_name (4 ,"four" ).unwrap (); + sim.set_line_name (5 ,"five" ).unwrap (); + sim.hog_line (3 ,"hog3" ,GPIOSIM_HOG_DIR_OUTPUT_HIGH as i32 ) + .unwrap (); + sim.hog_line (4 ,"hog4" ,GPIOSIM_HOG_DIR_OUTPUT_LOW as i32 ) + .unwrap (); + sim.enable ().unwrap (); + +let chip =Chip ::open (sim.dev_path ()).unwrap (); + chip.line_info (6 ).unwrap (); + +let info4 = chip.line_info (4 ).unwrap (); +assert_eq! (info4.get_offset (),4 ); +assert_eq! (info4.get_name ().unwrap (),"four" ); +assert_eq! (info4.is_used (),true ); +assert_eq! (info4.get_consumer ().unwrap (),"hog4" );
bindings/rust/tests/line_info.rs line 89
Quoted 10 lines
bindings/rust/tests/line_info.rs new file +10
-0+assert_eq! (info4.get_direction ().unwrap (),Direction ::Output ); +assert_eq! (info4.is_active_low (),false ); +assert_eq! (info4.get_bias ().unwrap (),Bias ::Unknown ); +assert_eq! (info4.get_drive ().unwrap (),Drive ::PushPull ); +assert_eq! (info4.get_edge_detection ().unwrap (),Edge ::None ); +assert_eq! (info4.get_event_clock ().unwrap (),EventClock ::Monotonic ); +assert_eq! (info4.is_debounced (),false ); +assert_eq! (info4.get_debounce_period (),Duration ::from_millis (0 )); + } + }
Test that you can read all supported values for all fields.
Quoted 230 lines
bindings/rust/tests/line_info.rs new file +1
-0+}bindings/rust/tests/line_request.rs new file +223
-0@@ -0,0 +1,234 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar <[email protected]> + +mod common; + +mod line_request { +use libc::{EBUSY ,EINVAL }; + +use vmm_sys_util::errno::Error as IoError ; + +use crate ::common::*; +use libgpiod::{Bias ,Direction ,Error as ChipError ,LineConfig }; +use libgpiod_sys::{ +GPIOSIM_PULL_DOWN ,GPIOSIM_PULL_UP ,GPIOSIM_VALUE_ACTIVE ,GPIOSIM_VALUE_INACTIVE , + }; + +const NGPIO :u64 =8 ; + +mod invalid_arguments { +use super ::*; + +#[test] +fn no_offsets () { +let mut config =TestConfig ::new (NGPIO ).unwrap (); + config.rconfig (None ); + config.lconfig_raw (); + +assert_eq! ( + config.request_lines ().unwrap_err (), +ChipError ::OperationFailed ("Gpio LineRequest request-lines" ,IoError ::new (EINVAL )) + ); + } + +#[test] +fn duplicate_offsets () { +let mut config =TestConfig ::new (NGPIO ).unwrap (); + config.rconfig (Some (&[2 ,0 ,0 ,4 ])); + config.lconfig_raw (); + +assert_eq! ( + config.request_lines ().unwrap_err (), +ChipError ::OperationFailed ("Gpio LineRequest request-lines" ,IoError ::new (EBUSY )) + ); + } + +#[test] +fn out_of_bound_offsets () { +let mut config =TestConfig ::new (NGPIO ).unwrap (); + config.rconfig (Some (&[2 ,0 ,8 ,4 ])); + config.lconfig_raw (); + +assert_eq! ( + config.request_lines ().unwrap_err (), +ChipError ::OperationFailed ("Gpio LineRequest request-lines" ,IoError ::new (EINVAL )) + ); + } + } + +mod verify { +use super ::*; + +#[test] +fn custom_consumer () { +const GPIO :u32 =2 ; +const CONSUMER : &str ="foobar" ; +let mut config =TestConfig ::new (NGPIO ).unwrap (); + config.rconfig_consumer (Some (&[GPIO ]),Some (CONSUMER )); + config.lconfig_raw (); + config.request_lines ().unwrap (); + +let info = config.chip ().line_info (GPIO ).unwrap (); + +assert_eq! (info.is_used (),true ); +assert_eq! (info.get_consumer ().unwrap (),CONSUMER ); + } + +#[test] +fn empty_consumer () { +const GPIO :u32 =2 ; +let mut config =TestConfig ::new (NGPIO ).unwrap (); + config.rconfig (Some (&[GPIO ])); + config.lconfig_raw (); + config.request_lines ().unwrap (); + +let info = config.chip ().line_info (GPIO ).unwrap (); + +assert_eq! (info.is_used (),true ); +assert_eq! (info.get_consumer ().unwrap (),"?" ); + } + +#[test] +fn read_values () { +let offsets = [7 ,1 ,0 ,6 ,2 ]; +let pulls = [ +GPIOSIM_PULL_UP , +GPIOSIM_PULL_UP , +GPIOSIM_PULL_DOWN , +GPIOSIM_PULL_UP , +GPIOSIM_PULL_DOWN , + ]; +let mut config =TestConfig ::new (NGPIO ).unwrap (); + config.set_pull (&offsets, &pulls); + config.rconfig (Some (&offsets)); + config.lconfig (Some (Direction ::Input ),None ,None ,None ,None ); + config.request_lines ().unwrap (); + +let request = config.request (); + +// Buffer is smaller +let mut values:Vec <i32 > =vec! [0 ;4 ]; +assert_eq! ( + request.get_values (&mut values).unwrap_err (), +ChipError ::OperationFailed ( +"Gpio LineRequest array size mismatch" , +IoError ::new (EINVAL ), + ) + ); + +// Buffer is larger +let mut values:Vec <i32 > =vec! [0 ;6 ]; +assert_eq! ( + request.get_values (&mut values).unwrap_err (), +ChipError ::OperationFailed ( +"Gpio LineRequest array size mismatch" , +IoError ::new (EINVAL ), + ) + ); + +// Single values read properly +assert_eq! (request.get_value (7 ).unwrap (),1 ); + +// Values read properly +let mut values:Vec <i32 > =vec! [0 ;5 ]; + request.get_values (&mut values).unwrap (); +for iin 0 ..values.len () { +assert_eq! ( + values[i], +match pulls[i] { +GPIOSIM_PULL_DOWN =>0 , + _ =>1 , + } + ); + } + +// Subset of values read properly +let mut values:Vec <i32 > =vec! [0 ;3 ]; + request.get_values_subset (&[2 ,0 ,6 ], &mut values).unwrap (); +assert_eq! (values[0 ],0 ); +assert_eq! (values[1 ],0 ); +assert_eq! (values[2 ],1 ); + +// Value read properly after reconfigure +let mut lconfig =LineConfig ::new ().unwrap (); + lconfig.set_active_low_default (true ); + request.reconfigure_lines (&lconfig).unwrap (); +assert_eq! (request.get_value (7 ).unwrap (),0 ); + } + +#[test] +fn set_output_values () { +let offsets = [0 ,1 ,3 ,4 ]; +let mut config =TestConfig ::new (NGPIO ).unwrap (); + config.rconfig (Some (&offsets)); + config.lconfig (Some (Direction ::Output ),Some (1 ),Some ((4 ,0 )),None ,None ); + config.request_lines ().unwrap (); + +assert_eq! (config.sim ().val (0 ).unwrap (),GPIOSIM_VALUE_ACTIVE ); +assert_eq! (config.sim ().val (1 ).unwrap (),GPIOSIM_VALUE_ACTIVE ); +assert_eq! (config.sim ().val (3 ).unwrap (),GPIOSIM_VALUE_ACTIVE ); + +// Overriden +assert_eq! (config.sim ().val (4 ).unwrap (),GPIOSIM_VALUE_INACTIVE ); + +// Default +assert_eq! (config.sim ().val (2 ).unwrap (),GPIOSIM_VALUE_INACTIVE ); + } + +#[test] +fn reconfigure_output_values () { +let offsets = [0 ,1 ,3 ,4 ]; +let mut config =TestConfig ::new (NGPIO ).unwrap (); + config.rconfig (Some (&offsets)); + config.lconfig (Some (Direction ::Output ),Some (0 ),None ,None ,None ); + config.request_lines ().unwrap (); +let request = config.request (); + +// Set single value + request.set_value (1 ,1 ).unwrap (); +assert_eq! (config.sim ().val (0 ).unwrap (),GPIOSIM_VALUE_INACTIVE ); +assert_eq! (config.sim ().val (1 ).unwrap (),GPIOSIM_VALUE_ACTIVE ); +assert_eq! (config.sim ().val (3 ).unwrap (),GPIOSIM_VALUE_INACTIVE ); +assert_eq! (config.sim ().val (4 ).unwrap (),GPIOSIM_VALUE_INACTIVE ); + request.set_value (1 ,0 ).unwrap (); +assert_eq! (config.sim ().val (1 ).unwrap (),GPIOSIM_VALUE_INACTIVE ); + +// Set values of subset + request.set_values_subset (&[4 ,3 ], &[1 ,1 ]).unwrap (); +assert_eq! (config.sim ().val (0 ).unwrap (),GPIOSIM_VALUE_INACTIVE ); +assert_eq! (config.sim ().val (1 ).unwrap (),GPIOSIM_VALUE_INACTIVE ); +assert_eq! (config.sim ().val (3 ).unwrap (),GPIOSIM_VALUE_ACTIVE ); +assert_eq! (config.sim ().val (4 ).unwrap (),GPIOSIM_VALUE_ACTIVE ); + request.set_values_subset (&[4 ,3 ], &[0 ,0 ]).unwrap (); +assert_eq! (config.sim ().val (3 ).unwrap (),GPIOSIM_VALUE_INACTIVE ); +assert_eq! (config.sim ().val (4 ).unwrap (),GPIOSIM_VALUE_INACTIVE ); + +// Set all values + request.set_values (&[1 ,0 ,1 ,0 ]).unwrap (); +assert_eq! (config.sim ().val (0 ).unwrap (),GPIOSIM_VALUE_ACTIVE ); +assert_eq! (config.sim ().val (1 ).unwrap (),GPIOSIM_VALUE_INACTIVE ); +assert_eq! (config.sim ().val (3 ).unwrap (),GPIOSIM_VALUE_ACTIVE ); +assert_eq! (config.sim ().val (4 ).unwrap (),GPIOSIM_VALUE_INACTIVE ); + request.set_values (&[0 ,0 ,0 ,0 ]).unwrap (); +assert_eq! (config.sim ().val (0 ).unwrap (),GPIOSIM_VALUE_INACTIVE ); +assert_eq! (config.sim ().val (1 ).unwrap (),GPIOSIM_VALUE_INACTIVE ); +assert_eq! (config.sim ().val (3 ).unwrap (),GPIOSIM_VALUE_INACTIVE ); +assert_eq! (config.sim ().val (4 ).unwrap (),GPIOSIM_VALUE_INACTIVE ); + } + +#[test] +fn set_bias () { +let offsets = [3 ];
bindings/rust/tests/line_request.rs line 233
Quoted 10 lines
bindings/rust/tests/line_request.rs new file +10
-0+let mut config =TestConfig ::new (NGPIO ).unwrap (); + config.rconfig (Some (&offsets)); + config.lconfig (Some (Direction ::Input ),None ,None ,None ,Some (Bias ::PullUp )); + config.request_lines ().unwrap (); + config.request (); + +// Set single value +assert_eq! (config.sim ().val (3 ).unwrap (),GPIOSIM_VALUE_ACTIVE ); + } + }
Test reconfigure() failure modes.
Test you can reconfigure all values for all attributes (not all values for debounce obviously - just the flags).
Similarly for request_lines (ideally in chip.rs)
Quoted 52 lines
bindings/rust/tests/line_request.rs new file +1
-0+}bindings/rust/tests/request_config.rs new file +42
-0@@ -0,0 +1,42 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar <[email protected]> + +mod common; + +mod request_config { +use vmm_sys_util::errno::Error as IoError ; + +use libgpiod::{Error as ChipError ,RequestConfig }; + +mod verify { +use super ::*; + +#[test] +fn default () { +let rconfig =RequestConfig ::new ().unwrap (); + +assert_eq! (rconfig.get_offsets ().len (),0 ); +assert_eq! (rconfig.get_event_buffer_size (),0 ); +assert_eq! ( + rconfig.get_consumer ().unwrap_err (), +ChipError ::OperationFailed ("Gpio RequestConfig get-consumer" ,IoError ::new (0 )) + ); + } + +#[test] +fn initialized () { +let offsets = [0 ,1 ,2 ,3 ]; +const CONSUMER : &str ="foobar" ; +let rconfig =RequestConfig ::new ().unwrap (); + rconfig.set_consumer (CONSUMER ); + rconfig.set_offsets (&offsets); + rconfig.set_event_buffer_size (64 ); + +assert_eq! (rconfig.get_offsets (), offsets); +assert_eq! (rconfig.get_event_buffer_size (),64 ); +assert_eq! (rconfig.get_consumer ().unwrap (),CONSUMER ); + } + } +}Signature · 1 line
2.31.1.272.g89b43f80a514
Clippy warnings to fix:
$cargo clippy --tests
warning: this expression creates a reference which is immediately dereferenced by the compiler
--> tests/common/config.rs:103:9
|
103 | &self.chip.as_ref().unwrap()
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: change this to: `self.chip.as_ref().unwrap()`
| = note: #[warn(clippy::needless_borrow)] on by default
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow
warning: this expression creates a reference which is immediately dereferenced by the compiler
--> tests/common/config.rs:107:9
|
107 | &self.request.as_ref().unwrap()
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: change this to: `self.request.as_ref().unwrap()`
|= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow
warning: used assert_eq! with a literal bool
--> tests/line_config.rs:24:13
| 24 | assert_eq!(lconfig.get_active_low_default(), false); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` |
= note: #[warn(clippy::bool_assert_comparison)] on by default
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
warning: used assert_eq! with a literal bool
--> tests/line_config.rs:49:13
| 49 | assert_eq!(lconfig.direction_is_overridden(GPIO), true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` |
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
warning: used assert_eq! with a literal bool
--> tests/line_config.rs:56:13
| 56 | assert_eq!(lconfig.direction_is_overridden(GPIO), false); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` |
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
warning: used assert_eq! with a literal bool
--> tests/line_config.rs:68:13
| 68 | assert_eq!(lconfig.edge_detection_is_overridden(GPIO), true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` |
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
warning: used assert_eq! with a literal bool
--> tests/line_config.rs:72:13
| 72 | assert_eq!(lconfig.edge_detection_is_overridden(GPIO), false); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` |
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
warning: used assert_eq! with a literal bool
--> tests/line_config.rs:84:13
| 84 | assert_eq!(lconfig.bias_is_overridden(GPIO), true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` |
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
warning: used assert_eq! with a literal bool
--> tests/line_config.rs:88:13
| 88 | assert_eq!(lconfig.bias_is_overridden(GPIO), false); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` |
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
warning: used assert_eq! with a literal bool
--> tests/line_config.rs:100:13
|
100 | assert_eq!(lconfig.drive_is_overridden(GPIO), true);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
|= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
warning: used assert_eq! with a literal bool
--> tests/line_config.rs:104:13
|
104 | assert_eq!(lconfig.drive_is_overridden(GPIO), false);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
|= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
warning: used assert_eq! with a literal bool
--> tests/line_config.rs:116:13
|
116 | assert_eq!(lconfig.active_low_is_overridden(GPIO), true);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
|= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
warning: used assert_eq! with a literal bool
--> tests/line_config.rs:117:13
|
117 | assert_eq!(lconfig.get_active_low_offset(GPIO), true);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
|= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
warning: used assert_eq! with a literal bool
--> tests/line_config.rs:120:13
|
120 | assert_eq!(lconfig.active_low_is_overridden(GPIO), false);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
|= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
warning: used assert_eq! with a literal bool
--> tests/line_config.rs:121:13
|
121 | assert_eq!(lconfig.get_active_low_offset(GPIO), false);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
|= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
warning: used assert_eq! with a literal bool
--> tests/line_config.rs:132:13
|
132 | assert_eq!(lconfig.debounce_period_is_overridden(GPIO), true);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
|= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
warning: used assert_eq! with a literal bool
--> tests/line_config.rs:139:13
|
139 | assert_eq!(lconfig.debounce_period_is_overridden(GPIO), false);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
|= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
warning: used assert_eq! with a literal bool
--> tests/line_config.rs:154:13
|
154 | assert_eq!(lconfig.event_clock_is_overridden(GPIO), true);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
|= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
warning: used assert_eq! with a literal bool
--> tests/line_config.rs:161:13
|
161 | assert_eq!(lconfig.event_clock_is_overridden(GPIO), false);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
|= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
warning: used assert_eq! with a literal bool
--> tests/line_config.rs:178:17
|
178 | assert_eq!(lconfig.output_value_is_overridden(line), true);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
|= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
warning: used assert_eq! with a literal bool
--> tests/line_config.rs:182:17
|
182 | assert_eq!(lconfig.output_value_is_overridden(line), false);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
|= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
warning: used assert_eq! with a literal bool
--> tests/line_info.rs:38:13
| 38 | assert_eq!(info.is_used(), true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` |
= note: #[warn(clippy::bool_assert_comparison)] on by default
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
warning: used assert_eq! with a literal bool
--> tests/line_info.rs:41:13
| 41 | assert_eq!(info.is_active_low(), false); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` |
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
warning: used assert_eq! with a literal bool
--> tests/line_info.rs:46:13
| 46 | assert_eq!(info.is_debounced(), false); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` |
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
warning: used assert_eq! with a literal bool
--> tests/line_info.rs:78:13
| 78 | assert_eq!(info4.is_used(), true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` |
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
warning: used assert_eq! with a literal bool
--> tests/line_info.rs:81:13
| 81 | assert_eq!(info4.is_active_low(), false); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` |
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
warning: used assert_eq! with a literal bool
--> tests/line_info.rs:86:13
| 86 | assert_eq!(info4.is_debounced(), false); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` |
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
warning: used assert_eq! with a literal bool
--> tests/line_request.rs:75:13
| 75 | assert_eq!(info.is_used(), true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` |
= note: #[warn(clippy::bool_assert_comparison)] on by default
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
warning: used assert_eq! with a literal bool
--> tests/line_request.rs:89:13
| 89 | assert_eq!(info.is_used(), true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` |
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
warning: libgpiod (test "line_config") generated 21 warnings
warning: libgpiod (test "request_config") generated 2 warnings (2 duplicates)
warning: libgpiod (test "info_event") generated 2 warnings (2 duplicates)
warning: libgpiod (test "line_info") generated 8 warnings (2 duplicates)
warning: libgpiod (test "chip") generated 2 warnings (2 duplicates)
warning: libgpiod (test "line_request") generated 4 warnings (2 duplicates)
warning: libgpiod (test "edge_event") generated 2 warnings (2 duplicates)
Finished dev [unoptimized + debuginfo] target(s) in 0.05s
Cheers,
Kent.
Re: [PATCH V4 7/8] libgpiod: Add rust tests
Quoted 4 lines · Kent Gibson
On Fri, Jul 08, 2022 at 05:05:00PM +0530, Viresh Kumar wrote:
Don't include the test results in the commit message.
Those are more of a release artifact than a commit artifact. It is assumed the tests pass for you or you wouldn't be submitting them.
I wasn't trying to prove that I tested them :)
The idea was to show how module/test names eventually look in the output.
Maybe I could have just replied to this email and pasted it, sure commit message doesn't need it.
Quoted 13 lines
Quoted 13 lines
bindings/rust/tests/chip.rs +12
-0+mod configure { +use super ::*; +const NGPIO :u64 =16 ; +const LABEL : &str ="foobar" ; + +#[test] +fn verify () { +let sim =Sim ::new (Some (NGPIO ),Some (LABEL ),true ).unwrap (); +let chip =Chip ::open (sim.dev_path ()).unwrap (); + +assert_eq! (chip.get_label ().unwrap (),LABEL ); +assert_eq! (chip.get_name ().unwrap (), sim.chip_name ());
Quoted 8 lines
bindings/rust/tests/chip.rs line 66
Quoted 5 lines
bindings/rust/tests/chip.rs +5
-0+assert_eq! (chip.get_path ().unwrap (), sim.dev_path ()); +assert_eq! (chip.get_num_lines (),NGPIO as u32 ); + chip.get_fd ().unwrap (); + } +A test for a basic open on an existing chip and a smoke test of some chip methods is called "verify", so
chip::configure::verify?
You want me to rename this ? Sorry, got confused :(
Yeah, I am generally bad with naming, suggestions are welcome here :)
Quoted 22 lines
Quoted 22 lines
bindings/rust/tests/chip.rs +22
-0+#[test] +fn line_lookup () { +let sim =Sim ::new (Some (NGPIO ),None ,false ).unwrap (); + sim.set_line_name (0 ,"zero" ).unwrap (); + sim.set_line_name (2 ,"two" ).unwrap (); + sim.set_line_name (3 ,"three" ).unwrap (); + sim.set_line_name (5 ,"five" ).unwrap (); + sim.set_line_name (10 ,"ten" ).unwrap (); + sim.set_line_name (11 ,"ten" ).unwrap (); + sim.enable ().unwrap (); + +let chip =Chip ::open (sim.dev_path ()).unwrap (); + +// Success case +assert_eq! (chip.find_line ("zero" ).unwrap (),0 ); +assert_eq! (chip.find_line ("two" ).unwrap (),2 ); +assert_eq! (chip.find_line ("three" ).unwrap (),3 ); +assert_eq! (chip.find_line ("five" ).unwrap (),5 ); + +// Success with duplicate names, should return first entry +assert_eq! (chip.find_line ("ten" ).unwrap (),10 ); +
Quoted 8 lines
bindings/rust/tests/chip.rs line 94
Quoted 6 lines
bindings/rust/tests/chip.rs +6
-0+// Failure +assert_eq! ( + chip.find_line ("nonexistent" ).unwrap_err (), +ChipError ::OperationFailed ("Gpio Chip find-line" ,IoError ::new (ENOENT )) + ); + }If it is testing
find_line()then why not call itfind_line?
I think I picked many names from the TEST_CASE name from cxx bindings. This was written there as:
TEST_CASE("line lookup by name works", "[chip]")
and I didn't think much about it :)
Sure I can name this find_line().
Quoted 20 lines
Quoted 7 lines
bindings/rust/tests/edge_event.rs +6
-0+#[test] +fn wait_timeout () { +let mut config =TestConfig ::new (NGPIO ).unwrap (); + config.rconfig (Some (&[0 ])); + config.lconfig_edge (Some (Edge ::Both )); + config.request_lines ().unwrap ();bindings/rust/tests/edge_event.rs line 59
Quoted 10 lines
bindings/rust/tests/edge_event.rs +10
-0+ +// No events available +assert_eq! ( + config + .request () + .wait_edge_event (Duration ::from_millis (100 )) + .unwrap_err (), +ChipError ::OperationTimedOut + ); + }Is a timeout really a "failure"?
Quoted 2 lines
It is testing
wait_edge_event(), which is a method ofline_request, and so should be in theline_requesttest suite.
Copied it from cxx again, I just tried to add similar tests in similar files.
TEST_CASE("edge_event wait timeout", "[edge-event]")
Quoted 9 lines
Quoted 9 lines
bindings/rust/tests/edge_event.rs +9
-0+ +#[test] +fn dir_out_edge_failure () { +let mut config =TestConfig ::new (NGPIO ).unwrap (); + config.rconfig (Some (&[0 ])); + config.lconfig (Some (Direction ::Output ),None ,None ,Some (Edge ::Both ),None ); + +assert_eq! ( + config.request_lines ().unwrap_err (),
Quoted 8 lines
bindings/rust/tests/edge_event.rs line 73
Quoted 5 lines
bindings/rust/tests/edge_event.rs +5
-0+ChipError ::OperationFailed ("Gpio LineRequest request-lines" ,IoError ::new (EINVAL )) + ); + } + } +This is testing a failure mode of
request_lines(), notedge_events. Where is theedge_eventhere?
I agree, I will move this out.
This needs fixing too though.
TEST_CASE("output mode and edge detection don't work together", "[edge-event]")
Quoted 39 lines
Quoted 39 lines
bindings/rust/tests/info_event.rs new file +33
-0@@ -0,0 +1,126 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar <[email protected]> + +mod common; + +mod info_event { +use libc::EINVAL ; +use std::sync::Arc ; +use std::thread::{sleep, spawn}; +use std::time::Duration ; + +use vmm_sys_util::errno::Error as IoError ; + +use crate ::common::*; +use libgpiod::{Chip ,Direction ,Error as ChipError ,Event ,LineConfig ,RequestConfig }; + +fn request_reconfigure_line (chip:Arc <Chip >) { +spawn (move || { +sleep (Duration ::from_millis (10 )); + +let lconfig1 =LineConfig ::new ().unwrap (); +let rconfig =RequestConfig ::new ().unwrap (); + rconfig.set_offsets (&[7 ]); + +let request = chip.request_lines (&rconfig, &lconfig1).unwrap (); + +sleep (Duration ::from_millis (10 )); + +let mut lconfig2 =LineConfig ::new ().unwrap (); + lconfig2.set_direction_default (Direction ::Output ); +
Quoted 8 lines
bindings/rust/tests/info_event.rs line 39
Quoted 6 lines
bindings/rust/tests/info_event.rs new file +6
-0+ request.reconfigure_lines (&lconfig2).unwrap (); + +sleep (Duration ::from_millis (10 )); + }); + } +Benefit of the background thread?
Again copied from cxx, I think the idea here is to get the events one by one and read one before the next one is generated. i.e. to do it all in parallel, which looked fine to me.
Quoted 28 lines
Quoted 28 lines
bindings/rust/tests/info_event.rs new file +28
-0+mod properties { +use super ::*; + +#[test] +fn verify () { +let sim =Sim ::new (Some (NGPIO ),None ,false ).unwrap (); + sim.set_line_name (1 ,"one" ).unwrap (); + sim.set_line_name (2 ,"two" ).unwrap (); + sim.set_line_name (4 ,"four" ).unwrap (); + sim.set_line_name (5 ,"five" ).unwrap (); + sim.hog_line (3 ,"hog3" ,GPIOSIM_HOG_DIR_OUTPUT_HIGH as i32 ) + .unwrap (); + sim.hog_line (4 ,"hog4" ,GPIOSIM_HOG_DIR_OUTPUT_LOW as i32 ) + .unwrap (); + sim.enable ().unwrap (); + +let chip =Chip ::open (sim.dev_path ()).unwrap (); + chip.line_info (6 ).unwrap (); + +let info4 = chip.line_info (4 ).unwrap (); +assert_eq! (info4.get_offset (),4 ); +assert_eq! (info4.get_name ().unwrap (),"four" ); +assert_eq! (info4.is_used (),true ); +assert_eq! (info4.get_consumer ().unwrap (),"hog4" ); +assert_eq! (info4.get_direction ().unwrap (),Direction ::Output ); +assert_eq! (info4.is_active_low (),false ); +assert_eq! (info4.get_bias ().unwrap (),Bias ::Unknown ); +assert_eq! (info4.get_drive ().unwrap (),Drive ::PushPull );
Quoted 8 lines
bindings/rust/tests/info_event.rs line 73
Quoted 6 lines
bindings/rust/tests/info_event.rs new file +6
-0+assert_eq! (info4.get_edge_detection ().unwrap (),Edge ::None ); +assert_eq! (info4.get_event_clock ().unwrap (),EventClock ::Monotonic ); +assert_eq! (info4.is_debounced (),false ); +assert_eq! (info4.get_debounce_period (),Duration ::from_millis (0 )); + } + }Test that you can read all supported values for all fields.
Like setting bias to all possible values one by one and reading them back ? Or something else ?
Quoted 3 lines
Clippy warnings to fix:
$cargo clippy --tests
I just ran
cargo clippy --workspace --bins --examples --benches --all-features -- -D warnings
and thought it works for tests too, my bad.
Signature · 1 line
viresh
Re: [PATCH V4 7/8] libgpiod: Add rust tests
Quoted 6 lines · Viresh Kumar
Quoted 4 lines · Kent Gibson
On Fri, Jul 08, 2022 at 05:05:00PM +0530, Viresh Kumar wrote:
Don't include the test results in the commit message.
Those are more of a release artifact than a commit artifact. It is assumed the tests pass for you or you wouldn't be submitting them.
Quoted 7 lines
I wasn't trying to prove that I tested them :)
The idea was to show how module/test names eventually look in the output.
Maybe I could have just replied to this email and pasted it, sure commit message doesn't need it.
If you want to add more detail to the patch, but not to the commit message, then add it between the "---" and the file list. Or put it in the cover letter.
Quoted 22 lines
Quoted 13 lines
Quoted 13 lines
bindings/rust/tests/chip.rs +12
-0+mod configure { +use super ::*; +const NGPIO :u64 =16 ; +const LABEL : &str ="foobar" ; + +#[test] +fn verify () { +let sim =Sim ::new (Some (NGPIO ),Some (LABEL ),true ).unwrap (); +let chip =Chip ::open (sim.dev_path ()).unwrap (); + +assert_eq! (chip.get_label ().unwrap (),LABEL ); +assert_eq! (chip.get_name ().unwrap (), sim.chip_name ());Quoted 8 lines
bindings/rust/tests/chip.rs line 66
Quoted 5 lines
bindings/rust/tests/chip.rs +5
-0+assert_eq! (chip.get_path ().unwrap (), sim.dev_path ()); +assert_eq! (chip.get_num_lines (),NGPIO as u32 ); + chip.get_fd ().unwrap (); + } +A test for a basic open on an existing chip and a smoke test of some chip methods is called "verify", so
chip::configure::verify?
Quoted 1 line
You want me to rename this ? Sorry, got confused :(
I was trying to work out your naming scheme.
Couldn't see one - sorry.
Quoted 3 lines
Yeah, I am generally bad with naming, suggestions are welcome here :)
Naming is always the hard part. Find a system that will do the worst of it for you.
I usually go with <struct>::<method> for the tests that cover a method on a struct. For complex methods that require multiple tests add another tier that described what subset of functionality or failure mode is being tested.
Quoted 31 lines
Quoted 22 lines
Quoted 22 lines
bindings/rust/tests/chip.rs +22
-0+#[test] +fn line_lookup () { +let sim =Sim ::new (Some (NGPIO ),None ,false ).unwrap (); + sim.set_line_name (0 ,"zero" ).unwrap (); + sim.set_line_name (2 ,"two" ).unwrap (); + sim.set_line_name (3 ,"three" ).unwrap (); + sim.set_line_name (5 ,"five" ).unwrap (); + sim.set_line_name (10 ,"ten" ).unwrap (); + sim.set_line_name (11 ,"ten" ).unwrap (); + sim.enable ().unwrap (); + +let chip =Chip ::open (sim.dev_path ()).unwrap (); + +// Success case +assert_eq! (chip.find_line ("zero" ).unwrap (),0 ); +assert_eq! (chip.find_line ("two" ).unwrap (),2 ); +assert_eq! (chip.find_line ("three" ).unwrap (),3 ); +assert_eq! (chip.find_line ("five" ).unwrap (),5 ); + +// Success with duplicate names, should return first entry +assert_eq! (chip.find_line ("ten" ).unwrap (),10 ); +Quoted 8 lines
bindings/rust/tests/chip.rs line 94
Quoted 6 lines
bindings/rust/tests/chip.rs +6
-0+// Failure +assert_eq! ( + chip.find_line ("nonexistent" ).unwrap_err (), +ChipError ::OperationFailed ("Gpio Chip find-line" ,IoError ::new (ENOENT )) + ); + }If it is testing
find_line()then why not call itfind_line?
Quoted 7 lines
I think I picked many names from the
TEST_CASEname from cxx bindings. This was written there as:
TEST_CASE("line lookup by name works", "[chip]")and I didn't think much about it :)
Fair enough.
Quoted 2 lines
Sure I can name this
find_line().
At least until you get around to renaming find_line to line_offset_from_name ;-).
Quoted 99 lines
Quoted 20 lines
Quoted 7 lines
bindings/rust/tests/edge_event.rs +6
-0+#[test] +fn wait_timeout () { +let mut config =TestConfig ::new (NGPIO ).unwrap (); + config.rconfig (Some (&[0 ])); + config.lconfig_edge (Some (Edge ::Both )); + config.request_lines ().unwrap ();bindings/rust/tests/edge_event.rs line 59
Quoted 10 lines
bindings/rust/tests/edge_event.rs +10
-0+ +// No events available +assert_eq! ( + config + .request () + .wait_edge_event (Duration ::from_millis (100 )) + .unwrap_err (), +ChipError ::OperationTimedOut + ); + }Is a timeout really a "failure"?
Quoted 2 lines
It is testing
wait_edge_event(), which is a method ofline_request, and so should be in theline_requesttest suite.Copied it from cxx again, I just tried to add similar tests in similar files.
TEST_CASE("edge_event wait timeout", "[edge-event]")Quoted 9 lines
Quoted 9 lines
bindings/rust/tests/edge_event.rs +9
-0+ +#[test] +fn dir_out_edge_failure () { +let mut config =TestConfig ::new (NGPIO ).unwrap (); + config.rconfig (Some (&[0 ])); + config.lconfig (Some (Direction ::Output ),None ,None ,Some (Edge ::Both ),None ); + +assert_eq! ( + config.request_lines ().unwrap_err (),Quoted 8 lines
bindings/rust/tests/edge_event.rs line 73
Quoted 5 lines
bindings/rust/tests/edge_event.rs +5
-0+ChipError ::OperationFailed ("Gpio LineRequest request-lines" ,IoError ::new (EINVAL )) + ); + } + } +This is testing a failure mode of
request_lines(), notedge_events. Where is theedge_eventhere?I agree, I will move this out.
This needs fixing too though.
TEST_CASE("output mode and edge detection don't work together", "[edge-event]")Quoted 39 lines
Quoted 39 lines
bindings/rust/tests/info_event.rs new file +33
-0@@ -0,0 +1,126 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar <[email protected]> + +mod common; + +mod info_event { +use libc::EINVAL ; +use std::sync::Arc ; +use std::thread::{sleep, spawn}; +use std::time::Duration ; + +use vmm_sys_util::errno::Error as IoError ; + +use crate ::common::*; +use libgpiod::{Chip ,Direction ,Error as ChipError ,Event ,LineConfig ,RequestConfig }; + +fn request_reconfigure_line (chip:Arc <Chip >) { +spawn (move || { +sleep (Duration ::from_millis (10 )); + +let lconfig1 =LineConfig ::new ().unwrap (); +let rconfig =RequestConfig ::new ().unwrap (); + rconfig.set_offsets (&[7 ]); + +let request = chip.request_lines (&rconfig, &lconfig1).unwrap (); + +sleep (Duration ::from_millis (10 )); + +let mut lconfig2 =LineConfig ::new ().unwrap (); + lconfig2.set_direction_default (Direction ::Output ); +Quoted 8 lines
bindings/rust/tests/info_event.rs line 39
Quoted 6 lines
bindings/rust/tests/info_event.rs new file +6
-0+ request.reconfigure_lines (&lconfig2).unwrap (); + +sleep (Duration ::from_millis (10 )); + }); + } +Benefit of the background thread?
Quoted 4 lines
Again copied from cxx, I think the idea here is to get the events one by one and read one before the next one is generated. i.e. to do it all in parallel, which looked fine to me.
Following the pattern from the other bindings is fine.
That is a point where Bart and I disagree.
He likes making the tests closer to reality.
I like keeping them simple.
They are separate paths and strictly speaking both should be tested. But if you are only going to do one, do the easy one ;-).
Anyway leave it as is - no point changing it now.
Quoted 42 lines
Quoted 28 lines
Quoted 28 lines
bindings/rust/tests/info_event.rs new file +28
-0+mod properties { +use super ::*; + +#[test] +fn verify () { +let sim =Sim ::new (Some (NGPIO ),None ,false ).unwrap (); + sim.set_line_name (1 ,"one" ).unwrap (); + sim.set_line_name (2 ,"two" ).unwrap (); + sim.set_line_name (4 ,"four" ).unwrap (); + sim.set_line_name (5 ,"five" ).unwrap (); + sim.hog_line (3 ,"hog3" ,GPIOSIM_HOG_DIR_OUTPUT_HIGH as i32 ) + .unwrap (); + sim.hog_line (4 ,"hog4" ,GPIOSIM_HOG_DIR_OUTPUT_LOW as i32 ) + .unwrap (); + sim.enable ().unwrap (); + +let chip =Chip ::open (sim.dev_path ()).unwrap (); + chip.line_info (6 ).unwrap (); + +let info4 = chip.line_info (4 ).unwrap (); +assert_eq! (info4.get_offset (),4 ); +assert_eq! (info4.get_name ().unwrap (),"four" ); +assert_eq! (info4.is_used (),true ); +assert_eq! (info4.get_consumer ().unwrap (),"hog4" ); +assert_eq! (info4.get_direction ().unwrap (),Direction ::Output ); +assert_eq! (info4.is_active_low (),false ); +assert_eq! (info4.get_bias ().unwrap (),Bias ::Unknown ); +assert_eq! (info4.get_drive ().unwrap (),Drive ::PushPull );Quoted 8 lines
bindings/rust/tests/info_event.rs line 73
Quoted 6 lines
bindings/rust/tests/info_event.rs new file +6
-0+assert_eq! (info4.get_edge_detection ().unwrap (),Edge ::None ); +assert_eq! (info4.get_event_clock ().unwrap (),EventClock ::Monotonic ); +assert_eq! (info4.is_debounced (),false ); +assert_eq! (info4.get_debounce_period (),Duration ::from_millis (0 )); + } + }Test that you can read all supported values for all fields.
Like setting bias to all possible values one by one and reading them back ? Or something else ?
Quoted 2 lines
Clippy warnings to fix:
Quoted 8 lines
Quoted 1 line
$cargo clippy --tests
I just ran
cargo clippy --workspace --bins --examples --benches --all-features -- -D warnings
and thought it works for tests too, my bad.
Turns out not. Surprised me too.
Cheers,
Kent.
Re: [PATCH V4 7/8] libgpiod: Add rust tests
In reply to Kent GibsonQuoted 8 lines · Kent Gibson
Quoted 8 lines
bindings/rust/tests/line_request.rs +7
-0+#[test] +fn set_bias () { +let offsets = [3 ]; +let mut config =TestConfig ::new (NGPIO ).unwrap (); + config.rconfig (Some (&offsets)); + config.lconfig (Some (Direction ::Input ),None ,None ,None ,Some (Bias ::PullUp )); + config.request_lines ().unwrap ();
Quoted 8 lines
bindings/rust/tests/line_request.rs line 233
Quoted 6 lines
bindings/rust/tests/line_request.rs +6
-0+ config.request (); + +// Set single value +assert_eq! (config.sim ().val (3 ).unwrap (),GPIOSIM_VALUE_ACTIVE ); + } + }Test
reconfigure()failure modes.
What are we specifically looking to test here ? I am not sure I understood which failures modes I should target.
Quoted 2 lines
Test you can reconfigure all values for all attributes (not all values for debounce obviously - just the flags).
This I have done now, with request.reconfigure_lines().
Quoted 1 line
Similarly for
request_lines(ideally inchip.rs)
Isn't this same as the above one? I am not sure again what I should test here.
I have pushed my current changes here, if it helps:
[email protected]:vireshk/libgpiod.git master
Signature · 1 line
viresh
Re: [PATCH V4 7/8] libgpiod: Add rust tests
Quoted 18 lines · Viresh Kumar
Quoted 8 lines · Kent Gibson
Quoted 8 lines
bindings/rust/tests/line_request.rs +7
-0+#[test] +fn set_bias () { +let offsets = [3 ]; +let mut config =TestConfig ::new (NGPIO ).unwrap (); + config.rconfig (Some (&offsets)); + config.lconfig (Some (Direction ::Input ),None ,None ,None ,Some (Bias ::PullUp )); + config.request_lines ().unwrap ();Quoted 8 lines
bindings/rust/tests/line_request.rs line 233
Quoted 6 lines
bindings/rust/tests/line_request.rs +6
-0+ config.request (); + +// Set single value +assert_eq! (config.sim ().val (3 ).unwrap (),GPIOSIM_VALUE_ACTIVE ); + } + }Test
reconfigure()failure modes.
Quoted 3 lines
What are we specifically looking to test here ? I am not sure I understood which failures modes I should target.
reconfigure() can return E2BIG if the configuration is too complex to encode into uAPI structures. There are only 10 attribute slots in the uAPI config, so more than 11 lines with distinct configs can't be encoded. You want to test that you propagate that error correctly.
I typically test this by giving each line a different debounce value.
Quoted 9 lines
Quoted 2 lines
Test you can reconfigure all values for all attributes (not all values for debounce obviously - just the flags).
This I have done now, with
request.reconfigure_lines().Quoted 1 line
Similarly for
request_lines(ideally inchip.rs)Isn't this same as the above one? I am not sure again what I should test here.
The request_lines() could be considered sufficient to test the conversions for both request_config and line_config objects.
But testing request_lines() and reconfigure_lines() doesn't just test those conversions, it checks that the functions don't mess with the config along the way.
If you want to take that as a given then sure, just testing the config variants for request_lines() will do.
Cheers,
Kent.
Quoted 6 lines
I have pushed my current changes here, if it helps:
[email protected]:vireshk/libgpiod.git master
Signature · 1 line
viresh
Re: [PATCH V4 7/8] libgpiod: Add rust tests
Quoted 8 lines · Kent Gibson
The
request_lines()could be considered sufficient to test the conversions for bothrequest_configandline_configobjects.But testing
request_lines()andreconfigure_lines()doesn't just test those conversions, it checks that the functions don't mess with the config along the way.
If you want to take that as a given then sure, just testing the config variants forrequest_lines()will do.
Okay, so we want test setting various flag types in line_config and then see if request_line() fails or not and verify (read) them later. Right ?
Very similar to this is also done in tests/line_info now, where you suggested:
"Test that you can read all supported values for all fields."
So I set all possible values for fields and then read them back.
Lets see after the next version, if they are sufficient or not. I will happily add more if required then.
Thanks.
Signature · 1 line
viresh
Re: [PATCH V4 7/8] libgpiod: Add rust tests
Quoted 10 lines · Viresh Kumar
Quoted 8 lines · Kent Gibson
The
request_lines()could be considered sufficient to test the conversions for bothrequest_configandline_configobjects.But testing
request_lines()andreconfigure_lines()doesn't just test those conversions, it checks that the functions don't mess with the config along the way.
If you want to take that as a given then sure, just testing the config variants forrequest_lines()will do.
Quoted 4 lines
Okay, so we want test setting various flag types in
line_configand then see ifrequest_line()fails or not and verify (read) them later. Right ?
Right.
Cheers,
Kent.
Quoted 14 lines
Very similar to this is also done in tests/line_info now, where you suggested:
"Test that you can read all supported values for all fields."
So I set all possible values for fields and then read them back.
Lets see after the next version, if they are sufficient or not. I will happily add more if required then.
Thanks.
Signature · 1 line
viresh
PATCH v4 8/8 libgpiod: Integrate building of rust bindings with make
In reply to Viresh KumarLets make build rust bindings as well.
--- README | 8 +++++--- TODO | 8 -------- bindings/Makefile.am | 6 ++++++ bindings/rust/Makefile.am | 18 ++++++++++++++++++ configure.ac | 16 ++++++++++++++++ 5 files changed, 45 insertions(+), 11 deletions(-) create mode 100644 bindings/rust/Makefile.am
README +5 -3
@@ -119,9 +119,9 @@ TOOLS BINDINGS -------- -High-level, object-oriented bindings for C++ and python3 are provided. They -can be enabled by passing --enable-bindings-cxx and --enable-bindings-python -arguments respectively to configure. +High-level, object-oriented bindings for C++, python3 and Rust are provided. +They can be enabled by passing --enable-bindings-cxx, --enable-bindings-python +and --enable-bindings-rust arguments respectively to configure. C++ bindings require C++11 support and autoconf-archive collection if building from git.
@@ -132,6 +132,8 @@ the PYTHON_CPPFLAGS and PYTHON_LIBS variables in order to point the build system to the correct locations. During native builds, the configure script can auto-detect the location of the development files. +Rust bindings require cargo support. + TESTING -------
TODO +0 -8
@@ -28,14 +28,6 @@ and is partially functional. ---------- -* implement rust bindings - -With Rust gaining popularity as a low-level system's language and the -possibility of it making its way into the linux kernel, it's probably time to -provide Rust bindings to libgpiod as part of the project. - ----------- - * implement a simple daemon for controlling GPIOs in C together with a client program
bindings/Makefile.am +6 -0
@@ -14,3 +14,9 @@ if WITH_BINDINGS_PYTHON SUBDIRS += python endif + +if WITH_BINDINGS_RUST + +SUBDIRS += rust + +endif
bindings/rust/Makefile.am new file +18 -0
@@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# SPDX-FileCopyrightText: 2022 Viresh Kumar <[email protected]> + +command = cargo build --release --lib + +if WITH_TESTS +command += --tests +endif + +if WITH_EXAMPLES +command += --examples +endif + +all: + $(command) + +clean: + cargo clean
configure.ac +16 -0
@@ -211,6 +211,21 @@ then [AC_SUBST(PYTHON_LIBS, [`$PYTHON-config --libs`])]) fi +AC_ARG_ENABLE([bindings-rust], + [AS_HELP_STRING([--enable-bindings-rust],[enable rust bindings [default=no]])], + [if test "x$enableval" = xyes; then with_bindings_rust=true; fi], + [with_bindings_rust=false]) +AM_CONDITIONAL([WITH_BINDINGS_RUST], [test "x$with_bindings_rust" = xtrue]) + +if test "x$with_bindings_rust" = xtrue +then + AC_CHECK_PROG([has_cargo], [cargo], [true], [false]) + if test "x$has_cargo" = xfalse + then + AC_MSG_ERROR([cargo not found - needed for rust bindings]) + fi +fi + AC_CHECK_PROG([has_doxygen], [doxygen], [true], [false]) AM_CONDITIONAL([HAS_DOXYGEN], [test "x$has_doxygen" = xtrue]) if test "x$has_doxygen" = xfalse
@@ -245,6 +260,7 @@ AC_CONFIG_FILES([Makefile bindings/python/Makefile bindings/python/examples/Makefile bindings/python/tests/Makefile + bindings/rust/Makefile man/Makefile]) AC_OUTPUT
Signature · 1 line
2.31.1.272.g89b43f80a514
Re: [PATCH V4 8/8] libgpiod: Integrate building of rust bindings with make
Quoted 132 lines · Viresh Kumar
Lets make build rust bindings as well.
Signed-off-by Viresh Kumar <[email protected]>--- README | 8 +++++--- TODO | 8 -------- bindings/Makefile.am | 6 ++++++ bindings/rust/Makefile.am | 18 ++++++++++++++++++ configure.ac | 16 ++++++++++++++++ 5 files changed, 45 insertions(+), 11 deletions(-) create mode 100644 bindings/rust/Makefile.amREADME +5
-3@@ -119,9 +119,9 @@ TOOLS BINDINGS -------- -High-level, object-oriented bindings for C++ and python3 are provided. They -can be enabled by passing --enable-bindings-cxx and --enable-bindings-python -arguments respectively to configure. +High-level, object-oriented bindings for C++, python3 and Rust are provided. +They can be enabled by passing --enable-bindings-cxx, --enable-bindings-python +and --enable-bindings-rust arguments respectively to configure. C++ bindings require C++11 support and autoconf-archive collection if building from git.@@ -132,6 +132,8 @@ the PYTHON_CPPFLAGS and PYTHON_LIBS variables in order to point the build system to the correct locations. During native builds, the configure script can auto-detect the location of the development files. +Rust bindings require cargo support. + TESTING -------TODO +0
-8@@ -28,14 +28,6 @@ and is partially functional. ---------- -* implement rust bindings - -With Rust gaining popularity as a low-level system's language and the -possibility of it making its way into the linux kernel, it's probably time to -provide Rust bindings to libgpiod as part of the project. - ----------- - * implement a simple daemon for controlling GPIOs in C together with a client programbindings/Makefile.am +6
-0@@ -14,3 +14,9 @@ if WITH_BINDINGS_PYTHON SUBDIRS += python endif + +if WITH_BINDINGS_RUST + +SUBDIRS += rust + +endifbindings/rust/Makefile.am new file +18
-0@@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# SPDX-FileCopyrightText: 2022 Viresh Kumar <[email protected]> + +command = cargo build --release --lib + +if WITH_TESTS +command += --tests +endif + +if WITH_EXAMPLES +command += --examples +endif + +all: + $(command) + +clean: + cargo cleanconfigure.ac +16
-0@@ -211,6 +211,21 @@ then [AC_SUBST(PYTHON_LIBS, [`$PYTHON-config --libs`])]) fi +AC_ARG_ENABLE([bindings-rust], + [AS_HELP_STRING([--enable-bindings-rust],[enable rust bindings [default=no]])], + [if test "x$enableval" = xyes; then with_bindings_rust=true; fi], + [with_bindings_rust=false]) +AM_CONDITIONAL([WITH_BINDINGS_RUST], [test "x$with_bindings_rust" = xtrue]) + +if test "x$with_bindings_rust" = xtrue +then + AC_CHECK_PROG([has_cargo], [cargo], [true], [false]) + if test "x$has_cargo" = xfalse + then + AC_MSG_ERROR([cargo not found - needed for rust bindings]) + fi +fi + AC_CHECK_PROG([has_doxygen], [doxygen], [true], [false]) AM_CONDITIONAL([HAS_DOXYGEN], [test "x$has_doxygen" = xtrue]) if test "x$has_doxygen" = xfalse@@ -245,6 +260,7 @@ AC_CONFIG_FILES([Makefile bindings/python/Makefile bindings/python/examples/Makefile bindings/python/tests/Makefile + bindings/rust/Makefile man/Makefile]) AC_OUTPUTSignature · 1 line
2.31.1.272.g89b43f80a514
Wouldn't build for me on a Debian bullseye VM.
Apparently bindgen requires clang to find the system headers [1][2], and there is no dep check or warning about that.
Also not sure why the build wanted bindgen, as by default it uses the pre-generated bindings?
Anyway, for reference this was the build error without clang installed:
...
Compiling thiserror v1.0.31
Compiling libgpiod-sys v0.1.0 (/home/dev/libgpiod/bindings/rust/libgpiod-sys)
error: failed to run custom build command for libgpiod-sys v0.1.0 (/home/dev/libgpiod/bindings/rust/libgpiod-sys)
Caused by:
process didn't exit successfully: /home/dev/libgpiod/bindings/rust/target/release/build/libgpiod-sys-0fb8ce8170c88d8f/build-script-build (exit status: 101) --- stdout
cargo:rerun-if-changed=wrapper.h
cargo:rerun-if-changed=../../../lib/chip.c
cargo:rerun-if-changed=../../../lib/chip-info.c
cargo:rerun-if-changed=../../../lib/edge-event.c
cargo:rerun-if-changed=../../../lib/info-event.c
cargo:rerun-if-changed=../../../lib/internal.c
cargo:rerun-if-changed=../../../lib/line-config.c
cargo:rerun-if-changed=../../../lib/line-info.c
cargo:rerun-if-changed=../../../lib/line-request.c
cargo:rerun-if-changed=../../../lib/misc.c
cargo:rerun-if-changed=../../../lib/request-config.c
cargo:rerun-if-changed=../../../tests/gpiosim/gpiosim.c
cargo:rerun-if-changed=gpiosim_wrapper.h
cargo:rustc-link-lib=kmod
cargo:rustc-link-lib=mount
--- stderr
/usr/include/string.h:33:10: fatal error: 'stddef.h' file not found /usr/include/string.h:33:10: fatal error: 'stddef.h' file not found, err: true thread 'main' panicked at 'Unable to generate bindings: ()', libgpiod-sys/build.rs:42:10
Cheers,
Kent.
[1] https://rust-lang.github.io/rust-bindgen/requirements.html
[2] https://github.com/rust-lang/rust-bindgen/issues/242
Re: [PATCH V4 8/8] libgpiod: Integrate building of rust bindings with make
Quoted 2 lines · Kent Gibson
Wouldn't build for me on a Debian bullseye VM.
Apparently bindgen requires clang to find the system headers [1][2],
Right.
Quoted 2 lines
and
there is no dep check or warning about that.
Ahh, I need to add that somewhere then.
Quoted 2 lines
Also not sure why the build wanted bindgen, as by default it uses the pre-generated bindings?
Did you enable tests as well ? That enables "gpiosim", which enables "generate" and bindgen will be required.
I thought it will be better to build bindings everytime for tests.
Quoted 1 line
Anyway, for reference this was the build error without clang installed:
Did it work for you after clang was installed ?
Quoted 29 lines
...
Compiling thiserror v1.0.31
Compiling libgpiod-sys v0.1.0 (/home/dev/libgpiod/bindings/rust/libgpiod-sys)
error: failed to run custom build command forlibgpiod-sys v0.1.0 (/home/dev/libgpiod/bindings/rust/libgpiod-sys)Caused by:
process didn't exit successfully:/home/dev/libgpiod/bindings/rust/target/release/build/libgpiod-sys-0fb8ce8170c88d8f/build-script-build(exit status: 101) --- stdout
cargo:rerun-if-changed=wrapper.h
cargo:rerun-if-changed=../../../lib/chip.c
cargo:rerun-if-changed=../../../lib/chip-info.c
cargo:rerun-if-changed=../../../lib/edge-event.c
cargo:rerun-if-changed=../../../lib/info-event.c
cargo:rerun-if-changed=../../../lib/internal.c
cargo:rerun-if-changed=../../../lib/line-config.c
cargo:rerun-if-changed=../../../lib/line-info.c
cargo:rerun-if-changed=../../../lib/line-request.c
cargo:rerun-if-changed=../../../lib/misc.c
cargo:rerun-if-changed=../../../lib/request-config.c
cargo:rerun-if-changed=../../../tests/gpiosim/gpiosim.c
cargo:rerun-if-changed=gpiosim_wrapper.h
cargo:rustc-link-lib=kmod
cargo:rustc-link-lib=mount--- stderr
/usr/include/string.h:33:10: fatal error: 'stddef.h' file not found/usr/include/string.h:33:10: fatal error: 'stddef.h' file not found, err: true thread 'main' panicked at 'Unable to generate bindings: ()',libgpiod-sys/build.rs:42:10
Signature · 1 line
viresh
Re: [PATCH V4 8/8] libgpiod: Integrate building of rust bindings with make
Quoted 9 lines · Viresh Kumar
Quoted 2 lines · Kent Gibson
Wouldn't build for me on a Debian bullseye VM.
Apparently bindgen requires clang to find the system headers [1][2],Right.
Quoted 2 lines
and
there is no dep check or warning about that.
Quoted 7 lines
Ahh, I need to add that somewhere then.
Quoted 2 lines
Also not sure why the build wanted bindgen, as by default it uses the pre-generated bindings?
Did you enable tests as well ? That enables "gpiosim", which enables "generate" and bindgen will be required.
Ahh, yeah I do --enable-tests as well.
Quoted 3 lines
I thought it will be better to build bindings everytime for tests.
So you can't run tests on those platforms where bindgen is problematic?
Quoted 4 lines
Quoted 1 line
Anyway, for reference this was the build error without clang installed:
Did it work for you after clang was installed ?
Yeah, all good once clang was installed.
Cheers,
Kent.
Quoted 32 lines
Quoted 29 lines
...
Compiling thiserror v1.0.31
Compiling libgpiod-sys v0.1.0 (/home/dev/libgpiod/bindings/rust/libgpiod-sys)
error: failed to run custom build command forlibgpiod-sys v0.1.0 (/home/dev/libgpiod/bindings/rust/libgpiod-sys)Caused by:
process didn't exit successfully:/home/dev/libgpiod/bindings/rust/target/release/build/libgpiod-sys-0fb8ce8170c88d8f/build-script-build(exit status: 101) --- stdout
cargo:rerun-if-changed=wrapper.h
cargo:rerun-if-changed=../../../lib/chip.c
cargo:rerun-if-changed=../../../lib/chip-info.c
cargo:rerun-if-changed=../../../lib/edge-event.c
cargo:rerun-if-changed=../../../lib/info-event.c
cargo:rerun-if-changed=../../../lib/internal.c
cargo:rerun-if-changed=../../../lib/line-config.c
cargo:rerun-if-changed=../../../lib/line-info.c
cargo:rerun-if-changed=../../../lib/line-request.c
cargo:rerun-if-changed=../../../lib/misc.c
cargo:rerun-if-changed=../../../lib/request-config.c
cargo:rerun-if-changed=../../../tests/gpiosim/gpiosim.c
cargo:rerun-if-changed=gpiosim_wrapper.h
cargo:rustc-link-lib=kmod
cargo:rustc-link-lib=mount--- stderr
/usr/include/string.h:33:10: fatal error: 'stddef.h' file not found/usr/include/string.h:33:10: fatal error: 'stddef.h' file not found, err: true thread 'main' panicked at 'Unable to generate bindings: ()',libgpiod-sys/build.rs:42:10Signature · 1 line
viresh
Re: [PATCH V4 8/8] libgpiod: Integrate building of rust bindings with make
Quoted 1 line · Kent Gibson
So you can't run tests on those platforms where bindgen is problematic?
Specifically for my case, that environment (rust-vmm-container) never runs libgpiod tests, but just vhost-device/gpio tests. So it works.
Signature · 1 line
viresh
Re: [PATCH V4 8/8] libgpiod: Integrate building of rust bindings with make
Quoted 6 lines · Viresh Kumar
Quoted 1 line · Kent Gibson
So you can't run tests on those platforms where bindgen is problematic?
Specifically for my case, that environment (rust-vmm-container) never runs libgpiod tests, but just vhost-device/gpio tests. So it works.
That may work for you, but in general it would be better if the tests can be run even if the bindings can't be regenerated, so the two should be independent.
Cheers,
Kent.
Re: [PATCH V4 8/8] libgpiod: Integrate building of rust bindings with make
Quoted 11 lines · Kent Gibson
Quoted 6 lines · Viresh Kumar
Quoted 1 line · Kent Gibson
So you can't run tests on those platforms where bindgen is problematic?
Specifically for my case, that environment (rust-vmm-container) never runs libgpiod tests, but just vhost-device/gpio tests. So it works.
That may work for you, but in general it would be better if the tests can be run even if the bindings can't be regenerated, so the two should be independent.
Hmm, then we can take the same approach as for gpiod.h bindings. Use pre-compiled ones unless "generate" feature is enabled.
Will do.
Signature · 1 line
viresh
Quoted 37 lines · Viresh Kumar
Hello,
Here is another version of rust bindings for libgpiod v2.0, based of the next/libgpiod-2.0.
Pushed here:
https://github.com/vireshk/libgpiod master
V3->V4:
- Rebased on top of new changes, and made changes accordingly.
- Added rust integration tests with gpiosim.
- Found a kernel bug with tests, sent a patch for that to LKML.
V2->V3:
- Remove naming redundancy, users just need to do this now
use libgpiod:{Chip, Direction, LineConfig} now (Bartosz);
- Fix lifetime issues between event-buffer and edge-event modules, the event buffer is released after the last edge-event reference is dropped (Bartosz).
- Allow edge-event to be copied, and freed later (Bartosz).
- Add two separate rust crates, sys and wrapper (Gerard).
- Null-terminate the strings passed to libgpiod (Wedson).
- Drop unnecessary checks to validate string returned from chip:name/label/path.
- Fix SAFETY comments (Wedson).
- Drop unnecessaryclone()instances (Bartosz).
V1->V2:
- Added examples (I tested everything exceptgpiomon.rs, didn't have right hardware/mock device to test).
- Build rust bindings as part of Make, update documentation.Thanks.
--
Viresh
Quoted 10 lines
Viresh Kumar (8):
libgpiod: Add libgpiod-sys rust crate
libgpiod: Add pre generated rust bindings
libgpiod-sys: Add support to generate gpiosim bindings
libgpiod: Add rust wrapper crate
libgpiod: Add rust examples
libgpiod: Derive debug traits for few definitions
libgpiod: Add rust tests
libgpiod: Integrate building of rust bindings with make
Hey Viresh, Kent and Miguel!
Rust noob here: I have my cargo installed using rustup for my local user but I can't run it via sudo or as root (to run tests) because I'm seeing this:
error: no override and no default toolchain set
What is the right way to use cargo as root? I'm hesitant to just curl a random script and pipe it to shell as root honestly.
Bart
Hi Bartosz,
Quoted 9 lines · Bartosz Golaszewski
Rust noob here: I have my cargo installed using rustup for my local user but I can't run it via sudo or as root (to run tests) because I'm seeing this:
error: no override and no default toolchain set
What is the right way to use cargo as root? I'm hesitant to just curl a random script and pipe it to shell as root honestly.
If by "random script" you mean the rustup installer, you can always download rustup on its own, or even directly a Rust "standalone installer", which are signed and do not require rustup, from:
https://forge.rust-lang.org/infra/other-installation-methods.html#standalone
However, since it appears that you have rustup installed, maybe you don't mean that. In any case, if rustup is installed, maybe you can try something like:
rustup default stable
or similar to install a toolchain.
Cheers,
Miguel
Quoted 7 lines · Miguel Ojeda
If by "random script" you mean the rustup installer, you can always download rustup on its own, or even directly a Rust "standalone installer", which are signed and do not require rustup, from:
https://forge.rust-lang.org/infra/other-installation-methods.html#standalone
Quoted 7 lines
However, since it appears that you have rustup installed, maybe you don't mean that. In any case, if rustup is installed, maybe you can try something like:
rustup default stable
or similar to install a toolchain.
Having said that, if building as a normal user is OK (should be), then you maybe can simply run the test binary as root (building it with something like cargo --no-run as a normal user).
Cheers,
Miguel
Quoted 2 lines · Miguel Ojeda
something like
cargo --no-runas a normal user).
Sorry, that was intended to be cargo test --no-run (and the test binary will be in target/debug/deps). Also note that is only for the unit tests, though, not doctests. It can get more involved for those if you want to avoid to run Cargo/rustdoc as root.
Hope that helps!
Cheers,
Miguel
Quoted 11 lines · Miguel Ojeda
Quoted 2 lines · Miguel Ojeda
something like
cargo --no-runas a normal user).Sorry, that was intended to be
cargo test --no-run(and the test binary will be intarget/debug/deps). Also note that is only for the unit tests, though, not doctests. It can get more involved for those if you want to avoid to run Cargo/rustdoc as root.
Quoted 4 lines
Hope that helps!
Cheers,
Miguel
This does seem to build the test-suite but there's no single executable in target/debug/deps. Instead it seems that every test section gets its own executable. Does 'cargo test' run them separately? Or is there a single binary including all of them somewhere else?
Bart
Quoted 13 lines · Bartosz Golaszewski
Quoted 11 lines · Miguel Ojeda
Quoted 2 lines · Miguel Ojeda
something like
cargo --no-runas a normal user).Sorry, that was intended to be
cargo test --no-run(and the test binary will be intarget/debug/deps). Also note that is only for the unit tests, though, not doctests. It can get more involved for those if you want to avoid to run Cargo/rustdoc as root.
Quoted 7 lines
Quoted 4 lines
Hope that helps!
Cheers,
MiguelThis does seem to build the test-suite but there's no single executable in target/debug/deps.
As does providing the --enable-tests to configure, btw.
Quoted 5 lines
Instead it seems that every test
section gets its own executable. Does 'cargo test' run them separately? Or is there a single binary including all of them somewhere else?
AIUI 'cargo test' builds and runs those tests individually.
As we need to run the integration tests as root, and cargo doesn't play nice unless you install it as root, and the executables include md5(?) hashes in their names, it is a major PITA to run the full suite as root. Unless you do everything as root.
Anyway to get those names from cargo?
Assuming the gpio-sim module is already loaded, is it at all possible to run tests as a low privilege user? e.g. udev rules can be added to chown the /dev/gpiochips into a gpio group. Is it possible to do something similar for the configfs and sysfs for the gpio-sim?
Or do we need a gpiosimd that will allow a user to launch and control sims without being root ;-).
Cheers,
Kent.
Quoted 6 lines · Bartosz Golaszewski
This does seem to build the test-suite but there's no single executable in target/debug/deps. Instead it seems that every test section gets its own executable. Does 'cargo test' run them separately? Or is there a single binary including all of them somewhere else?
Yeah, for tests/ tests it seems to create several binaries. For #[test]s, they are put in a single binary.
Another solution for both of those (from
https://github.com/rust-lang/cargo/issues/5999) is .cargo/config (in the project folder):
[target.x86_64-unknown-linux-gnu]
runner = 'sudo -E'
Which indeed seems to work for both tests/ and #[test]s.
However, I don't think that one works for doctests, though (at least I don't see it executing rustdoc with the runner)... But maybe you don't need privileges for those.
Cheers,
Miguel