All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH V7 0/8] libgpiod: Add Rust bindings
@ 2022-10-14 10:47 Viresh Kumar
  2022-10-14 10:47 ` [PATCH V7 1/8] libgpiod: Add libgpiod-sys rust crate Viresh Kumar
                   ` (8 more replies)
  0 siblings, 9 replies; 50+ messages in thread
From: Viresh Kumar @ 2022-10-14 10:47 UTC (permalink / raw)
  To: Linus Walleij, Bartosz Golaszewski
  Cc: Viresh Kumar, Vincent Guittot, linux-gpio, Kent Gibson,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

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 v7

[I have pushed v6 and v5 there too, in case someone wants to look at the
changes].

V6->V7:
- Don't let buffer read new events if the earlier events are still referenced.
- BufferIntenal is gone now, to make the above work.
- Update example and tests too for the same.

V5->V6:
- Updates according to the new line-settings interface.
- New file, line_settings.rs.
- Renamed 'enum Setting' as 'SettingVal' to avoid conflicting names, as we also
  have 'struct Settings' now.
- Support for HTE clock type.
- Implement 'Eq' for public structure/enums (reported by build).
- Remove 'SettingKindMap' and 'SettingMap' as they aren't required anymore.
- Updated tests based on new interface.

V4->V5:
- Arrange as workspace with crates for libgpiod-sys, libgpiod, gpiosim.
- Use static libgpiod and libgpiosim libraries instead of rebuilding again.
- Arrange in modules instead of flattened approach.
- New enums like Setting and SettingKind and new types based on them SettingMap
  and SettingKindMap.
- New property independent helpers for line_config, like set_prop_default().
- Improved tests/examples, new example for gpiowatch.
- Add pre-built bindings for gpiosim too.
- Many other changes.

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-sys: Add pre generated rust bindings
  libgpiod: Add rust wrapper crate
  libgpiod: Add rust examples
  libgpiod: Add gpiosim rust crate
  gpiosim: Add pre generated rust bindings
  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                      |    7 +
 bindings/rust/Makefile.am                     |   18 +
 bindings/rust/gpiosim/Cargo.toml              |   18 +
 bindings/rust/gpiosim/README.md               |   11 +
 bindings/rust/gpiosim/build.rs                |   43 +
 bindings/rust/gpiosim/src/bindings.rs         |  180 +++
 bindings/rust/gpiosim/src/lib.rs              |   25 +
 bindings/rust/gpiosim/src/sim.rs              |  323 +++++
 bindings/rust/libgpiod-sys/Cargo.toml         |   15 +
 bindings/rust/libgpiod-sys/README.md          |   11 +
 bindings/rust/libgpiod-sys/build.rs           |   41 +
 bindings/rust/libgpiod-sys/src/bindings.rs    | 1173 +++++++++++++++++
 bindings/rust/libgpiod-sys/src/lib.rs         |   13 +
 bindings/rust/libgpiod/Cargo.toml             |   16 +
 bindings/rust/libgpiod/examples/gpiodetect.rs |   30 +
 bindings/rust/libgpiod/examples/gpiofind.rs   |   35 +
 bindings/rust/libgpiod/examples/gpioget.rs    |   42 +
 bindings/rust/libgpiod/examples/gpioinfo.rs   |   95 ++
 bindings/rust/libgpiod/examples/gpiomon.rs    |   73 +
 bindings/rust/libgpiod/examples/gpioset.rs    |   60 +
 bindings/rust/libgpiod/examples/gpiowatch.rs  |   54 +
 bindings/rust/libgpiod/src/chip.rs            |  253 ++++
 bindings/rust/libgpiod/src/edge_event.rs      |  101 ++
 bindings/rust/libgpiod/src/event_buffer.rs    |   66 +
 bindings/rust/libgpiod/src/info_event.rs      |   68 +
 bindings/rust/libgpiod/src/lib.rs             |  471 +++++++
 bindings/rust/libgpiod/src/line_config.rs     |  118 ++
 bindings/rust/libgpiod/src/line_info.rs       |  180 +++
 bindings/rust/libgpiod/src/line_request.rs    |  246 ++++
 bindings/rust/libgpiod/src/line_settings.rs   |  277 ++++
 bindings/rust/libgpiod/src/request_config.rs  |   96 ++
 bindings/rust/libgpiod/tests/chip.rs          |   97 ++
 bindings/rust/libgpiod/tests/common/config.rs |  134 ++
 bindings/rust/libgpiod/tests/common/mod.rs    |   10 +
 bindings/rust/libgpiod/tests/edge_event.rs    |  296 +++++
 bindings/rust/libgpiod/tests/info_event.rs    |  125 ++
 bindings/rust/libgpiod/tests/line_config.rs   |  105 ++
 bindings/rust/libgpiod/tests/line_info.rs     |  279 ++++
 bindings/rust/libgpiod/tests/line_request.rs  |  519 ++++++++
 bindings/rust/libgpiod/tests/line_settings.rs |  226 ++++
 .../rust/libgpiod/tests/request_config.rs     |   38 +
 configure.ac                                  |   16 +
 46 files changed, 6020 insertions(+), 11 deletions(-)
 create mode 100644 bindings/rust/Cargo.toml
 create mode 100644 bindings/rust/Makefile.am
 create mode 100644 bindings/rust/gpiosim/Cargo.toml
 create mode 100644 bindings/rust/gpiosim/README.md
 create mode 100644 bindings/rust/gpiosim/build.rs
 create mode 100644 bindings/rust/gpiosim/src/bindings.rs
 create mode 100644 bindings/rust/gpiosim/src/lib.rs
 create mode 100644 bindings/rust/gpiosim/src/sim.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/src/bindings.rs
 create mode 100644 bindings/rust/libgpiod-sys/src/lib.rs
 create mode 100644 bindings/rust/libgpiod/Cargo.toml
 create mode 100644 bindings/rust/libgpiod/examples/gpiodetect.rs
 create mode 100644 bindings/rust/libgpiod/examples/gpiofind.rs
 create mode 100644 bindings/rust/libgpiod/examples/gpioget.rs
 create mode 100644 bindings/rust/libgpiod/examples/gpioinfo.rs
 create mode 100644 bindings/rust/libgpiod/examples/gpiomon.rs
 create mode 100644 bindings/rust/libgpiod/examples/gpioset.rs
 create mode 100644 bindings/rust/libgpiod/examples/gpiowatch.rs
 create mode 100644 bindings/rust/libgpiod/src/chip.rs
 create mode 100644 bindings/rust/libgpiod/src/edge_event.rs
 create mode 100644 bindings/rust/libgpiod/src/event_buffer.rs
 create mode 100644 bindings/rust/libgpiod/src/info_event.rs
 create mode 100644 bindings/rust/libgpiod/src/lib.rs
 create mode 100644 bindings/rust/libgpiod/src/line_config.rs
 create mode 100644 bindings/rust/libgpiod/src/line_info.rs
 create mode 100644 bindings/rust/libgpiod/src/line_request.rs
 create mode 100644 bindings/rust/libgpiod/src/line_settings.rs
 create mode 100644 bindings/rust/libgpiod/src/request_config.rs
 create mode 100644 bindings/rust/libgpiod/tests/chip.rs
 create mode 100644 bindings/rust/libgpiod/tests/common/config.rs
 create mode 100644 bindings/rust/libgpiod/tests/common/mod.rs
 create mode 100644 bindings/rust/libgpiod/tests/edge_event.rs
 create mode 100644 bindings/rust/libgpiod/tests/info_event.rs
 create mode 100644 bindings/rust/libgpiod/tests/line_config.rs
 create mode 100644 bindings/rust/libgpiod/tests/line_info.rs
 create mode 100644 bindings/rust/libgpiod/tests/line_request.rs
 create mode 100644 bindings/rust/libgpiod/tests/line_settings.rs
 create mode 100644 bindings/rust/libgpiod/tests/request_config.rs

-- 
2.31.1.272.g89b43f80a514


^ permalink raw reply	[flat|nested] 50+ messages in thread

* [PATCH V7 1/8] libgpiod: Add libgpiod-sys rust crate
  2022-10-14 10:47 [PATCH V7 0/8] libgpiod: Add Rust bindings Viresh Kumar
@ 2022-10-14 10:47 ` Viresh Kumar
  2022-10-17 12:59   ` Kent Gibson
  2022-10-14 10:47 ` [PATCH V7 2/8] libgpiod-sys: Add pre generated rust bindings Viresh Kumar
                   ` (7 subsequent siblings)
  8 siblings, 1 reply; 50+ messages in thread
From: Viresh Kumar @ 2022-10-14 10:47 UTC (permalink / raw)
  To: Linus Walleij, Bartosz Golaszewski
  Cc: Viresh Kumar, Vincent Guittot, linux-gpio, Kent Gibson,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

This adds libgpiod-sys rust crate, which provides FFI (foreign function
interface) bindings for libgpiod APIs.

Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
---
 .gitignore                            |  5 ++++
 bindings/rust/Cargo.toml              |  5 ++++
 bindings/rust/libgpiod-sys/Cargo.toml | 15 ++++++++++
 bindings/rust/libgpiod-sys/build.rs   | 41 +++++++++++++++++++++++++++
 bindings/rust/libgpiod-sys/src/lib.rs | 13 +++++++++
 5 files changed, 79 insertions(+)
 create mode 100644 bindings/rust/Cargo.toml
 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

diff --git a/.gitignore b/.gitignore
index 6c08415b390d..9f2fcf440c5d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,3 +35,8 @@ stamp-h1
 # profiling
 *.gcda
 *.gcno
+
+# Added by cargo
+
+target
+Cargo.lock
diff --git a/bindings/rust/Cargo.toml b/bindings/rust/Cargo.toml
new file mode 100644
index 000000000000..d0b3a3c88ff1
--- /dev/null
+++ b/bindings/rust/Cargo.toml
@@ -0,0 +1,5 @@
+[workspace]
+
+members = [
+    "libgpiod-sys",
+]
diff --git a/bindings/rust/libgpiod-sys/Cargo.toml b/bindings/rust/libgpiod-sys/Cargo.toml
new file mode 100644
index 000000000000..77f82719d269
--- /dev/null
+++ b/bindings/rust/libgpiod-sys/Cargo.toml
@@ -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"
diff --git a/bindings/rust/libgpiod-sys/build.rs b/bindings/rust/libgpiod-sys/build.rs
new file mode 100644
index 000000000000..98863686c7af
--- /dev/null
+++ b/bindings/rust/libgpiod-sys/build.rs
@@ -0,0 +1,41 @@
+#[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() {
+    // Tell cargo to invalidate the built crate whenever following files change
+    println!("cargo:rerun-if-changed=../../../include/gpiod.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()
+        // The input header we would like to generate
+        // bindings for.
+        .header("../../../include/gpiod.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 main() {
+    #[cfg(feature = "generate")]
+    generate_bindings();
+
+    println!("cargo:rustc-link-search=./../../lib/.libs/");
+    println!("cargo:rustc-link-lib=static=gpiod");
+}
diff --git a/bindings/rust/libgpiod-sys/src/lib.rs b/bindings/rust/libgpiod-sys/src/lib.rs
new file mode 100644
index 000000000000..1ca355b5f5ac
--- /dev/null
+++ b/bindings/rust/libgpiod-sys/src/lib.rs
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#[allow(non_camel_case_types, non_upper_case_globals)]
+#[cfg_attr(test, allow(deref_nullptr, non_snake_case))]
+
+mod bindings_raw {
+    #[cfg(feature = "generate")]
+    include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
+
+    #[cfg(not(feature = "generate"))]
+    include!("bindings.rs");
+}
+pub use bindings_raw::*;
-- 
2.31.1.272.g89b43f80a514


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH V7 2/8] libgpiod-sys: Add pre generated rust bindings
  2022-10-14 10:47 [PATCH V7 0/8] libgpiod: Add Rust bindings Viresh Kumar
  2022-10-14 10:47 ` [PATCH V7 1/8] libgpiod: Add libgpiod-sys rust crate Viresh Kumar
@ 2022-10-14 10:47 ` Viresh Kumar
  2022-10-14 10:47 ` [PATCH V7 3/8] libgpiod: Add rust wrapper crate Viresh Kumar
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 50+ messages in thread
From: Viresh Kumar @ 2022-10-14 10:47 UTC (permalink / raw)
  To: Linus Walleij, Bartosz Golaszewski
  Cc: Viresh Kumar, Vincent Guittot, linux-gpio, Kent Gibson,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

This adds a copy of pre generated bindings and adds the suggested way of
updating those in README.

Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
---
 bindings/rust/libgpiod-sys/README.md       |   11 +
 bindings/rust/libgpiod-sys/src/bindings.rs | 1173 ++++++++++++++++++++
 2 files changed, 1184 insertions(+)
 create mode 100644 bindings/rust/libgpiod-sys/README.md
 create mode 100644 bindings/rust/libgpiod-sys/src/bindings.rs

diff --git a/bindings/rust/libgpiod-sys/README.md b/bindings/rust/libgpiod-sys/README.md
new file mode 100644
index 000000000000..ecf75b31c41e
--- /dev/null
+++ b/bindings/rust/libgpiod-sys/README.md
@@ -0,0 +1,11 @@
+# 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. Copy the bindings 'cp ../target/debug/build/libgpiod-sys-###/out/bindings.rs src/bindings.rs'
+4. Commit changes in `src/bindings.rs`
diff --git a/bindings/rust/libgpiod-sys/src/bindings.rs b/bindings/rust/libgpiod-sys/src/bindings.rs
new file mode 100644
index 000000000000..fc2ffa02c3c4
--- /dev/null
+++ b/bindings/rust/libgpiod-sys/src/bindings.rs
@@ -0,0 +1,1173 @@
+/* automatically generated by rust-bindgen 0.59.2 */
+
+pub const true_: u32 = 1;
+pub const false_: u32 = 0;
+pub const __bool_true_false_are_defined: u32 = 1;
+pub const _STDINT_H: u32 = 1;
+pub const _FEATURES_H: u32 = 1;
+pub const _DEFAULT_SOURCE: u32 = 1;
+pub const __GLIBC_USE_ISOC2X: u32 = 0;
+pub const __USE_ISOC11: u32 = 1;
+pub const __USE_ISOC99: u32 = 1;
+pub const __USE_ISOC95: u32 = 1;
+pub const __USE_POSIX_IMPLICITLY: u32 = 1;
+pub const _POSIX_SOURCE: u32 = 1;
+pub const _POSIX_C_SOURCE: u32 = 200809;
+pub const __USE_POSIX: u32 = 1;
+pub const __USE_POSIX2: u32 = 1;
+pub const __USE_POSIX199309: u32 = 1;
+pub const __USE_POSIX199506: u32 = 1;
+pub const __USE_XOPEN2K: u32 = 1;
+pub const __USE_XOPEN2K8: u32 = 1;
+pub const _ATFILE_SOURCE: u32 = 1;
+pub const __USE_MISC: u32 = 1;
+pub const __USE_ATFILE: u32 = 1;
+pub const __USE_FORTIFY_LEVEL: u32 = 0;
+pub const __GLIBC_USE_DEPRECATED_GETS: u32 = 0;
+pub const __GLIBC_USE_DEPRECATED_SCANF: u32 = 0;
+pub const _STDC_PREDEF_H: u32 = 1;
+pub const __STDC_IEC_559__: u32 = 1;
+pub const __STDC_IEC_559_COMPLEX__: u32 = 1;
+pub const __STDC_ISO_10646__: u32 = 201706;
+pub const __GNU_LIBRARY__: u32 = 6;
+pub const __GLIBC__: u32 = 2;
+pub const __GLIBC_MINOR__: u32 = 31;
+pub const _SYS_CDEFS_H: u32 = 1;
+pub const __glibc_c99_flexarr_available: u32 = 1;
+pub const __WORDSIZE: u32 = 64;
+pub const __WORDSIZE_TIME64_COMPAT32: u32 = 1;
+pub const __SYSCALL_WORDSIZE: u32 = 64;
+pub const __LONG_DOUBLE_USES_FLOAT128: u32 = 0;
+pub const __HAVE_GENERIC_SELECTION: u32 = 1;
+pub const __GLIBC_USE_LIB_EXT2: u32 = 0;
+pub const __GLIBC_USE_IEC_60559_BFP_EXT: u32 = 0;
+pub const __GLIBC_USE_IEC_60559_BFP_EXT_C2X: u32 = 0;
+pub const __GLIBC_USE_IEC_60559_FUNCS_EXT: u32 = 0;
+pub const __GLIBC_USE_IEC_60559_FUNCS_EXT_C2X: u32 = 0;
+pub const __GLIBC_USE_IEC_60559_TYPES_EXT: u32 = 0;
+pub const _BITS_TYPES_H: u32 = 1;
+pub const __TIMESIZE: u32 = 64;
+pub const _BITS_TYPESIZES_H: u32 = 1;
+pub const __OFF_T_MATCHES_OFF64_T: u32 = 1;
+pub const __INO_T_MATCHES_INO64_T: u32 = 1;
+pub const __RLIM_T_MATCHES_RLIM64_T: u32 = 1;
+pub const __STATFS_MATCHES_STATFS64: u32 = 1;
+pub const __FD_SETSIZE: u32 = 1024;
+pub const _BITS_TIME64_H: u32 = 1;
+pub const _BITS_WCHAR_H: u32 = 1;
+pub const _BITS_STDINT_INTN_H: u32 = 1;
+pub const _BITS_STDINT_UINTN_H: u32 = 1;
+pub const INT8_MIN: i32 = -128;
+pub const INT16_MIN: i32 = -32768;
+pub const INT32_MIN: i32 = -2147483648;
+pub const INT8_MAX: u32 = 127;
+pub const INT16_MAX: u32 = 32767;
+pub const INT32_MAX: u32 = 2147483647;
+pub const UINT8_MAX: u32 = 255;
+pub const UINT16_MAX: u32 = 65535;
+pub const UINT32_MAX: u32 = 4294967295;
+pub const INT_LEAST8_MIN: i32 = -128;
+pub const INT_LEAST16_MIN: i32 = -32768;
+pub const INT_LEAST32_MIN: i32 = -2147483648;
+pub const INT_LEAST8_MAX: u32 = 127;
+pub const INT_LEAST16_MAX: u32 = 32767;
+pub const INT_LEAST32_MAX: u32 = 2147483647;
+pub const UINT_LEAST8_MAX: u32 = 255;
+pub const UINT_LEAST16_MAX: u32 = 65535;
+pub const UINT_LEAST32_MAX: u32 = 4294967295;
+pub const INT_FAST8_MIN: i32 = -128;
+pub const INT_FAST16_MIN: i64 = -9223372036854775808;
+pub const INT_FAST32_MIN: i64 = -9223372036854775808;
+pub const INT_FAST8_MAX: u32 = 127;
+pub const INT_FAST16_MAX: u64 = 9223372036854775807;
+pub const INT_FAST32_MAX: u64 = 9223372036854775807;
+pub const UINT_FAST8_MAX: u32 = 255;
+pub const UINT_FAST16_MAX: i32 = -1;
+pub const UINT_FAST32_MAX: i32 = -1;
+pub const INTPTR_MIN: i64 = -9223372036854775808;
+pub const INTPTR_MAX: u64 = 9223372036854775807;
+pub const UINTPTR_MAX: i32 = -1;
+pub const PTRDIFF_MIN: i64 = -9223372036854775808;
+pub const PTRDIFF_MAX: u64 = 9223372036854775807;
+pub const SIG_ATOMIC_MIN: i32 = -2147483648;
+pub const SIG_ATOMIC_MAX: u32 = 2147483647;
+pub const SIZE_MAX: i32 = -1;
+pub const WINT_MIN: u32 = 0;
+pub const WINT_MAX: u32 = 4294967295;
+pub type size_t = ::std::os::raw::c_ulong;
+pub type wchar_t = ::std::os::raw::c_int;
+#[repr(C)]
+#[repr(align(16))]
+#[derive(Debug, Copy, Clone)]
+pub struct max_align_t {
+    pub __clang_max_align_nonce1: ::std::os::raw::c_longlong,
+    pub __bindgen_padding_0: u64,
+    pub __clang_max_align_nonce2: u128,
+}
+#[test]
+fn bindgen_test_layout_max_align_t() {
+    assert_eq!(
+        ::std::mem::size_of::<max_align_t>(),
+        32usize,
+        concat!("Size of: ", stringify!(max_align_t))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<max_align_t>(),
+        16usize,
+        concat!("Alignment of ", stringify!(max_align_t))
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<max_align_t>())).__clang_max_align_nonce1 as *const _ as usize
+        },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(max_align_t),
+            "::",
+            stringify!(__clang_max_align_nonce1)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<max_align_t>())).__clang_max_align_nonce2 as *const _ as usize
+        },
+        16usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(max_align_t),
+            "::",
+            stringify!(__clang_max_align_nonce2)
+        )
+    );
+}
+pub type __u_char = ::std::os::raw::c_uchar;
+pub type __u_short = ::std::os::raw::c_ushort;
+pub type __u_int = ::std::os::raw::c_uint;
+pub type __u_long = ::std::os::raw::c_ulong;
+pub type __int8_t = ::std::os::raw::c_schar;
+pub type __uint8_t = ::std::os::raw::c_uchar;
+pub type __int16_t = ::std::os::raw::c_short;
+pub type __uint16_t = ::std::os::raw::c_ushort;
+pub type __int32_t = ::std::os::raw::c_int;
+pub type __uint32_t = ::std::os::raw::c_uint;
+pub type __int64_t = ::std::os::raw::c_long;
+pub type __uint64_t = ::std::os::raw::c_ulong;
+pub type __int_least8_t = __int8_t;
+pub type __uint_least8_t = __uint8_t;
+pub type __int_least16_t = __int16_t;
+pub type __uint_least16_t = __uint16_t;
+pub type __int_least32_t = __int32_t;
+pub type __uint_least32_t = __uint32_t;
+pub type __int_least64_t = __int64_t;
+pub type __uint_least64_t = __uint64_t;
+pub type __quad_t = ::std::os::raw::c_long;
+pub type __u_quad_t = ::std::os::raw::c_ulong;
+pub type __intmax_t = ::std::os::raw::c_long;
+pub type __uintmax_t = ::std::os::raw::c_ulong;
+pub type __dev_t = ::std::os::raw::c_ulong;
+pub type __uid_t = ::std::os::raw::c_uint;
+pub type __gid_t = ::std::os::raw::c_uint;
+pub type __ino_t = ::std::os::raw::c_ulong;
+pub type __ino64_t = ::std::os::raw::c_ulong;
+pub type __mode_t = ::std::os::raw::c_uint;
+pub type __nlink_t = ::std::os::raw::c_ulong;
+pub type __off_t = ::std::os::raw::c_long;
+pub type __off64_t = ::std::os::raw::c_long;
+pub type __pid_t = ::std::os::raw::c_int;
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct __fsid_t {
+    pub __val: [::std::os::raw::c_int; 2usize],
+}
+#[test]
+fn bindgen_test_layout___fsid_t() {
+    assert_eq!(
+        ::std::mem::size_of::<__fsid_t>(),
+        8usize,
+        concat!("Size of: ", stringify!(__fsid_t))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<__fsid_t>(),
+        4usize,
+        concat!("Alignment of ", stringify!(__fsid_t))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<__fsid_t>())).__val as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(__fsid_t),
+            "::",
+            stringify!(__val)
+        )
+    );
+}
+pub type __clock_t = ::std::os::raw::c_long;
+pub type __rlim_t = ::std::os::raw::c_ulong;
+pub type __rlim64_t = ::std::os::raw::c_ulong;
+pub type __id_t = ::std::os::raw::c_uint;
+pub type __time_t = ::std::os::raw::c_long;
+pub type __useconds_t = ::std::os::raw::c_uint;
+pub type __suseconds_t = ::std::os::raw::c_long;
+pub type __daddr_t = ::std::os::raw::c_int;
+pub type __key_t = ::std::os::raw::c_int;
+pub type __clockid_t = ::std::os::raw::c_int;
+pub type __timer_t = *mut ::std::os::raw::c_void;
+pub type __blksize_t = ::std::os::raw::c_long;
+pub type __blkcnt_t = ::std::os::raw::c_long;
+pub type __blkcnt64_t = ::std::os::raw::c_long;
+pub type __fsblkcnt_t = ::std::os::raw::c_ulong;
+pub type __fsblkcnt64_t = ::std::os::raw::c_ulong;
+pub type __fsfilcnt_t = ::std::os::raw::c_ulong;
+pub type __fsfilcnt64_t = ::std::os::raw::c_ulong;
+pub type __fsword_t = ::std::os::raw::c_long;
+pub type __ssize_t = ::std::os::raw::c_long;
+pub type __syscall_slong_t = ::std::os::raw::c_long;
+pub type __syscall_ulong_t = ::std::os::raw::c_ulong;
+pub type __loff_t = __off64_t;
+pub type __caddr_t = *mut ::std::os::raw::c_char;
+pub type __intptr_t = ::std::os::raw::c_long;
+pub type __socklen_t = ::std::os::raw::c_uint;
+pub type __sig_atomic_t = ::std::os::raw::c_int;
+pub type int_least8_t = __int_least8_t;
+pub type int_least16_t = __int_least16_t;
+pub type int_least32_t = __int_least32_t;
+pub type int_least64_t = __int_least64_t;
+pub type uint_least8_t = __uint_least8_t;
+pub type uint_least16_t = __uint_least16_t;
+pub type uint_least32_t = __uint_least32_t;
+pub type uint_least64_t = __uint_least64_t;
+pub type int_fast8_t = ::std::os::raw::c_schar;
+pub type int_fast16_t = ::std::os::raw::c_long;
+pub type int_fast32_t = ::std::os::raw::c_long;
+pub type int_fast64_t = ::std::os::raw::c_long;
+pub type uint_fast8_t = ::std::os::raw::c_uchar;
+pub type uint_fast16_t = ::std::os::raw::c_ulong;
+pub type uint_fast32_t = ::std::os::raw::c_ulong;
+pub type uint_fast64_t = ::std::os::raw::c_ulong;
+pub type intmax_t = __intmax_t;
+pub type uintmax_t = __uintmax_t;
+#[doc = " @mainpage libgpiod public API"]
+#[doc = ""]
+#[doc = " This is the complete documentation of the public API made available to"]
+#[doc = " users of libgpiod."]
+#[doc = ""]
+#[doc = " <p>The API is logically split into several parts such as: GPIO chip & line"]
+#[doc = " operators, GPIO events handling etc."]
+#[doc = ""]
+#[doc = " <p>General note on error handling: all functions exported by libgpiod that"]
+#[doc = " can fail, set errno to one of the error values defined in errno.h upon"]
+#[doc = " failure. The way of notifying the caller that an error occurred varies"]
+#[doc = " between functions, but in general a function that returns an int, returns -1"]
+#[doc = " on error, while a function returning a pointer indicates an error condition"]
+#[doc = " by returning a NULL pointer. It's not practical to list all possible error"]
+#[doc = " codes for every function as they propagate errors from the underlying libc"]
+#[doc = " functions."]
+#[doc = ""]
+#[doc = " <p>In general libgpiod functions are not NULL-aware and it's expected that"]
+#[doc = " users pass valid pointers to objects as arguments. An exception to this rule"]
+#[doc = " are the functions that free/close/release resources - which work when passed"]
+#[doc = " a NULL-pointer as argument. Other exceptions are documented."]
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct gpiod_chip {
+    _unused: [u8; 0],
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct gpiod_chip_info {
+    _unused: [u8; 0],
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct gpiod_line_info {
+    _unused: [u8; 0],
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct gpiod_line_settings {
+    _unused: [u8; 0],
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct gpiod_line_config {
+    _unused: [u8; 0],
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct gpiod_request_config {
+    _unused: [u8; 0],
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct gpiod_line_request {
+    _unused: [u8; 0],
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct gpiod_info_event {
+    _unused: [u8; 0],
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct gpiod_edge_event {
+    _unused: [u8; 0],
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct gpiod_edge_event_buffer {
+    _unused: [u8; 0],
+}
+extern "C" {
+    #[doc = " @brief Open a chip by path."]
+    #[doc = " @param path Path to the gpiochip device file."]
+    #[doc = " @return GPIO chip request or NULL if an error occurred."]
+    pub fn gpiod_chip_open(path: *const ::std::os::raw::c_char) -> *mut gpiod_chip;
+}
+extern "C" {
+    #[doc = " @brief Close the chip and release all associated resources."]
+    #[doc = " @param chip Chip to close."]
+    pub fn gpiod_chip_close(chip: *mut gpiod_chip);
+}
+extern "C" {
+    #[doc = " @brief Get information about the chip."]
+    #[doc = " @param chip GPIO chip object."]
+    #[doc = " @return New GPIO chip info object or NULL if an error occurred. The returned"]
+    #[doc = "         object must be freed by the caller using ::gpiod_chip_info_free."]
+    pub fn gpiod_chip_get_info(chip: *mut gpiod_chip) -> *mut gpiod_chip_info;
+}
+extern "C" {
+    #[doc = " @brief Get the path used to open the chip."]
+    #[doc = " @param chip GPIO chip object."]
+    #[doc = " @return Path to the file passed as argument to ::gpiod_chip_open."]
+    pub fn gpiod_chip_get_path(chip: *mut gpiod_chip) -> *const ::std::os::raw::c_char;
+}
+extern "C" {
+    #[doc = " @brief Get a snapshot of information about a line."]
+    #[doc = " @param chip GPIO chip object."]
+    #[doc = " @param offset The offset of the GPIO line."]
+    #[doc = " @return New GPIO line info object or NULL if an error occurred. The returned"]
+    #[doc = "\t   object must be freed by the caller using ::gpiod_line_info_free."]
+    pub fn gpiod_chip_get_line_info(
+        chip: *mut gpiod_chip,
+        offset: ::std::os::raw::c_uint,
+    ) -> *mut gpiod_line_info;
+}
+extern "C" {
+    #[doc = " @brief Get a snapshot of the status of a line and start watching it for"]
+    #[doc = "\t  future changes."]
+    #[doc = " @param chip GPIO chip object."]
+    #[doc = " @param offset The offset of the GPIO line."]
+    #[doc = " @return New GPIO line info object or NULL if an error occurred. The returned"]
+    #[doc = "\t   object must be freed by the caller using ::gpiod_line_info_free."]
+    #[doc = " @note Line status does not include the line value.  To monitor the line"]
+    #[doc = "\t value the line must be requested as an input with edge detection set."]
+    pub fn gpiod_chip_watch_line_info(
+        chip: *mut gpiod_chip,
+        offset: ::std::os::raw::c_uint,
+    ) -> *mut gpiod_line_info;
+}
+extern "C" {
+    #[doc = " @brief Stop watching a line for status changes."]
+    #[doc = " @param chip GPIO chip object."]
+    #[doc = " @param offset The offset of the line to stop watching."]
+    #[doc = " @return 0 on success, -1 on failure."]
+    pub fn gpiod_chip_unwatch_line_info(
+        chip: *mut gpiod_chip,
+        offset: ::std::os::raw::c_uint,
+    ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    #[doc = " @brief Get the file descriptor associated with the chip."]
+    #[doc = " @param chip GPIO chip object."]
+    #[doc = " @return File descriptor number for the chip."]
+    #[doc = "\t   This function never fails."]
+    #[doc = "\t   The returned file descriptor must not be closed by the caller."]
+    #[doc = "\t   Call ::gpiod_chip_close to close the file descriptor."]
+    pub fn gpiod_chip_get_fd(chip: *mut gpiod_chip) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    #[doc = " @brief Wait for line status change events on any of the watched lines"]
+    #[doc = "\t  on the chip."]
+    #[doc = " @param chip GPIO chip object."]
+    #[doc = " @param timeout_ns Wait time limit in nanoseconds. If set to 0, the function"]
+    #[doc = "\t\t     returns immediatelly. If set to a negative number, the"]
+    #[doc = "\t\t     function blocks indefinitely until an event becomes"]
+    #[doc = "\t\t     available."]
+    #[doc = " @return 0 if wait timed out, -1 if an error occurred, 1 if an event is"]
+    #[doc = "\t   pending."]
+    pub fn gpiod_chip_wait_info_event(
+        chip: *mut gpiod_chip,
+        timeout_ns: i64,
+    ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    #[doc = " @brief Read a single line status change event from the chip."]
+    #[doc = " @param chip GPIO chip object."]
+    #[doc = " @return Newly read watch event object or NULL on error. The event must be"]
+    #[doc = "\t   freed by the caller using ::gpiod_info_event_free."]
+    #[doc = " @note If no events are pending, this function will block."]
+    pub fn gpiod_chip_read_info_event(chip: *mut gpiod_chip) -> *mut gpiod_info_event;
+}
+extern "C" {
+    #[doc = " @brief Map a line's name to its offset within the chip."]
+    #[doc = " @param chip GPIO chip object."]
+    #[doc = " @param name Name of the GPIO line to map."]
+    #[doc = " @return Offset of the line within the chip or -1 on error."]
+    #[doc = " @note If a line with given name is not exposed by the chip, the function"]
+    #[doc = "       sets errno to ENOENT."]
+    pub fn gpiod_chip_get_line_offset_from_name(
+        chip: *mut gpiod_chip,
+        name: *const ::std::os::raw::c_char,
+    ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    #[doc = " @brief Request a set of lines for exclusive usage."]
+    #[doc = " @param chip GPIO chip object."]
+    #[doc = " @param req_cfg Request config object. Can be NULL for default settings."]
+    #[doc = " @param line_cfg Line config object."]
+    #[doc = " @return New line request object or NULL if an error occurred. The request"]
+    #[doc = "\t   must be released by the caller using ::gpiod_line_request_release."]
+    pub fn gpiod_chip_request_lines(
+        chip: *mut gpiod_chip,
+        req_cfg: *mut gpiod_request_config,
+        line_cfg: *mut gpiod_line_config,
+    ) -> *mut gpiod_line_request;
+}
+extern "C" {
+    #[doc = " @brief Free a chip info object and release all associated resources."]
+    #[doc = " @param info GPIO chip info object to free."]
+    pub fn gpiod_chip_info_free(info: *mut gpiod_chip_info);
+}
+extern "C" {
+    #[doc = " @brief Get the name of the chip as represented in the kernel."]
+    #[doc = " @param info GPIO chip info object."]
+    #[doc = " @return Pointer to a human-readable string containing the chip name."]
+    pub fn gpiod_chip_info_get_name(info: *mut gpiod_chip_info) -> *const ::std::os::raw::c_char;
+}
+extern "C" {
+    #[doc = " @brief Get the label of the chip as represented in the kernel."]
+    #[doc = " @param info GPIO chip info object."]
+    #[doc = " @return Pointer to a human-readable string containing the chip label."]
+    pub fn gpiod_chip_info_get_label(info: *mut gpiod_chip_info) -> *const ::std::os::raw::c_char;
+}
+extern "C" {
+    #[doc = " @brief Get the number of lines exposed by the chip."]
+    #[doc = " @param info GPIO chip info object."]
+    #[doc = " @return Number of GPIO lines."]
+    pub fn gpiod_chip_info_get_num_lines(info: *mut gpiod_chip_info) -> size_t;
+}
+pub const GPIOD_LINE_VALUE_INACTIVE: ::std::os::raw::c_uint = 0;
+pub const GPIOD_LINE_VALUE_ACTIVE: ::std::os::raw::c_uint = 1;
+#[doc = " @brief Logical line state."]
+pub type _bindgen_ty_1 = ::std::os::raw::c_uint;
+pub const GPIOD_LINE_DIRECTION_AS_IS: ::std::os::raw::c_uint = 1;
+pub const GPIOD_LINE_DIRECTION_INPUT: ::std::os::raw::c_uint = 2;
+pub const GPIOD_LINE_DIRECTION_OUTPUT: ::std::os::raw::c_uint = 3;
+#[doc = " @brief Direction settings."]
+pub type _bindgen_ty_2 = ::std::os::raw::c_uint;
+pub const GPIOD_LINE_EDGE_NONE: ::std::os::raw::c_uint = 1;
+pub const GPIOD_LINE_EDGE_RISING: ::std::os::raw::c_uint = 2;
+pub const GPIOD_LINE_EDGE_FALLING: ::std::os::raw::c_uint = 3;
+pub const GPIOD_LINE_EDGE_BOTH: ::std::os::raw::c_uint = 4;
+#[doc = " @brief Edge detection settings."]
+pub type _bindgen_ty_3 = ::std::os::raw::c_uint;
+pub const GPIOD_LINE_BIAS_AS_IS: ::std::os::raw::c_uint = 1;
+pub const GPIOD_LINE_BIAS_UNKNOWN: ::std::os::raw::c_uint = 2;
+pub const GPIOD_LINE_BIAS_DISABLED: ::std::os::raw::c_uint = 3;
+pub const GPIOD_LINE_BIAS_PULL_UP: ::std::os::raw::c_uint = 4;
+pub const GPIOD_LINE_BIAS_PULL_DOWN: ::std::os::raw::c_uint = 5;
+#[doc = " @brief Internal bias settings."]
+pub type _bindgen_ty_4 = ::std::os::raw::c_uint;
+pub const GPIOD_LINE_DRIVE_PUSH_PULL: ::std::os::raw::c_uint = 1;
+pub const GPIOD_LINE_DRIVE_OPEN_DRAIN: ::std::os::raw::c_uint = 2;
+pub const GPIOD_LINE_DRIVE_OPEN_SOURCE: ::std::os::raw::c_uint = 3;
+#[doc = " @brief Drive settings."]
+pub type _bindgen_ty_5 = ::std::os::raw::c_uint;
+pub const GPIOD_LINE_EVENT_CLOCK_MONOTONIC: ::std::os::raw::c_uint = 1;
+pub const GPIOD_LINE_EVENT_CLOCK_REALTIME: ::std::os::raw::c_uint = 2;
+pub const GPIOD_LINE_EVENT_CLOCK_HTE: ::std::os::raw::c_uint = 3;
+#[doc = " @brief Event clock settings."]
+pub type _bindgen_ty_6 = ::std::os::raw::c_uint;
+extern "C" {
+    #[doc = " @brief Free a line info object and release all associated resources."]
+    #[doc = " @param info GPIO line info object to free."]
+    pub fn gpiod_line_info_free(info: *mut gpiod_line_info);
+}
+extern "C" {
+    #[doc = " @brief Copy a line info object."]
+    #[doc = " @param info Line info to copy."]
+    #[doc = " @return Copy of the line info or NULL on error. The returned object must"]
+    #[doc = "\t   be freed by the caller using :gpiod_line_info_free."]
+    pub fn gpiod_line_info_copy(info: *mut gpiod_line_info) -> *mut gpiod_line_info;
+}
+extern "C" {
+    #[doc = " @brief Get the offset of the line."]
+    #[doc = " @param info GPIO line info object."]
+    #[doc = " @return Offset of the line within the parent chip."]
+    #[doc = ""]
+    #[doc = " The offset uniquely identifies the line on the chip."]
+    #[doc = " The combination of the chip and offset uniquely identifies the line within"]
+    #[doc = " the system."]
+    pub fn gpiod_line_info_get_offset(info: *mut gpiod_line_info) -> ::std::os::raw::c_uint;
+}
+extern "C" {
+    #[doc = " @brief Get the name of the line."]
+    #[doc = " @param info GPIO line info object."]
+    #[doc = " @return Name of the GPIO line as it is represented in the kernel."]
+    #[doc = "\t   This function returns a pointer to a null-terminated string"]
+    #[doc = "\t   or NULL if the line is unnamed."]
+    pub fn gpiod_line_info_get_name(info: *mut gpiod_line_info) -> *const ::std::os::raw::c_char;
+}
+extern "C" {
+    #[doc = " @brief Check if the line is in use."]
+    #[doc = " @param info GPIO line object."]
+    #[doc = " @return True if the line is in use, false otherwise."]
+    #[doc = ""]
+    #[doc = " The exact reason a line is busy cannot be determined from user space."]
+    #[doc = " It may have been requested by another process or hogged by the kernel."]
+    #[doc = " It only matters that the line is used and can't be requested until"]
+    #[doc = " released by the existing consumer."]
+    pub fn gpiod_line_info_is_used(info: *mut gpiod_line_info) -> bool;
+}
+extern "C" {
+    #[doc = " @brief Get the name of the consumer of the line."]
+    #[doc = " @param info GPIO line info object."]
+    #[doc = " @return Name of the GPIO consumer as it is represented in the kernel."]
+    #[doc = "\t   This function returns a pointer to a null-terminated string"]
+    #[doc = "\t   or NULL if the consumer name is not set."]
+    pub fn gpiod_line_info_get_consumer(
+        info: *mut gpiod_line_info,
+    ) -> *const ::std::os::raw::c_char;
+}
+extern "C" {
+    #[doc = " @brief Get the direction setting of the line."]
+    #[doc = " @param info GPIO line info object."]
+    #[doc = " @return Returns ::GPIOD_LINE_DIRECTION_INPUT or"]
+    #[doc = "\t   ::GPIOD_LINE_DIRECTION_OUTPUT."]
+    pub fn gpiod_line_info_get_direction(info: *mut gpiod_line_info) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    #[doc = " @brief Get the edge detection setting of the line."]
+    #[doc = " @param info GPIO line info object."]
+    #[doc = " @return Returns ::GPIOD_LINE_EDGE_NONE, ::GPIOD_LINE_EDGE_RISING,"]
+    #[doc = "\t   ::GPIOD_LINE_EDGE_FALLING or ::GPIOD_LINE_EDGE_BOTH."]
+    pub fn gpiod_line_info_get_edge_detection(info: *mut gpiod_line_info) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    #[doc = " @brief Get the bias setting of the line."]
+    #[doc = " @param info GPIO line object."]
+    #[doc = " @return Returns ::GPIOD_LINE_BIAS_PULL_UP, ::GPIOD_LINE_BIAS_PULL_DOWN,"]
+    #[doc = "\t   ::GPIOD_LINE_BIAS_DISABLED or ::GPIOD_LINE_BIAS_UNKNOWN."]
+    pub fn gpiod_line_info_get_bias(info: *mut gpiod_line_info) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    #[doc = " @brief Get the drive setting of the line."]
+    #[doc = " @param info GPIO line info object."]
+    #[doc = " @return Returns ::GPIOD_LINE_DRIVE_PUSH_PULL, ::GPIOD_LINE_DRIVE_OPEN_DRAIN"]
+    #[doc = "\t   or ::GPIOD_LINE_DRIVE_OPEN_SOURCE."]
+    pub fn gpiod_line_info_get_drive(info: *mut gpiod_line_info) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    #[doc = " @brief Check if the logical value of the line is inverted compared to the"]
+    #[doc = "\t  physical."]
+    #[doc = " @param info GPIO line object."]
+    #[doc = " @return True if the line is \"active-low\", false otherwise."]
+    pub fn gpiod_line_info_is_active_low(info: *mut gpiod_line_info) -> bool;
+}
+extern "C" {
+    #[doc = " @brief Check if the line is debounced (either by hardware or by the kernel"]
+    #[doc = "\t  software debouncer)."]
+    #[doc = " @param info GPIO line info object."]
+    #[doc = " @return True if the line is debounced, false otherwise."]
+    pub fn gpiod_line_info_is_debounced(info: *mut gpiod_line_info) -> bool;
+}
+extern "C" {
+    #[doc = " @brief Get the debounce period of the line, in microseconds."]
+    #[doc = " @param info GPIO line info object."]
+    #[doc = " @return Debounce period in microseconds."]
+    #[doc = "\t   0 if the line is not debounced."]
+    pub fn gpiod_line_info_get_debounce_period_us(
+        info: *mut gpiod_line_info,
+    ) -> ::std::os::raw::c_ulong;
+}
+extern "C" {
+    #[doc = " @brief Get the event clock setting used for edge event timestamps for the"]
+    #[doc = "\t  line."]
+    #[doc = " @param info GPIO line info object."]
+    #[doc = " @return Returns ::GPIOD_LINE_EVENT_CLOCK_MONOTONIC or"]
+    #[doc = "\t   ::GPIOD_LINE_EVENT_CLOCK_REALTIME."]
+    pub fn gpiod_line_info_get_event_clock(info: *mut gpiod_line_info) -> ::std::os::raw::c_int;
+}
+pub const GPIOD_INFO_EVENT_LINE_REQUESTED: ::std::os::raw::c_uint = 1;
+pub const GPIOD_INFO_EVENT_LINE_RELEASED: ::std::os::raw::c_uint = 2;
+pub const GPIOD_INFO_EVENT_LINE_CONFIG_CHANGED: ::std::os::raw::c_uint = 3;
+#[doc = " @brief Line status change event types."]
+pub type _bindgen_ty_7 = ::std::os::raw::c_uint;
+extern "C" {
+    #[doc = " @brief Free the info event object and release all associated resources."]
+    #[doc = " @param event Info event to free."]
+    pub fn gpiod_info_event_free(event: *mut gpiod_info_event);
+}
+extern "C" {
+    #[doc = " @brief Get the event type of the status change event."]
+    #[doc = " @param event Line status watch event."]
+    #[doc = " @return One of ::GPIOD_INFO_EVENT_LINE_REQUESTED,"]
+    #[doc = "\t   ::GPIOD_INFO_EVENT_LINE_RELEASED or"]
+    #[doc = "\t   ::GPIOD_INFO_EVENT_LINE_CONFIG_CHANGED."]
+    pub fn gpiod_info_event_get_event_type(event: *mut gpiod_info_event) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    #[doc = " @brief Get the timestamp of the event."]
+    #[doc = " @param event Line status watch event."]
+    #[doc = " @return Timestamp in nanoseconds, read from the monotonic clock."]
+    pub fn gpiod_info_event_get_timestamp_ns(event: *mut gpiod_info_event) -> u64;
+}
+extern "C" {
+    #[doc = " @brief Get the snapshot of line-info associated with the event."]
+    #[doc = " @param event Line info event object."]
+    #[doc = " @return Returns a pointer to the line-info object associated with the event"]
+    #[doc = "\t   whose lifetime is tied to the event object. It must not be freed by"]
+    #[doc = "\t   the caller."]
+    pub fn gpiod_info_event_get_line_info(event: *mut gpiod_info_event) -> *mut gpiod_line_info;
+}
+extern "C" {
+    #[doc = " @brief Create a new line settings object."]
+    #[doc = " @return New line settings object or NULL on error."]
+    pub fn gpiod_line_settings_new() -> *mut gpiod_line_settings;
+}
+extern "C" {
+    #[doc = " @brief Free the line settings object and release all associated resources."]
+    #[doc = " @param settings Line settings object."]
+    pub fn gpiod_line_settings_free(settings: *mut gpiod_line_settings);
+}
+extern "C" {
+    #[doc = " @brief Reset the line settings object to its default values."]
+    #[doc = " @param settings Line settings object."]
+    pub fn gpiod_line_settings_reset(settings: *mut gpiod_line_settings);
+}
+extern "C" {
+    #[doc = " @brief Copy the line settings object."]
+    #[doc = " @param settings Line settings object to copy."]
+    #[doc = " @return New line settings object that must be freed using"]
+    #[doc = "         ::gpiod_line_settings_free or NULL on failure."]
+    pub fn gpiod_line_settings_copy(settings: *mut gpiod_line_settings)
+        -> *mut gpiod_line_settings;
+}
+extern "C" {
+    #[doc = " @brief Set direction."]
+    #[doc = " @param settings Line settings object."]
+    #[doc = " @param direction New direction."]
+    #[doc = " @return 0 on success, -1 on error."]
+    pub fn gpiod_line_settings_set_direction(
+        settings: *mut gpiod_line_settings,
+        direction: ::std::os::raw::c_int,
+    ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    #[doc = " @brief Get direction."]
+    #[doc = " @param settings Line settings object."]
+    #[doc = " @return Current direction."]
+    pub fn gpiod_line_settings_get_direction(
+        settings: *mut gpiod_line_settings,
+    ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    #[doc = " @brief Set edge detection."]
+    #[doc = " @param settings Line settings object."]
+    #[doc = " @param edge New edge detection setting."]
+    #[doc = " @return 0 on success, -1 on failure."]
+    pub fn gpiod_line_settings_set_edge_detection(
+        settings: *mut gpiod_line_settings,
+        edge: ::std::os::raw::c_int,
+    ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    #[doc = " @brief Get edge detection."]
+    #[doc = " @param settings Line settings object."]
+    #[doc = " @return Current edge detection setting."]
+    pub fn gpiod_line_settings_get_edge_detection(
+        settings: *mut gpiod_line_settings,
+    ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    #[doc = " @brief Set bias."]
+    #[doc = " @param settings Line settings object."]
+    #[doc = " @param bias New bias."]
+    #[doc = " @return 0 on success, -1 on failure."]
+    pub fn gpiod_line_settings_set_bias(
+        settings: *mut gpiod_line_settings,
+        bias: ::std::os::raw::c_int,
+    ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    #[doc = " @brief Get bias."]
+    #[doc = " @param settings Line settings object."]
+    #[doc = " @return Current bias setting."]
+    pub fn gpiod_line_settings_get_bias(
+        settings: *mut gpiod_line_settings,
+    ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    #[doc = " @brief Set drive."]
+    #[doc = " @param settings Line settings object."]
+    #[doc = " @param drive New drive setting."]
+    #[doc = " @return 0 on success, -1 on failure."]
+    pub fn gpiod_line_settings_set_drive(
+        settings: *mut gpiod_line_settings,
+        drive: ::std::os::raw::c_int,
+    ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    #[doc = " @brief Get drive."]
+    #[doc = " @param settings Line settings object."]
+    #[doc = " @return Current drive setting."]
+    pub fn gpiod_line_settings_get_drive(
+        settings: *mut gpiod_line_settings,
+    ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    #[doc = " @brief Set active-low setting."]
+    #[doc = " @param settings Line settings object."]
+    #[doc = " @param active_low New active-low setting."]
+    pub fn gpiod_line_settings_set_active_low(settings: *mut gpiod_line_settings, active_low: bool);
+}
+extern "C" {
+    #[doc = " @brief Get active-low setting."]
+    #[doc = " @param settings Line settings object."]
+    #[doc = " @return True if active-low is enabled, false otherwise."]
+    pub fn gpiod_line_settings_get_active_low(settings: *mut gpiod_line_settings) -> bool;
+}
+extern "C" {
+    #[doc = " @brief Set debounce period."]
+    #[doc = " @param settings Line settings object."]
+    #[doc = " @param period New debounce period in microseconds."]
+    pub fn gpiod_line_settings_set_debounce_period_us(
+        settings: *mut gpiod_line_settings,
+        period: ::std::os::raw::c_ulong,
+    );
+}
+extern "C" {
+    #[doc = " @brief Get debounce period."]
+    #[doc = " @param settings Line settings object."]
+    #[doc = " @return Current debounce period in microseconds."]
+    pub fn gpiod_line_settings_get_debounce_period_us(
+        settings: *mut gpiod_line_settings,
+    ) -> ::std::os::raw::c_ulong;
+}
+extern "C" {
+    #[doc = " @brief Set event clock."]
+    #[doc = " @param settings Line settings object."]
+    #[doc = " @param event_clock New event clock."]
+    #[doc = " @return 0 on success, -1 on failure."]
+    pub fn gpiod_line_settings_set_event_clock(
+        settings: *mut gpiod_line_settings,
+        event_clock: ::std::os::raw::c_int,
+    ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    #[doc = " @brief Get event clock setting."]
+    #[doc = " @param settings Line settings object."]
+    #[doc = " @return Current event clock setting."]
+    pub fn gpiod_line_settings_get_event_clock(
+        settings: *mut gpiod_line_settings,
+    ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    #[doc = " @brief Set the output value."]
+    #[doc = " @param settings Line settings object."]
+    #[doc = " @param value New output value."]
+    #[doc = " @return 0 on success, -1 on failure."]
+    pub fn gpiod_line_settings_set_output_value(
+        settings: *mut gpiod_line_settings,
+        value: ::std::os::raw::c_int,
+    ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    #[doc = " @brief Get the output value."]
+    #[doc = " @param settings Line settings object."]
+    #[doc = " @return Current output value."]
+    pub fn gpiod_line_settings_get_output_value(
+        settings: *mut gpiod_line_settings,
+    ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    #[doc = " @brief Create a new line config object."]
+    #[doc = " @return New line config object or NULL on error."]
+    pub fn gpiod_line_config_new() -> *mut gpiod_line_config;
+}
+extern "C" {
+    #[doc = " @brief Free the line config object and release all associated resources."]
+    #[doc = " @param config Line config object to free."]
+    pub fn gpiod_line_config_free(config: *mut gpiod_line_config);
+}
+extern "C" {
+    #[doc = " @brief Reset the line config object."]
+    #[doc = " @param config Line config object to free."]
+    #[doc = ""]
+    #[doc = " Resets the entire configuration stored in the object. This is useful if"]
+    #[doc = " the user wants to reuse the object without reallocating it."]
+    pub fn gpiod_line_config_reset(config: *mut gpiod_line_config);
+}
+extern "C" {
+    #[doc = " @brief Add line settings for a set of offsets."]
+    #[doc = " @param config Line config object."]
+    #[doc = " @param offsets Array of offsets for which to apply the settings."]
+    #[doc = " @param num_offsets Number of offsets stored in the offsets array."]
+    #[doc = " @param settings Line settings to apply."]
+    #[doc = " @return 0 on success, -1 on failure."]
+    pub fn gpiod_line_config_add_line_settings(
+        config: *mut gpiod_line_config,
+        offsets: *const ::std::os::raw::c_uint,
+        num_offsets: size_t,
+        settings: *mut gpiod_line_settings,
+    ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    #[doc = " @brief Get line settings for offset."]
+    #[doc = " @param config Line config object."]
+    #[doc = " @param offset Offset for which to get line settings."]
+    #[doc = " @return New line settings object (must be freed by the caller) or NULL on"]
+    #[doc = "         error."]
+    pub fn gpiod_line_config_get_line_settings(
+        config: *mut gpiod_line_config,
+        offset: ::std::os::raw::c_uint,
+    ) -> *mut gpiod_line_settings;
+}
+extern "C" {
+    #[doc = " @brief Get configured offsets."]
+    #[doc = " @param config Line config object."]
+    #[doc = " @param num_offsets Pointer to a variable in which the number of line offsets"]
+    #[doc = "                    will be stored."]
+    #[doc = " @param offsets Pointer to a pointer which will be set to point to an array"]
+    #[doc = "                containing the configured offsets. The array will be allocated"]
+    #[doc = "                using malloc() and must be freed using free()."]
+    #[doc = " @return 0 on success, -1 on failure."]
+    pub fn gpiod_line_config_get_offsets(
+        config: *mut gpiod_line_config,
+        num_offsets: *mut size_t,
+        offsets: *mut *mut ::std::os::raw::c_uint,
+    ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    #[doc = " @brief Create a new request config object."]
+    #[doc = " @return New request config object or NULL on error."]
+    pub fn gpiod_request_config_new() -> *mut gpiod_request_config;
+}
+extern "C" {
+    #[doc = " @brief Free the request config object and release all associated resources."]
+    #[doc = " @param config Line config object."]
+    pub fn gpiod_request_config_free(config: *mut gpiod_request_config);
+}
+extern "C" {
+    #[doc = " @brief Set the consumer name for the request."]
+    #[doc = " @param config Request config object."]
+    #[doc = " @param consumer Consumer name."]
+    #[doc = " @note If the consumer string is too long, it will be truncated to the max"]
+    #[doc = "       accepted length."]
+    pub fn gpiod_request_config_set_consumer(
+        config: *mut gpiod_request_config,
+        consumer: *const ::std::os::raw::c_char,
+    );
+}
+extern "C" {
+    #[doc = " @brief Get the consumer name configured in the request config."]
+    #[doc = " @param config Request config object."]
+    #[doc = " @return Consumer name stored in the request config."]
+    pub fn gpiod_request_config_get_consumer(
+        config: *mut gpiod_request_config,
+    ) -> *const ::std::os::raw::c_char;
+}
+extern "C" {
+    #[doc = " @brief Set the size of the kernel event buffer for the request."]
+    #[doc = " @param config Request config object."]
+    #[doc = " @param event_buffer_size New event buffer size."]
+    #[doc = " @note The kernel may adjust the value if it's too high. If set to 0, the"]
+    #[doc = "       default value will be used."]
+    #[doc = " @note The kernel buffer is distinct from and independent of the user space"]
+    #[doc = "\t buffer (::gpiod_edge_event_buffer_new)."]
+    pub fn gpiod_request_config_set_event_buffer_size(
+        config: *mut gpiod_request_config,
+        event_buffer_size: size_t,
+    );
+}
+extern "C" {
+    #[doc = " @brief Get the edge event buffer size for the request config."]
+    #[doc = " @param config Request config object."]
+    #[doc = " @return Edge event buffer size setting from the request config."]
+    pub fn gpiod_request_config_get_event_buffer_size(config: *mut gpiod_request_config) -> size_t;
+}
+extern "C" {
+    #[doc = " @brief Release the requested lines and free all associated resources."]
+    #[doc = " @param request Line request object to release."]
+    pub fn gpiod_line_request_release(request: *mut gpiod_line_request);
+}
+extern "C" {
+    #[doc = " @brief Get the number of lines in the request."]
+    #[doc = " @param request Line request object."]
+    #[doc = " @return Number of requested lines."]
+    pub fn gpiod_line_request_get_num_lines(request: *mut gpiod_line_request) -> size_t;
+}
+extern "C" {
+    #[doc = " @brief Get the offsets of the lines in the request."]
+    #[doc = " @param request Line request object."]
+    #[doc = " @param offsets Array to store offsets. Must be sized to hold the number of"]
+    #[doc = "\t\t  lines returned by ::gpiod_line_request_get_num_lines."]
+    pub fn gpiod_line_request_get_offsets(
+        request: *mut gpiod_line_request,
+        offsets: *mut ::std::os::raw::c_uint,
+    );
+}
+extern "C" {
+    #[doc = " @brief Get the value of a single requested line."]
+    #[doc = " @param request Line request object."]
+    #[doc = " @param offset The offset of the line of which the value should be read."]
+    #[doc = " @return Returns 1 or 0 on success and -1 on error."]
+    pub fn gpiod_line_request_get_value(
+        request: *mut gpiod_line_request,
+        offset: ::std::os::raw::c_uint,
+    ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    #[doc = " @brief Get the values of a subset of requested lines."]
+    #[doc = " @param request GPIO line request."]
+    #[doc = " @param num_values Number of lines for which to read values."]
+    #[doc = " @param offsets Array of offsets identifying the subset of requested lines"]
+    #[doc = "\t\t  from which to read values."]
+    #[doc = " @param values Array in which the values will be stored.  Must be sized"]
+    #[doc = "\t\t to hold \\p num_values entries.  Each value is associated with the"]
+    #[doc = "\t\t line identified by the corresponding entry in \\p offsets."]
+    #[doc = " @return 0 on success, -1 on failure."]
+    pub fn gpiod_line_request_get_values_subset(
+        request: *mut gpiod_line_request,
+        num_values: size_t,
+        offsets: *const ::std::os::raw::c_uint,
+        values: *mut ::std::os::raw::c_int,
+    ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    #[doc = " @brief Get the values of all requested lines."]
+    #[doc = " @param request GPIO line request."]
+    #[doc = " @param values Array in which the values will be stored. Must be sized to"]
+    #[doc = "\t\t hold the number of lines returned by"]
+    #[doc = "\t\t ::gpiod_line_request_get_num_lines."]
+    #[doc = "\t\t Each value is associated with the line identified by the"]
+    #[doc = "\t\t corresponding entry in the offset array returned by"]
+    #[doc = "\t\t ::gpiod_line_request_get_offsets."]
+    #[doc = " @return 0 on success, -1 on failure."]
+    pub fn gpiod_line_request_get_values(
+        request: *mut gpiod_line_request,
+        values: *mut ::std::os::raw::c_int,
+    ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    #[doc = " @brief Set the value of a single requested line."]
+    #[doc = " @param request Line request object."]
+    #[doc = " @param offset The offset of the line for which the value should be set."]
+    #[doc = " @param value Value to set."]
+    pub fn gpiod_line_request_set_value(
+        request: *mut gpiod_line_request,
+        offset: ::std::os::raw::c_uint,
+        value: ::std::os::raw::c_int,
+    ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    #[doc = " @brief Set the values of a subset of requested lines."]
+    #[doc = " @param request GPIO line request."]
+    #[doc = " @param num_values Number of lines for which to set values."]
+    #[doc = " @param offsets Array of offsets, containing the number of entries specified"]
+    #[doc = "\t\t  by \\p num_values, identifying the requested lines for"]
+    #[doc = "\t\t  which to set values."]
+    #[doc = " @param values Array of values to set, containing the number of entries"]
+    #[doc = "\t\t specified by \\p num_values.  Each value is associated with the"]
+    #[doc = "\t\t line identified by the corresponding entry in \\p offsets."]
+    #[doc = " @return 0 on success, -1 on failure."]
+    pub fn gpiod_line_request_set_values_subset(
+        request: *mut gpiod_line_request,
+        num_values: size_t,
+        offsets: *const ::std::os::raw::c_uint,
+        values: *const ::std::os::raw::c_int,
+    ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    #[doc = " @brief Set the values of all lines associated with a request."]
+    #[doc = " @param request GPIO line request."]
+    #[doc = " @param values Array containing the values to set. Must be sized to"]
+    #[doc = "\t\t contain the number of lines returned by"]
+    #[doc = "\t\t ::gpiod_line_request_get_num_lines."]
+    #[doc = "\t\t Each value is associated with the line identified by the"]
+    #[doc = "\t\t corresponding entry in the offset array returned by"]
+    #[doc = "\t\t ::gpiod_line_request_get_offsets."]
+    pub fn gpiod_line_request_set_values(
+        request: *mut gpiod_line_request,
+        values: *const ::std::os::raw::c_int,
+    ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    #[doc = " @brief Update the configuration of lines associated with a line request."]
+    #[doc = " @param request GPIO line request."]
+    #[doc = " @param config New line config to apply."]
+    #[doc = " @return 0 on success, -1 on failure."]
+    #[doc = " @note The new line configuration completely replaces the old."]
+    #[doc = " @note Any requested lines without overrides are configured to the requested"]
+    #[doc = "\t defaults."]
+    #[doc = " @note Any configured overrides for lines that have not been requested"]
+    #[doc = "\t are silently ignored."]
+    pub fn gpiod_line_request_reconfigure_lines(
+        request: *mut gpiod_line_request,
+        config: *mut gpiod_line_config,
+    ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    #[doc = " @brief Get the file descriptor associated with a line request."]
+    #[doc = " @param request GPIO line request."]
+    #[doc = " @return The file descriptor associated with the request."]
+    #[doc = "\t   This function never fails."]
+    #[doc = "\t   The returned file descriptor must not be closed by the caller."]
+    #[doc = "\t   Call ::gpiod_line_request_release to close the file."]
+    pub fn gpiod_line_request_get_fd(request: *mut gpiod_line_request) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    #[doc = " @brief Wait for edge events on any of the requested lines."]
+    #[doc = " @param request GPIO line request."]
+    #[doc = " @param timeout_ns Wait time limit in nanoseconds. If set to 0, the function"]
+    #[doc = "\t\t     returns immediatelly. If set to a negative number, the"]
+    #[doc = "\t\t     function blocks indefinitely until an event becomes"]
+    #[doc = "\t\t     available."]
+    #[doc = " @return 0 if wait timed out, -1 if an error occurred, 1 if an event is"]
+    #[doc = "\t   pending."]
+    #[doc = "q"]
+    #[doc = " Lines must have edge detection set for edge events to be emitted."]
+    #[doc = " By default edge detection is disabled."]
+    pub fn gpiod_line_request_wait_edge_event(
+        request: *mut gpiod_line_request,
+        timeout_ns: i64,
+    ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    #[doc = " @brief Read a number of edge events from a line request."]
+    #[doc = " @param request GPIO line request."]
+    #[doc = " @param buffer Edge event buffer, sized to hold at least \\p max_events."]
+    #[doc = " @param max_events Maximum number of events to read."]
+    #[doc = " @return On success returns the number of events read from the file"]
+    #[doc = "\t   descriptor, on failure return -1."]
+    #[doc = " @note This function will block if no event was queued for the line request."]
+    #[doc = " @note Any exising events in the buffer are overwritten.  This is not an"]
+    #[doc = "       append operation."]
+    pub fn gpiod_line_request_read_edge_event(
+        request: *mut gpiod_line_request,
+        buffer: *mut gpiod_edge_event_buffer,
+        max_events: size_t,
+    ) -> ::std::os::raw::c_int;
+}
+pub const GPIOD_EDGE_EVENT_RISING_EDGE: ::std::os::raw::c_uint = 1;
+pub const GPIOD_EDGE_EVENT_FALLING_EDGE: ::std::os::raw::c_uint = 2;
+#[doc = " @brief Event types."]
+pub type _bindgen_ty_8 = ::std::os::raw::c_uint;
+extern "C" {
+    #[doc = " @brief Free the edge event object."]
+    #[doc = " @param event Edge event object to free."]
+    pub fn gpiod_edge_event_free(event: *mut gpiod_edge_event);
+}
+extern "C" {
+    #[doc = " @brief Copy the edge event object."]
+    #[doc = " @param event Edge event to copy."]
+    #[doc = " @return Copy of the edge event or NULL on error. The returned object must"]
+    #[doc = "\t   be freed by the caller using ::gpiod_edge_event_free."]
+    pub fn gpiod_edge_event_copy(event: *mut gpiod_edge_event) -> *mut gpiod_edge_event;
+}
+extern "C" {
+    #[doc = " @brief Get the event type."]
+    #[doc = " @param event GPIO edge event."]
+    #[doc = " @return The event type (::GPIOD_EDGE_EVENT_RISING_EDGE or"]
+    #[doc = "\t   ::GPIOD_EDGE_EVENT_FALLING_EDGE)."]
+    pub fn gpiod_edge_event_get_event_type(event: *mut gpiod_edge_event) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    #[doc = " @brief Get the timestamp of the event."]
+    #[doc = " @param event GPIO edge event."]
+    #[doc = " @return Timestamp in nanoseconds."]
+    #[doc = " @note The source clock for the timestamp depends on the event_clock"]
+    #[doc = "\t setting for the line."]
+    pub fn gpiod_edge_event_get_timestamp_ns(event: *mut gpiod_edge_event) -> u64;
+}
+extern "C" {
+    #[doc = " @brief Get the offset of the line which triggered the event."]
+    #[doc = " @param event GPIO edge event."]
+    #[doc = " @return Line offset."]
+    pub fn gpiod_edge_event_get_line_offset(event: *mut gpiod_edge_event)
+        -> ::std::os::raw::c_uint;
+}
+extern "C" {
+    #[doc = " @brief Get the global sequence number of the event."]
+    #[doc = " @param event GPIO edge event."]
+    #[doc = " @return Sequence number of the event in the series of events for all lines"]
+    #[doc = "\t   in the associated line request."]
+    pub fn gpiod_edge_event_get_global_seqno(
+        event: *mut gpiod_edge_event,
+    ) -> ::std::os::raw::c_ulong;
+}
+extern "C" {
+    #[doc = " @brief Get the event sequence number specific to the line."]
+    #[doc = " @param event GPIO edge event."]
+    #[doc = " @return Sequence number of the event in the series of events only for this"]
+    #[doc = "\t   line within the lifetime of the associated line request."]
+    pub fn gpiod_edge_event_get_line_seqno(event: *mut gpiod_edge_event)
+        -> ::std::os::raw::c_ulong;
+}
+extern "C" {
+    #[doc = " @brief Create a new edge event buffer."]
+    #[doc = " @param capacity Number of events the buffer can store (min = 1, max = 1024)."]
+    #[doc = " @return New edge event buffer or NULL on error."]
+    #[doc = " @note If capacity equals 0, it will be set to a default value of 64. If"]
+    #[doc = "\t capacity is larger than 1024, it will be limited to 1024."]
+    #[doc = " @note The user space buffer is independent of the kernel buffer"]
+    #[doc = "\t (::gpiod_request_config_set_event_buffer_size).  As the user space"]
+    #[doc = "\t buffer is filled from the kernel buffer, there is no benefit making"]
+    #[doc = "\t the user space buffer larger than the kernel buffer."]
+    #[doc = "\t The default kernel buffer size for each request is 16*num_lines."]
+    pub fn gpiod_edge_event_buffer_new(capacity: size_t) -> *mut gpiod_edge_event_buffer;
+}
+extern "C" {
+    #[doc = " @brief Get the capacity (the max number of events that can be stored) of"]
+    #[doc = "\t  the event buffer."]
+    #[doc = " @param buffer Edge event buffer."]
+    #[doc = " @return The capacity of the buffer."]
+    pub fn gpiod_edge_event_buffer_get_capacity(buffer: *mut gpiod_edge_event_buffer) -> size_t;
+}
+extern "C" {
+    #[doc = " @brief Free the edge event buffer and release all associated resources."]
+    #[doc = " @param buffer Edge event buffer to free."]
+    pub fn gpiod_edge_event_buffer_free(buffer: *mut gpiod_edge_event_buffer);
+}
+extern "C" {
+    #[doc = " @brief Get an event stored in the buffer."]
+    #[doc = " @param buffer Edge event buffer."]
+    #[doc = " @param index Index of the event in the buffer."]
+    #[doc = " @return Pointer to an event stored in the buffer. The lifetime of the"]
+    #[doc = "\t   event is tied to the buffer object. Users must not free the event"]
+    #[doc = "\t   returned by this function."]
+    pub fn gpiod_edge_event_buffer_get_event(
+        buffer: *mut gpiod_edge_event_buffer,
+        index: ::std::os::raw::c_ulong,
+    ) -> *mut gpiod_edge_event;
+}
+extern "C" {
+    #[doc = " @brief Get the number of events a buffer has stored."]
+    #[doc = " @param buffer Edge event buffer."]
+    #[doc = " @return Number of events stored in the buffer."]
+    pub fn gpiod_edge_event_buffer_get_num_events(buffer: *mut gpiod_edge_event_buffer) -> size_t;
+}
+extern "C" {
+    #[doc = " @brief Check if the file pointed to by path is a GPIO chip character device."]
+    #[doc = " @param path Path to check."]
+    #[doc = " @return True if the file exists and is either a GPIO chip character device"]
+    #[doc = "\t   or a symbolic link to one."]
+    pub fn gpiod_is_gpiochip_device(path: *const ::std::os::raw::c_char) -> bool;
+}
+extern "C" {
+    #[doc = " @brief Get the API version of the library as a human-readable string."]
+    #[doc = " @return Human-readable string containing the library version."]
+    pub fn gpiod_version_string() -> *const ::std::os::raw::c_char;
+}
-- 
2.31.1.272.g89b43f80a514


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH V7 3/8] libgpiod: Add rust wrapper crate
  2022-10-14 10:47 [PATCH V7 0/8] libgpiod: Add Rust bindings Viresh Kumar
  2022-10-14 10:47 ` [PATCH V7 1/8] libgpiod: Add libgpiod-sys rust crate Viresh Kumar
  2022-10-14 10:47 ` [PATCH V7 2/8] libgpiod-sys: Add pre generated rust bindings Viresh Kumar
@ 2022-10-14 10:47 ` Viresh Kumar
  2022-10-17 12:59   ` Kent Gibson
  2022-10-18  3:27   ` Kent Gibson
  2022-10-14 10:47 ` [PATCH V7 4/8] libgpiod: Add rust examples Viresh Kumar
                   ` (5 subsequent siblings)
  8 siblings, 2 replies; 50+ messages in thread
From: Viresh Kumar @ 2022-10-14 10:47 UTC (permalink / raw)
  To: Linus Walleij, Bartosz Golaszewski
  Cc: Viresh Kumar, Vincent Guittot, linux-gpio, Kent Gibson,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

Add rust wrapper crate, around the libpiod-sys crate added earlier, to
provide a convenient interface for the users.

Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
---
 bindings/rust/Cargo.toml                     |   1 +
 bindings/rust/libgpiod/Cargo.toml            |  13 +
 bindings/rust/libgpiod/src/chip.rs           | 253 ++++++++++
 bindings/rust/libgpiod/src/edge_event.rs     | 101 ++++
 bindings/rust/libgpiod/src/event_buffer.rs   |  66 +++
 bindings/rust/libgpiod/src/info_event.rs     |  68 +++
 bindings/rust/libgpiod/src/lib.rs            | 471 +++++++++++++++++++
 bindings/rust/libgpiod/src/line_config.rs    | 118 +++++
 bindings/rust/libgpiod/src/line_info.rs      | 180 +++++++
 bindings/rust/libgpiod/src/line_request.rs   | 246 ++++++++++
 bindings/rust/libgpiod/src/line_settings.rs  | 277 +++++++++++
 bindings/rust/libgpiod/src/request_config.rs |  96 ++++
 12 files changed, 1890 insertions(+)
 create mode 100644 bindings/rust/libgpiod/Cargo.toml
 create mode 100644 bindings/rust/libgpiod/src/chip.rs
 create mode 100644 bindings/rust/libgpiod/src/edge_event.rs
 create mode 100644 bindings/rust/libgpiod/src/event_buffer.rs
 create mode 100644 bindings/rust/libgpiod/src/info_event.rs
 create mode 100644 bindings/rust/libgpiod/src/lib.rs
 create mode 100644 bindings/rust/libgpiod/src/line_config.rs
 create mode 100644 bindings/rust/libgpiod/src/line_info.rs
 create mode 100644 bindings/rust/libgpiod/src/line_request.rs
 create mode 100644 bindings/rust/libgpiod/src/line_settings.rs
 create mode 100644 bindings/rust/libgpiod/src/request_config.rs

diff --git a/bindings/rust/Cargo.toml b/bindings/rust/Cargo.toml
index d0b3a3c88ff1..1e57ef2c0002 100644
--- a/bindings/rust/Cargo.toml
+++ b/bindings/rust/Cargo.toml
@@ -2,4 +2,5 @@
 
 members = [
     "libgpiod-sys",
+    "libgpiod"
 ]
diff --git a/bindings/rust/libgpiod/Cargo.toml b/bindings/rust/libgpiod/Cargo.toml
new file mode 100644
index 000000000000..f25242abb153
--- /dev/null
+++ b/bindings/rust/libgpiod/Cargo.toml
@@ -0,0 +1,13 @@
+[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]
+intmap = "2.0.0"
+libc = ">=0.2.39"
+libgpiod-sys = { path = "../libgpiod-sys" }
+thiserror = "1.0"
+vmm-sys-util = "=0.10.0"
diff --git a/bindings/rust/libgpiod/src/chip.rs b/bindings/rust/libgpiod/src/chip.rs
new file mode 100644
index 000000000000..4f52c3f141f4
--- /dev/null
+++ b/bindings/rust/libgpiod/src/chip.rs
@@ -0,0 +1,253 @@
+// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
+//
+// Copyright 2022 Linaro Ltd. All Rights Reserved.
+//     Viresh Kumar <viresh.kumar@linaro.org>
+
+use libc::strlen;
+use std::os::raw::c_char;
+use std::path::Path;
+use std::sync::Arc;
+use std::time::Duration;
+use std::{slice, str};
+
+use vmm_sys_util::errno::Error as Errno;
+
+use super::{gpiod, info, line, request, Error, Offset, OperationType, Result};
+
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub(crate) struct Internal {
+    chip: *mut gpiod::gpiod_chip,
+}
+
+impl Internal {
+    /// Find a chip by path.
+    pub(crate) fn open<P: AsRef<Path>>(path: &P) -> Result<Self> {
+        // Null-terminate the string
+        let path = path.as_ref().to_string_lossy() + "\0";
+
+        let chip = unsafe { gpiod::gpiod_chip_open(path.as_ptr() as *const c_char) };
+        if chip.is_null() {
+            return Err(Error::OperationFailed(
+                OperationType::ChipOpen,
+                Errno::last(),
+            ));
+        }
+
+        Ok(Self { chip })
+    }
+
+    /// Private helper, Returns gpiod_chip
+    pub(crate) fn chip(&self) -> *mut gpiod::gpiod_chip {
+        self.chip
+    }
+}
+
+impl Drop for Internal {
+    /// Close the chip and release all associated resources.
+    fn drop(&mut self) {
+        unsafe { gpiod::gpiod_chip_close(self.chip) }
+    }
+}
+
+/// 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.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Chip {
+    ichip: Arc<Internal>,
+    info: Info,
+}
+
+unsafe impl Send for Chip {}
+unsafe impl Sync for Chip {}
+
+impl Chip {
+    /// Find a chip by path.
+    pub fn open<P: AsRef<Path>>(path: &P) -> Result<Self> {
+        let ichip = Arc::new(Internal::open(path)?);
+        let info = Info::new(ichip.clone())?;
+
+        Ok(Self { ichip, info })
+    }
+
+    /// Get the chip name as represented in the kernel.
+    pub fn name(&self) -> Result<&str> {
+        self.info.name()
+    }
+
+    /// Get the chip label as represented in the kernel.
+    pub fn label(&self) -> Result<&str> {
+        self.info.label()
+    }
+
+    /// Get the number of GPIO lines exposed by the chip.
+    pub fn num_lines(&self) -> usize {
+        self.info.num_lines()
+    }
+
+    /// Get the path used to find the chip.
+    pub fn path(&self) -> Result<&str> {
+        // SAFETY: The string returned by libgpiod is guaranteed to live as long
+        // as the `struct Chip`.
+        let path = unsafe { gpiod::gpiod_chip_get_path(self.ichip.chip()) };
+
+        // SAFETY: The string is guaranteed to be valid here by the C API.
+        str::from_utf8(unsafe { slice::from_raw_parts(path as *const u8, strlen(path) as usize) })
+            .map_err(Error::StringNotUtf8)
+    }
+
+    /// Get information about the chip.
+    pub fn info(&self) -> Result<Info> {
+        Info::new(self.ichip.clone())
+    }
+
+    /// Get a snapshot of information about the line.
+    pub fn line_info(&self, offset: Offset) -> Result<line::Info> {
+        line::Info::new(self.ichip.clone(), offset)
+    }
+
+    /// Get the current snapshot of information about the line at given offset and start watching
+    /// it for future changes.
+    pub fn watch_line_info(&self, offset: Offset) -> Result<line::Info> {
+        line::Info::new_watch(self.ichip.clone(), offset)
+    }
+
+    /// Stop watching a line
+    pub fn unwatch(&self, offset: Offset) {
+        unsafe {
+            gpiod::gpiod_chip_unwatch_line_info(self.ichip.chip(), offset);
+        }
+    }
+
+    /// 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 fd(&self) -> Result<u32> {
+        let fd = unsafe { gpiod::gpiod_chip_get_fd(self.ichip.chip()) };
+
+        if fd < 0 {
+            Err(Error::OperationFailed(
+                OperationType::ChipGetFd,
+                Errno::last(),
+            ))
+        } else {
+            Ok(fd as u32)
+        }
+    }
+
+    /// Wait for line status events on any of the watched lines on the chip.
+    pub fn wait_info_event(&self, timeout: Option<Duration>) -> Result<bool> {
+        let timeout = match timeout {
+            Some(x) => x.as_nanos() as i64,
+            // Block indefinitely
+            None => -1,
+        };
+
+        let ret = unsafe { gpiod::gpiod_chip_wait_info_event(self.ichip.chip(), timeout) };
+
+        match ret {
+            -1 => Err(Error::OperationFailed(
+                OperationType::ChipWaitInfoEvent,
+                Errno::last(),
+            )),
+            0 => Ok(false),
+            _ => Ok(true),
+        }
+    }
+
+    /// 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<info::Event> {
+        info::Event::new(&self.ichip)
+    }
+
+    /// Map a GPIO line's name to its offset within the chip.
+    pub fn line_offset_from_name(&self, name: &str) -> Result<Offset> {
+        // Null-terminate the string
+        let name = name.to_owned() + "\0";
+
+        let ret = unsafe {
+            gpiod::gpiod_chip_get_line_offset_from_name(
+                self.ichip.chip(),
+                name.as_ptr() as *const c_char,
+            )
+        };
+
+        if ret == -1 {
+            Err(Error::OperationFailed(
+                OperationType::ChipGetLine,
+                Errno::last(),
+            ))
+        } else {
+            Ok(ret as u32)
+        }
+    }
+
+    /// Request a set of lines for exclusive usage.
+    pub fn request_lines(
+        &self,
+        rconfig: &request::Config,
+        lconfig: &line::Config,
+    ) -> Result<line::Request> {
+        line::Request::new(&self.ichip, rconfig, lconfig)
+    }
+}
+
+/// GPIO chip Information
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Info {
+    info: *mut gpiod::gpiod_chip_info,
+}
+
+impl Info {
+    /// Find a GPIO chip by path.
+    pub(crate) fn new(chip: Arc<Internal>) -> Result<Self> {
+        let info = unsafe { gpiod::gpiod_chip_get_info(chip.chip()) };
+        if info.is_null() {
+            return Err(Error::OperationFailed(
+                OperationType::ChipInfoGet,
+                Errno::last(),
+            ));
+        }
+
+        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 { gpiod::gpiod_chip_info_get_name(self.info) };
+
+        // SAFETY: The string is guaranteed to be valid here by the C API.
+        str::from_utf8(unsafe { slice::from_raw_parts(name as *const u8, strlen(name) as usize) })
+            .map_err(Error::StringNotUtf8)
+    }
+
+    /// 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 { gpiod::gpiod_chip_info_get_label(self.info) };
+
+        // SAFETY: The string is guaranteed to be valid here by the C API.
+        str::from_utf8(unsafe { slice::from_raw_parts(label as *const u8, strlen(label) as usize) })
+            .map_err(Error::StringNotUtf8)
+    }
+
+    /// Get the number of GPIO lines exposed by the chip.
+    pub(crate) fn num_lines(&self) -> usize {
+        unsafe { gpiod::gpiod_chip_info_get_num_lines(self.info) as usize }
+    }
+}
+
+impl Drop for Info {
+    /// Close the GPIO chip info and release all associated resources.
+    fn drop(&mut self) {
+        unsafe { gpiod::gpiod_chip_info_free(self.info) }
+    }
+}
diff --git a/bindings/rust/libgpiod/src/edge_event.rs b/bindings/rust/libgpiod/src/edge_event.rs
new file mode 100644
index 000000000000..12c0fd73f778
--- /dev/null
+++ b/bindings/rust/libgpiod/src/edge_event.rs
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
+//
+// Copyright 2022 Linaro Ltd. All Rights Reserved.
+//     Viresh Kumar <viresh.kumar@linaro.org>
+
+use std::time::Duration;
+
+use vmm_sys_util::errno::Error as Errno;
+
+use super::{edge::event::Buffer, gpiod, EdgeKind, Error, Offset, OperationType, 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.
+
+#[derive(Debug, Eq, PartialEq)]
+pub struct Event<'b> {
+    buffer: Option<&'b Buffer>,
+    event: *mut gpiod::gpiod_edge_event,
+}
+
+impl<'b> Event<'b> {
+    /// Get an event stored in the buffer.
+    pub(crate) fn new(buffer: &'b Buffer, index: u64) -> Result<Self> {
+        let event = unsafe { gpiod::gpiod_edge_event_buffer_get_event(buffer.buffer(), index) };
+        if event.is_null() {
+            return Err(Error::OperationFailed(
+                OperationType::EdgeEventBufferGetEvent,
+                Errno::last(),
+            ));
+        }
+
+        Ok(Self {
+            buffer: Some(buffer),
+            event,
+        })
+    }
+
+    pub fn event_clone(event: &Event) -> Result<Self> {
+        let event = unsafe { gpiod::gpiod_edge_event_copy(event.event) };
+        if event.is_null() {
+            return Err(Error::OperationFailed(
+                OperationType::EdgeEventCopy,
+                Errno::last(),
+            ));
+        }
+
+        Ok(Self {
+            buffer: None,
+            event,
+        })
+    }
+
+    /// Get the event type.
+    pub fn event_type(&self) -> Result<EdgeKind> {
+        EdgeKind::new(unsafe { gpiod::gpiod_edge_event_get_event_type(self.event) } as u32)
+    }
+
+    /// Get the timestamp of the event.
+    pub fn timestamp(&self) -> Duration {
+        Duration::from_nanos(unsafe { gpiod::gpiod_edge_event_get_timestamp_ns(self.event) })
+    }
+
+    /// Get the offset of the line on which the event was triggered.
+    pub fn line_offset(&self) -> Offset {
+        unsafe { gpiod::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 global_seqno(&self) -> u64 {
+        unsafe { gpiod::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 line_seqno(&self) -> u64 {
+        unsafe { gpiod::gpiod_edge_event_get_line_seqno(self.event) }
+    }
+}
+
+impl<'b> Drop for Event<'b> {
+    /// Free the edge event.
+    fn drop(&mut self) {
+        // Free the event only if a copy is made
+        if self.buffer.is_none() {
+            unsafe { gpiod::gpiod_edge_event_free(self.event) };
+        }
+    }
+}
diff --git a/bindings/rust/libgpiod/src/event_buffer.rs b/bindings/rust/libgpiod/src/event_buffer.rs
new file mode 100644
index 000000000000..11c8b5e1d7c9
--- /dev/null
+++ b/bindings/rust/libgpiod/src/event_buffer.rs
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
+//
+// Copyright 2022 Linaro Ltd. All Rights Reserved.
+//     Viresh Kumar <viresh.kumar@linaro.org>
+
+use std::os::raw::c_ulong;
+
+use vmm_sys_util::errno::Error as Errno;
+
+use super::{edge, gpiod, Error, OperationType, Result};
+
+/// Line edge events buffer
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Buffer {
+    buffer: *mut gpiod::gpiod_edge_event_buffer,
+}
+
+impl Buffer {
+    /// 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: usize) -> Result<Self> {
+        let buffer = unsafe { gpiod::gpiod_edge_event_buffer_new(capacity as c_ulong) };
+        if buffer.is_null() {
+            return Err(Error::OperationFailed(
+                OperationType::EdgeEventBufferNew,
+                Errno::last(),
+            ));
+        }
+
+        Ok(Self { buffer })
+    }
+
+    /// Private helper, Returns gpiod_edge_event_buffer
+    pub(crate) fn buffer(&self) -> *mut gpiod::gpiod_edge_event_buffer {
+        self.buffer
+    }
+
+    /// Get the capacity of the event buffer.
+    pub fn capacity(&self) -> usize {
+        unsafe { gpiod::gpiod_edge_event_buffer_get_capacity(self.buffer()) as usize }
+    }
+
+    /// Read an event stored in the buffer.
+    pub fn event(&self, index: u64) -> Result<edge::Event> {
+        edge::Event::new(self, index)
+    }
+
+    /// Get the number of events the buffer contains.
+    pub fn len(&self) -> usize {
+        unsafe { gpiod::gpiod_edge_event_buffer_get_num_events(self.buffer()) as usize }
+    }
+
+    /// Check if buffer is empty.
+    pub fn is_empty(&self) -> bool {
+        self.len() == 0
+    }
+}
+
+impl Drop for Buffer {
+    /// Free the edge event buffer and release all associated resources.
+    fn drop(&mut self) {
+        unsafe { gpiod::gpiod_edge_event_buffer_free(self.buffer) };
+    }
+}
diff --git a/bindings/rust/libgpiod/src/info_event.rs b/bindings/rust/libgpiod/src/info_event.rs
new file mode 100644
index 000000000000..d8be87df6679
--- /dev/null
+++ b/bindings/rust/libgpiod/src/info_event.rs
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
+//
+// Copyright 2022 Linaro Ltd. All Rights Reserved.
+//     Viresh Kumar <viresh.kumar@linaro.org>
+
+use std::sync::Arc;
+use std::time::Duration;
+
+use vmm_sys_util::errno::Error as Errno;
+
+use super::{chip, gpiod, line, Error, InfoChangeKind, OperationType, 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.
+
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Event {
+    event: *mut gpiod::gpiod_info_event,
+}
+
+impl Event {
+    /// Get a single chip's line's status change event.
+    pub(crate) fn new(ichip: &Arc<chip::Internal>) -> Result<Self> {
+        let event = unsafe { gpiod::gpiod_chip_read_info_event(ichip.chip()) };
+        if event.is_null() {
+            return Err(Error::OperationFailed(
+                OperationType::ChipReadInfoEvent,
+                Errno::last(),
+            ));
+        }
+
+        Ok(Self { event })
+    }
+
+    /// Private helper, Returns gpiod_info_event
+    pub(crate) fn event(&self) -> *mut gpiod::gpiod_info_event {
+        self.event
+    }
+
+    /// Get the event type of the status change event.
+    pub fn event_type(&self) -> Result<InfoChangeKind> {
+        InfoChangeKind::new(unsafe { gpiod::gpiod_info_event_get_event_type(self.event) } as u32)
+    }
+
+    /// Get the timestamp of the event, read from the monotonic clock.
+    pub fn timestamp(&self) -> Duration {
+        Duration::from_nanos(unsafe { gpiod::gpiod_info_event_get_timestamp_ns(self.event) })
+    }
+
+    /// Get the line-info object associated with the event.
+    pub fn line_info(&self) -> Result<line::Info> {
+        line::Info::new_from_event(self)
+    }
+}
+
+impl Drop for Event {
+    /// Free the info event object and release all associated resources.
+    fn drop(&mut self) {
+        unsafe { gpiod::gpiod_info_event_free(self.event) }
+    }
+}
diff --git a/bindings/rust/libgpiod/src/lib.rs b/bindings/rust/libgpiod/src/lib.rs
new file mode 100644
index 000000000000..c5d974a9f4ea
--- /dev/null
+++ b/bindings/rust/libgpiod/src/lib.rs
@@ -0,0 +1,471 @@
+// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
+//
+// Rust wrappers for GPIOD APIs
+//
+// Copyright 2022 Linaro Ltd. All Rights Reserved.
+//     Viresh Kumar <viresh.kumar@linaro.org>
+
+//! 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.
+
+/// GPIO chip related definitions.
+pub mod chip;
+
+mod edge_event;
+mod event_buffer;
+
+/// GPIO chip edge event related definitions.
+pub mod edge {
+    pub use crate::edge_event::*;
+
+    /// GPIO chip edge event buffer related definitions.
+    pub mod event {
+        pub use crate::event_buffer::*;
+    }
+}
+
+mod info_event;
+
+/// GPIO chip info event related definitions.
+pub mod info {
+    pub use crate::info_event::*;
+}
+
+mod line_config;
+mod line_info;
+mod line_request;
+mod line_settings;
+
+/// GPIO chip line related definitions.
+pub mod line {
+    pub use crate::line_config::*;
+    pub use crate::line_info::*;
+    pub use crate::line_request::*;
+    pub use crate::line_settings::*;
+}
+
+mod request_config;
+
+/// GPIO chip request related definitions.
+pub mod request {
+    pub use crate::request_config::*;
+}
+
+use libgpiod_sys as gpiod;
+
+use intmap::IntMap;
+use libc::strlen;
+use std::fs;
+use std::os::raw::c_char;
+use std::path::Path;
+use std::time::Duration;
+use std::{fmt, slice, str};
+
+use thiserror::Error as ThisError;
+use vmm_sys_util::errno::Error as Errno;
+
+/// Operation types, used with OperationFailed() Error.
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub enum OperationType {
+    ChipOpen,
+    ChipGetFd,
+    ChipWaitInfoEvent,
+    ChipGetLine,
+    ChipGetLineInfo,
+    ChipInfoGet,
+    ChipReadInfoEvent,
+    ChipWatchLineInfo,
+    EdgeEventBufferGetEvent,
+    EdgeEventCopy,
+    EdgeEventBufferNew,
+    InfoEventGetLineInfo,
+    LineConfigNew,
+    LineConfigAddSettings,
+    LineConfigGetOffsets,
+    LineConfigGetSetting,
+    LineRequest,
+    LineRequestReconfigLines,
+    LineRequestGetVal,
+    LineRequestGetValSubset,
+    LineRequestSetVal,
+    LineRequestSetValSubset,
+    LineRequestReadEdgeEvent,
+    LineRequestWaitEdgeEvent,
+    LineSettingsNew,
+    LineSettingsCopy,
+    LineSettingsGetOutVal,
+    LineSettingsSetDirection,
+    LineSettingsSetEdgeDetection,
+    LineSettingsSetBias,
+    LineSettingsSetDrive,
+    LineSettingsSetActiveLow,
+    LineSettingsSetDebouncePeriod,
+    LineSettingsSetEventClock,
+    LineSettingsSetOutputValue,
+    RequestConfigNew,
+    RequestConfigGetConsumer,
+    SimBankGetVal,
+    SimBankNew,
+    SimBankSetLabel,
+    SimBankSetNumLines,
+    SimBankSetLineName,
+    SimBankSetPull,
+    SimBankHogLine,
+    SimCtxNew,
+    SimDevNew,
+    SimDevEnable,
+    SimDevDisable,
+}
+
+impl fmt::Display for OperationType {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "{:?}", self)
+    }
+}
+
+/// 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 get {0}")]
+    NullString(&'static str),
+    #[error("String not utf8: {0:?}")]
+    StringNotUtf8(str::Utf8Error),
+    #[error("Invalid enum {0} value: {1}")]
+    InvalidEnumValue(&'static str, u32),
+    #[error("Operation {0} Failed: {1}")]
+    OperationFailed(OperationType, Errno),
+    #[error("Invalid Arguments")]
+    InvalidArguments,
+    #[error("Std Io Error")]
+    IoError,
+}
+
+/// Value settings.
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub enum Value {
+    /// Active
+    Active,
+    /// Inactive
+    InActive,
+}
+
+/// Maps offset to Value.
+pub type ValueMap = IntMap<Value>;
+
+impl Value {
+    pub fn new(val: i32) -> Result<Self> {
+        match val {
+            0 => Ok(Value::InActive),
+            1 => Ok(Value::Active),
+            _ => Err(Error::InvalidEnumValue("Value", val as u32)),
+        }
+    }
+
+    fn value(&self) -> i32 {
+        match self {
+            Value::Active => 1,
+            Value::InActive => 0,
+        }
+    }
+}
+
+/// Offset type.
+pub type Offset = u32;
+
+/// Direction settings.
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+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 {
+            gpiod::GPIOD_LINE_DIRECTION_AS_IS => Ok(Direction::AsIs),
+            gpiod::GPIOD_LINE_DIRECTION_INPUT => Ok(Direction::Input),
+            gpiod::GPIOD_LINE_DIRECTION_OUTPUT => Ok(Direction::Output),
+            _ => Err(Error::InvalidEnumValue("Direction", dir)),
+        }
+    }
+
+    fn gpiod_direction(&self) -> u32 {
+        match self {
+            Direction::AsIs => gpiod::GPIOD_LINE_DIRECTION_AS_IS,
+            Direction::Input => gpiod::GPIOD_LINE_DIRECTION_INPUT,
+            Direction::Output => gpiod::GPIOD_LINE_DIRECTION_OUTPUT,
+        }
+    }
+}
+
+/// Internal bias settings.
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub enum Bias {
+    /// The internal bias is disabled.
+    Disabled,
+    /// The internal pull-up bias is enabled.
+    PullUp,
+    /// The internal pull-down bias is enabled.
+    PullDown,
+}
+
+impl Bias {
+    fn new(bias: u32) -> Result<Option<Self>> {
+        match bias {
+            gpiod::GPIOD_LINE_BIAS_UNKNOWN => Ok(None),
+            gpiod::GPIOD_LINE_BIAS_AS_IS => Ok(None),
+            gpiod::GPIOD_LINE_BIAS_DISABLED => Ok(Some(Bias::Disabled)),
+            gpiod::GPIOD_LINE_BIAS_PULL_UP => Ok(Some(Bias::PullUp)),
+            gpiod::GPIOD_LINE_BIAS_PULL_DOWN => Ok(Some(Bias::PullDown)),
+            _ => Err(Error::InvalidEnumValue("Bias", bias)),
+        }
+    }
+
+    fn gpiod_bias(bias: Option<Bias>) -> u32 {
+        match bias {
+            None => gpiod::GPIOD_LINE_BIAS_AS_IS,
+            Some(bias) => match bias {
+                Bias::Disabled => gpiod::GPIOD_LINE_BIAS_DISABLED,
+                Bias::PullUp => gpiod::GPIOD_LINE_BIAS_PULL_UP,
+                Bias::PullDown => gpiod::GPIOD_LINE_BIAS_PULL_DOWN,
+            },
+        }
+    }
+}
+
+/// Drive settings.
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+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 {
+            gpiod::GPIOD_LINE_DRIVE_PUSH_PULL => Ok(Drive::PushPull),
+            gpiod::GPIOD_LINE_DRIVE_OPEN_DRAIN => Ok(Drive::OpenDrain),
+            gpiod::GPIOD_LINE_DRIVE_OPEN_SOURCE => Ok(Drive::OpenSource),
+            _ => Err(Error::InvalidEnumValue("Drive", drive)),
+        }
+    }
+
+    fn gpiod_drive(&self) -> u32 {
+        match self {
+            Drive::PushPull => gpiod::GPIOD_LINE_DRIVE_PUSH_PULL,
+            Drive::OpenDrain => gpiod::GPIOD_LINE_DRIVE_OPEN_DRAIN,
+            Drive::OpenSource => gpiod::GPIOD_LINE_DRIVE_OPEN_SOURCE,
+        }
+    }
+}
+
+/// Edge detection settings.
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub enum Edge {
+    /// Line detects rising edge events.
+    Rising,
+    /// Line detects falling edge events.
+    Falling,
+    /// Line detects both rising and falling edge events.
+    Both,
+}
+
+impl Edge {
+    fn new(edge: u32) -> Result<Option<Self>> {
+        match edge {
+            gpiod::GPIOD_LINE_EDGE_NONE => Ok(None),
+            gpiod::GPIOD_LINE_EDGE_RISING => Ok(Some(Edge::Rising)),
+            gpiod::GPIOD_LINE_EDGE_FALLING => Ok(Some(Edge::Falling)),
+            gpiod::GPIOD_LINE_EDGE_BOTH => Ok(Some(Edge::Both)),
+            _ => Err(Error::InvalidEnumValue("Edge", edge)),
+        }
+    }
+
+    fn gpiod_edge(edge: Option<Edge>) -> u32 {
+        match edge {
+            None => gpiod::GPIOD_LINE_EDGE_NONE,
+            Some(edge) => match edge {
+                Edge::Rising => gpiod::GPIOD_LINE_EDGE_RISING,
+                Edge::Falling => gpiod::GPIOD_LINE_EDGE_FALLING,
+                Edge::Both => gpiod::GPIOD_LINE_EDGE_BOTH,
+            },
+        }
+    }
+}
+
+/// Line setting kind.
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub enum SettingKind {
+    /// Line direction.
+    Direction,
+    /// Bias.
+    Bias,
+    /// Drive.
+    Drive,
+    /// Edge detection.
+    EdgeDetection,
+    /// Active-low setting.
+    ActiveLow,
+    /// Debounce period.
+    DebouncePeriod,
+    /// Event clock type.
+    EventClock,
+    /// Output value.
+    OutputValue,
+}
+
+/// Line settings.
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub enum SettingVal {
+    /// Line direction.
+    Direction(Direction),
+    /// Bias.
+    Bias(Option<Bias>),
+    /// Drive.
+    Drive(Drive),
+    /// Edge detection.
+    EdgeDetection(Option<Edge>),
+    /// Active-low setting.
+    ActiveLow(bool),
+    /// Debounce period.
+    DebouncePeriod(Duration),
+    /// Event clock type.
+    EventClock(EventClock),
+    /// Output value.
+    OutputValue(Value),
+}
+
+impl fmt::Display for SettingVal {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "{:?}", self)
+    }
+}
+
+/// Event clock settings.
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub enum EventClock {
+    /// Line uses the monotonic clock for edge event timestamps.
+    Monotonic,
+    /// Line uses the realtime clock for edge event timestamps.
+    Realtime,
+    /// Line uses the hardware timestamp engine clock for edge event timestamps.
+    HTE,
+}
+
+impl EventClock {
+    fn new(clock: u32) -> Result<Self> {
+        match clock {
+            gpiod::GPIOD_LINE_EVENT_CLOCK_MONOTONIC => Ok(EventClock::Monotonic),
+            gpiod::GPIOD_LINE_EVENT_CLOCK_REALTIME => Ok(EventClock::Realtime),
+            gpiod::GPIOD_LINE_EVENT_CLOCK_HTE => Ok(EventClock::HTE),
+            _ => Err(Error::InvalidEnumValue("Eventclock", clock)),
+        }
+    }
+
+    fn gpiod_clock(&self) -> u32 {
+        match self {
+            EventClock::Monotonic => gpiod::GPIOD_LINE_EVENT_CLOCK_MONOTONIC,
+            EventClock::Realtime => gpiod::GPIOD_LINE_EVENT_CLOCK_REALTIME,
+            EventClock::HTE => gpiod::GPIOD_LINE_EVENT_CLOCK_HTE,
+        }
+    }
+}
+
+/// Line status change event types.
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub enum InfoChangeKind {
+    /// Line has been requested.
+    LineRequested,
+    /// Previously requested line has been released.
+    LineReleased,
+    /// Line configuration has changed.
+    LineConfigChanged,
+}
+
+impl InfoChangeKind {
+    fn new(kind: u32) -> Result<Self> {
+        match kind {
+            gpiod::GPIOD_INFO_EVENT_LINE_REQUESTED => Ok(InfoChangeKind::LineRequested),
+            gpiod::GPIOD_INFO_EVENT_LINE_RELEASED => Ok(InfoChangeKind::LineReleased),
+            gpiod::GPIOD_INFO_EVENT_LINE_CONFIG_CHANGED => Ok(InfoChangeKind::LineConfigChanged),
+            _ => Err(Error::InvalidEnumValue("InfoChangeKind", kind)),
+        }
+    }
+}
+
+/// Edge event types.
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub enum EdgeKind {
+    /// Rising edge event.
+    Rising,
+    /// Falling edge event.
+    Falling,
+}
+
+impl EdgeKind {
+    fn new(kind: u32) -> Result<Self> {
+        match kind {
+            gpiod::GPIOD_EDGE_EVENT_RISING_EDGE => Ok(EdgeKind::Rising),
+            gpiod::GPIOD_EDGE_EVENT_FALLING_EDGE => Ok(EdgeKind::Falling),
+            _ => Err(Error::InvalidEnumValue("EdgeEvent", kind)),
+        }
+    }
+}
+
+/// Various libgpiod-related functions.
+
+/// Check if the file pointed to by path is a GPIO chip character device.
+///
+/// Returns true if the file exists and is a GPIO chip character device or a
+/// symbolic link to it.
+pub fn is_gpiochip_device<P: AsRef<Path>>(path: &P) -> bool {
+    // Null-terminate the string
+    let path = path.as_ref().to_string_lossy() + "\0";
+
+    unsafe { gpiod::gpiod_is_gpiochip_device(path.as_ptr() as *const c_char) }
+}
+
+/// Iterator for GPIO devices.
+pub fn gpiochip_devices<P: AsRef<Path>>(path: &P) -> Result<Vec<chip::Chip>> {
+    let mut chips = Vec::new();
+
+    for entry in fs::read_dir(path).map_err(|_| Error::IoError)?.flatten() {
+        let path = entry.path();
+
+        if is_gpiochip_device(&path) {
+            chips.push(chip::Chip::open(&path)?);
+        }
+    }
+
+    Ok(chips)
+}
+
+/// Get the API version of the library as a human-readable string.
+pub fn version_string() -> Result<&'static str> {
+    // SAFETY: The string returned by libgpiod is guaranteed to live forever.
+    let version = unsafe { gpiod::gpiod_version_string() };
+
+    if version.is_null() {
+        return Err(Error::NullString("GPIO library version"));
+    }
+
+    // SAFETY: The string is guaranteed to be valid here by the C API.
+    str::from_utf8(unsafe { slice::from_raw_parts(version as *const u8, strlen(version) as usize) })
+        .map_err(Error::StringNotUtf8)
+}
diff --git a/bindings/rust/libgpiod/src/line_config.rs b/bindings/rust/libgpiod/src/line_config.rs
new file mode 100644
index 000000000000..ea25452fd1d6
--- /dev/null
+++ b/bindings/rust/libgpiod/src/line_config.rs
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
+//
+// Copyright 2022 Linaro Ltd. All Rights Reserved.
+//     Viresh Kumar <viresh.kumar@linaro.org>
+
+use std::os::raw::c_ulong;
+use std::slice::from_raw_parts;
+
+use vmm_sys_util::errno::Error as Errno;
+
+use super::{gpiod, line::Settings, Error, Offset, OperationType, 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 empty. Using it in a request will lead to an
+/// error. In order to a line-config to become useful, it needs to be assigned
+/// at least one offset-to-settings mapping by calling
+/// ::gpiod_line_config_add_line_settings.
+///
+/// When calling ::gpiod_chip_request_lines, the library will request all
+/// offsets that were assigned settings in the order that they were assigned.
+
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Config {
+    config: *mut gpiod::gpiod_line_config,
+}
+
+impl Config {
+    /// Create a new line config object.
+    pub fn new() -> Result<Self> {
+        let config = unsafe { gpiod::gpiod_line_config_new() };
+
+        if config.is_null() {
+            return Err(Error::OperationFailed(
+                OperationType::LineConfigNew,
+                Errno::last(),
+            ));
+        }
+
+        Ok(Self { config })
+    }
+
+    /// Private helper, Returns gpiod_line_config
+    pub(crate) fn config(&self) -> *mut gpiod::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 { gpiod::gpiod_line_config_reset(self.config) }
+    }
+
+    /// Add line settings for a set of offsets.
+    pub fn add_line_settings(&self, offsets: &[Offset], settings: Settings) -> Result<()> {
+        let ret = unsafe {
+            gpiod::gpiod_line_config_add_line_settings(
+                self.config,
+                offsets.as_ptr(),
+                offsets.len() as c_ulong,
+                settings.settings(),
+            )
+        };
+
+        if ret == -1 {
+            Err(Error::OperationFailed(
+                OperationType::LineConfigAddSettings,
+                Errno::last(),
+            ))
+        } else {
+            Ok(())
+        }
+    }
+
+    /// Get line settings for offset.
+    pub fn line_settings(&self, offset: Offset) -> Result<Settings> {
+        let settings = unsafe { gpiod::gpiod_line_config_get_line_settings(self.config, offset) };
+
+        if settings.is_null() {
+            return Err(Error::OperationFailed(
+                OperationType::LineConfigGetSetting,
+                Errno::last(),
+            ));
+        }
+
+        Ok(Settings::new_with_settings(settings))
+    }
+
+    /// Get configured offsets.
+    pub fn offsets(&self) -> Result<Vec<Offset>> {
+        let mut num: u64 = 0;
+        let mut offsets: *mut Offset = std::ptr::null_mut();
+
+        let ret =
+            unsafe { gpiod::gpiod_line_config_get_offsets(self.config, &mut num, &mut offsets) };
+
+        if ret == -1 {
+            Err(Error::OperationFailed(
+                OperationType::LineConfigGetOffsets,
+                Errno::last(),
+            ))
+        } else {
+            Ok(unsafe { from_raw_parts(offsets as *const Offset, num as usize).to_vec() })
+        }
+    }
+}
+
+impl Drop for Config {
+    /// Free the line config object and release all associated resources.
+    fn drop(&mut self) {
+        unsafe { gpiod::gpiod_line_config_free(self.config) }
+    }
+}
diff --git a/bindings/rust/libgpiod/src/line_info.rs b/bindings/rust/libgpiod/src/line_info.rs
new file mode 100644
index 000000000000..9db51fc30efd
--- /dev/null
+++ b/bindings/rust/libgpiod/src/line_info.rs
@@ -0,0 +1,180 @@
+// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
+//
+// Copyright 2022 Linaro Ltd. All Rights Reserved.
+//     Viresh Kumar <viresh.kumar@linaro.org>
+
+use libc::strlen;
+use std::sync::Arc;
+use std::time::Duration;
+use std::{slice, str};
+
+use vmm_sys_util::errno::Error as Errno;
+
+use super::{
+    chip, gpiod, info, Bias, Direction, Drive, Edge, Error, EventClock, Offset, OperationType,
+    Result,
+};
+
+/// 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.
+
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Info {
+    info: *mut gpiod::gpiod_line_info,
+    info_event: bool,
+}
+
+impl Info {
+    /// Get a snapshot of information about the line and optionally start watching it for changes.
+    pub(crate) fn new(ichip: Arc<chip::Internal>, offset: Offset) -> Result<Self> {
+        let info = unsafe { gpiod::gpiod_chip_get_line_info(ichip.chip(), offset) };
+
+        if info.is_null() {
+            return Err(Error::OperationFailed(
+                OperationType::ChipGetLineInfo,
+                Errno::last(),
+            ));
+        }
+
+        Ok(Self {
+            info,
+            info_event: false,
+        })
+    }
+
+    pub(crate) fn new_watch(ichip: Arc<chip::Internal>, offset: Offset) -> Result<Self> {
+        let info = unsafe { gpiod::gpiod_chip_watch_line_info(ichip.chip(), offset) };
+
+        if info.is_null() {
+            return Err(Error::OperationFailed(
+                OperationType::ChipWatchLineInfo,
+                Errno::last(),
+            ));
+        }
+
+        Ok(Self {
+            info,
+            info_event: false,
+        })
+    }
+
+    /// Get the Line info object associated with an event.
+    pub(crate) fn new_from_event(event: &info::Event) -> Result<Self> {
+        let info = unsafe { gpiod::gpiod_info_event_get_line_info(event.event()) };
+
+        if info.is_null() {
+            return Err(Error::OperationFailed(
+                OperationType::InfoEventGetLineInfo,
+                Errno::last(),
+            ));
+        }
+
+        Ok(Self {
+            info,
+            info_event: true,
+        })
+    }
+
+    /// 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 offset(&self) -> Offset {
+        unsafe { gpiod::gpiod_line_info_get_offset(self.info) }
+    }
+
+    /// Get GPIO line's name.
+    pub fn name(&self) -> Result<&str> {
+        // SAFETY: The string returned by libgpiod is guaranteed to live as long
+        // as the `struct Info`.
+        let name = unsafe { gpiod::gpiod_line_info_get_name(self.info) };
+        if name.is_null() {
+            return Err(Error::NullString("GPIO line's name"));
+        }
+
+        // SAFETY: The string is guaranteed to be valid here by the C API.
+        str::from_utf8(unsafe { slice::from_raw_parts(name as *const u8, strlen(name) as usize) })
+            .map_err(Error::StringNotUtf8)
+    }
+
+    /// 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 { gpiod::gpiod_line_info_is_used(self.info) }
+    }
+
+    /// Get the GPIO line's consumer name.
+    pub fn consumer(&self) -> Result<&str> {
+        // SAFETY: The string returned by libgpiod is guaranteed to live as long
+        // as the `struct Info`.
+        let name = unsafe { gpiod::gpiod_line_info_get_consumer(self.info) };
+        if name.is_null() {
+            return Err(Error::NullString("GPIO line's consumer name"));
+        }
+
+        // SAFETY: The string is guaranteed to be valid here by the C API.
+        str::from_utf8(unsafe { slice::from_raw_parts(name as *const u8, strlen(name) as usize) })
+            .map_err(Error::StringNotUtf8)
+    }
+
+    /// Get the GPIO line's direction.
+    pub fn direction(&self) -> Result<Direction> {
+        Direction::new(unsafe { gpiod::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 { gpiod::gpiod_line_info_is_active_low(self.info) }
+    }
+
+    /// Get the GPIO line's bias setting.
+    pub fn bias(&self) -> Result<Option<Bias>> {
+        Bias::new(unsafe { gpiod::gpiod_line_info_get_bias(self.info) } as u32)
+    }
+
+    /// Get the GPIO line's drive setting.
+    pub fn drive(&self) -> Result<Drive> {
+        Drive::new(unsafe { gpiod::gpiod_line_info_get_drive(self.info) } as u32)
+    }
+
+    /// Get the current edge detection setting of the line.
+    pub fn edge_detection(&self) -> Result<Option<Edge>> {
+        Edge::new(unsafe { gpiod::gpiod_line_info_get_edge_detection(self.info) } as u32)
+    }
+
+    /// Get the current event clock setting used for edge event timestamps.
+    pub fn event_clock(&self) -> Result<EventClock> {
+        EventClock::new(unsafe { gpiod::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 { gpiod::gpiod_line_info_is_debounced(self.info) }
+    }
+
+    /// Get the debounce period of the line.
+    pub fn debounce_period(&self) -> Duration {
+        Duration::from_micros(unsafe { gpiod::gpiod_line_info_get_debounce_period_us(self.info) })
+    }
+}
+
+impl Drop for Info {
+    fn drop(&mut self) {
+        // We must not free the Line info object created from `struct info::Event` by calling
+        // libgpiod API.
+        if !self.info_event {
+            unsafe { gpiod::gpiod_line_info_free(self.info) }
+        }
+    }
+}
diff --git a/bindings/rust/libgpiod/src/line_request.rs b/bindings/rust/libgpiod/src/line_request.rs
new file mode 100644
index 000000000000..e4ff951aef29
--- /dev/null
+++ b/bindings/rust/libgpiod/src/line_request.rs
@@ -0,0 +1,246 @@
+// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
+//
+// Copyright 2022 Linaro Ltd. All Rights Reserved.
+//     Viresh Kumar <viresh.kumar@linaro.org>
+
+use libc::EINVAL;
+use std::os::raw::c_ulong;
+use std::sync::Arc;
+use std::time::Duration;
+
+use vmm_sys_util::errno::Error as Errno;
+
+use super::{
+    chip, edge, gpiod, line, request, Error, Offset, OperationType, Result, Value, ValueMap,
+};
+
+/// Line request operations
+///
+/// Allows interaction with a set of requested lines.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Request {
+    request: *mut gpiod::gpiod_line_request,
+}
+
+impl Request {
+    /// Request a set of lines for exclusive usage.
+    pub(crate) fn new(
+        ichip: &Arc<chip::Internal>,
+        rconfig: &request::Config,
+        lconfig: &line::Config,
+    ) -> Result<Self> {
+        let request = unsafe {
+            gpiod::gpiod_chip_request_lines(ichip.chip(), rconfig.config(), lconfig.config())
+        };
+
+        if request.is_null() {
+            return Err(Error::OperationFailed(
+                OperationType::LineRequest,
+                Errno::last(),
+            ));
+        }
+
+        Ok(Self { request })
+    }
+
+    /// Get the number of lines in the request.
+    pub fn num_lines(&self) -> usize {
+        unsafe { gpiod::gpiod_line_request_get_num_lines(self.request) as usize }
+    }
+
+    /// Get the offsets of lines in the request.
+    pub fn offsets(&self) -> Vec<Offset> {
+        let mut offsets = vec![0; self.num_lines() as usize];
+
+        unsafe { gpiod::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 value(&self, offset: Offset) -> Result<Value> {
+        let value = unsafe { gpiod::gpiod_line_request_get_value(self.request, offset) };
+
+        if value != 0 && value != 1 {
+            Err(Error::OperationFailed(
+                OperationType::LineRequestGetVal,
+                Errno::last(),
+            ))
+        } else {
+            Value::new(value)
+        }
+    }
+
+    /// Get values of a subset of lines associated with the request.
+    pub fn values_subset(&self, offsets: &[Offset]) -> Result<ValueMap> {
+        let mut values = vec![0; offsets.len()];
+
+        let ret = unsafe {
+            gpiod::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(
+                OperationType::LineRequestGetValSubset,
+                Errno::last(),
+            ))
+        } else {
+            let mut map = ValueMap::new();
+
+            for (i, val) in values.iter().enumerate() {
+                map.insert(offsets[i].into(), Value::new(*val)?);
+            }
+
+            Ok(map)
+        }
+    }
+
+    /// Get values of all lines associated with the request.
+    pub fn values(&self) -> Result<ValueMap> {
+        self.values_subset(&self.offsets())
+    }
+
+    /// Set the value of a single line associated with the request.
+    pub fn set_value(&self, offset: Offset, value: Value) -> Result<()> {
+        let ret =
+            unsafe { gpiod::gpiod_line_request_set_value(self.request, offset, value.value()) };
+
+        if ret == -1 {
+            Err(Error::OperationFailed(
+                OperationType::LineRequestSetVal,
+                Errno::last(),
+            ))
+        } else {
+            Ok(())
+        }
+    }
+
+    /// Get values of a subset of lines associated with the request.
+    pub fn set_values_subset(&self, map: ValueMap) -> Result<()> {
+        let mut offsets = Vec::new();
+        let mut values = Vec::new();
+
+        for (offset, value) in map {
+            offsets.push(offset as u32);
+            values.push(value.value());
+        }
+
+        let ret = unsafe {
+            gpiod::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(
+                OperationType::LineRequestSetValSubset,
+                Errno::last(),
+            ))
+        } else {
+            Ok(())
+        }
+    }
+
+    /// Get values of all lines associated with the request.
+    pub fn set_values(&self, values: &[Value]) -> Result<()> {
+        if values.len() != self.num_lines() as usize {
+            return Err(Error::OperationFailed(
+                OperationType::LineRequestSetVal,
+                Errno::new(EINVAL),
+            ));
+        }
+
+        let mut new_values = Vec::new();
+        for value in values {
+            new_values.push(value.value());
+        }
+
+        let ret =
+            unsafe { gpiod::gpiod_line_request_set_values(self.request, new_values.as_ptr()) };
+
+        if ret == -1 {
+            Err(Error::OperationFailed(
+                OperationType::LineRequestSetVal,
+                Errno::last(),
+            ))
+        } else {
+            Ok(())
+        }
+    }
+
+    /// Update the configuration of lines associated with the line request.
+    pub fn reconfigure_lines(&self, lconfig: &line::Config) -> Result<()> {
+        let ret =
+            unsafe { gpiod::gpiod_line_request_reconfigure_lines(self.request, lconfig.config()) };
+
+        if ret == -1 {
+            Err(Error::OperationFailed(
+                OperationType::LineRequestReconfigLines,
+                Errno::last(),
+            ))
+        } else {
+            Ok(())
+        }
+    }
+
+    /// Get the file descriptor associated with the line request.
+    pub fn fd(&self) -> u32 {
+        unsafe { gpiod::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: Option<Duration>) -> Result<bool> {
+        let timeout = match timeout {
+            Some(x) => x.as_nanos() as i64,
+            // Block indefinitely
+            None => -1,
+        };
+
+        let ret = unsafe { gpiod::gpiod_line_request_wait_edge_event(self.request, timeout) };
+
+        match ret {
+            -1 => Err(Error::OperationFailed(
+                OperationType::LineRequestWaitEdgeEvent,
+                Errno::last(),
+            )),
+            0 => Ok(false),
+            _ => Ok(true),
+        }
+    }
+
+    /// 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_events(&self, buffer: &mut edge::event::Buffer) -> Result<u32> {
+        let ret = unsafe {
+            gpiod::gpiod_line_request_read_edge_event(
+                self.request,
+                buffer.buffer(),
+                buffer.capacity() as u64,
+            )
+        };
+
+        if ret == -1 {
+            Err(Error::OperationFailed(
+                OperationType::LineRequestReadEdgeEvent,
+                Errno::last(),
+            ))
+        } else {
+            Ok(ret as u32)
+        }
+    }
+}
+
+impl Drop for Request {
+    /// Release the requested lines and free all associated resources.
+    fn drop(&mut self) {
+        unsafe { gpiod::gpiod_line_request_release(self.request) }
+    }
+}
diff --git a/bindings/rust/libgpiod/src/line_settings.rs b/bindings/rust/libgpiod/src/line_settings.rs
new file mode 100644
index 000000000000..2c3090132ea5
--- /dev/null
+++ b/bindings/rust/libgpiod/src/line_settings.rs
@@ -0,0 +1,277 @@
+// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
+//
+// Copyright 2022 Linaro Ltd. All Rights Reserved.
+//     Viresh Kumar <viresh.kumar@linaro.org>
+
+use std::time::Duration;
+
+use vmm_sys_util::errno::Error as Errno;
+
+use super::{
+    gpiod, Bias, Direction, Drive, Edge, Error, EventClock, OperationType, Result, SettingKind,
+    SettingVal, Value,
+};
+
+/// Line settings objects.
+///
+/// Line settings object contains a set of line properties that can be used
+/// when requesting lines or reconfiguring an existing request.
+///
+/// Mutators in general can only fail if the new property value is invalid. The
+/// return values can be safely ignored - the object remains valid even after
+/// a mutator fails and simply uses the sane default appropriate for given
+/// property.
+
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Settings {
+    settings: *mut gpiod::gpiod_line_settings,
+}
+
+impl Settings {
+    /// Create a new line settings object.
+    pub fn new() -> Result<Self> {
+        let settings = unsafe { gpiod::gpiod_line_settings_new() };
+
+        if settings.is_null() {
+            return Err(Error::OperationFailed(
+                OperationType::LineSettingsNew,
+                Errno::last(),
+            ));
+        }
+
+        Ok(Self { settings })
+    }
+
+    pub fn new_with_settings(settings: *mut gpiod::gpiod_line_settings) -> Self {
+        Self { settings }
+    }
+
+    /// Private helper, Returns gpiod_line_settings
+    pub(crate) fn settings(&self) -> *mut gpiod::gpiod_line_settings {
+        self.settings
+    }
+
+    /// Resets the line settings object to its default values.
+    pub fn reset(&mut self) {
+        unsafe { gpiod::gpiod_line_settings_reset(self.settings) }
+    }
+
+    /// Makes copy of the settings object.
+    pub fn settings_clone(&self) -> Result<Self> {
+        let settings = unsafe { gpiod::gpiod_line_settings_copy(self.settings) };
+        if settings.is_null() {
+            return Err(Error::OperationFailed(
+                OperationType::LineSettingsCopy,
+                Errno::last(),
+            ));
+        }
+
+        Ok(Self { settings })
+    }
+
+    /// Set line prop setting.
+    pub fn set_prop(&mut self, values: &[SettingVal]) -> Result<()> {
+        for value in values {
+            match value {
+                SettingVal::Direction(val) => self.set_direction(*val)?,
+                SettingVal::EdgeDetection(val) => self.set_edge_detection(*val)?,
+                SettingVal::Bias(val) => self.set_bias(*val)?,
+                SettingVal::Drive(val) => self.set_drive(*val)?,
+                SettingVal::ActiveLow(val) => self.set_active_low(*val),
+                SettingVal::DebouncePeriod(val) => self.set_debounce_period(*val),
+                SettingVal::EventClock(val) => self.set_event_clock(*val)?,
+                SettingVal::OutputValue(val) => self.set_output_value(*val)?,
+            }
+        }
+
+        Ok(())
+    }
+
+    /// Get the line prop setting.
+    pub fn prop(&self, property: SettingKind) -> Result<SettingVal> {
+        Ok(match property {
+            SettingKind::Direction => SettingVal::Direction(self.direction()?),
+            SettingKind::EdgeDetection => SettingVal::EdgeDetection(self.edge_detection()?),
+            SettingKind::Bias => SettingVal::Bias(self.bias()?),
+            SettingKind::Drive => SettingVal::Drive(self.drive()?),
+            SettingKind::ActiveLow => SettingVal::ActiveLow(self.active_low()),
+            SettingKind::DebouncePeriod => SettingVal::DebouncePeriod(self.debounce_period()?),
+            SettingKind::EventClock => SettingVal::EventClock(self.event_clock()?),
+            SettingKind::OutputValue => SettingVal::OutputValue(self.output_value()?),
+        })
+    }
+
+    /// Set the line direction.
+    fn set_direction(&mut self, direction: Direction) -> Result<()> {
+        let ret = unsafe {
+            gpiod::gpiod_line_settings_set_direction(
+                self.settings,
+                direction.gpiod_direction() as i32,
+            )
+        };
+
+        if ret == -1 {
+            Err(Error::OperationFailed(
+                OperationType::LineSettingsSetDirection,
+                Errno::last(),
+            ))
+        } else {
+            Ok(())
+        }
+    }
+
+    /// Get the direction setting.
+    fn direction(&self) -> Result<Direction> {
+        Direction::new(unsafe { gpiod::gpiod_line_settings_get_direction(self.settings) } as u32)
+    }
+
+    /// Set the edge event detection setting.
+    fn set_edge_detection(&mut self, edge: Option<Edge>) -> Result<()> {
+        let ret = unsafe {
+            gpiod::gpiod_line_settings_set_edge_detection(
+                self.settings,
+                Edge::gpiod_edge(edge) as i32,
+            )
+        };
+
+        if ret == -1 {
+            Err(Error::OperationFailed(
+                OperationType::LineSettingsSetEdgeDetection,
+                Errno::last(),
+            ))
+        } else {
+            Ok(())
+        }
+    }
+
+    /// Get the edge event detection setting.
+    fn edge_detection(&self) -> Result<Option<Edge>> {
+        Edge::new(unsafe { gpiod::gpiod_line_settings_get_edge_detection(self.settings) } as u32)
+    }
+
+    /// Set the bias setting.
+    fn set_bias(&mut self, bias: Option<Bias>) -> Result<()> {
+        let ret = unsafe {
+            gpiod::gpiod_line_settings_set_bias(self.settings, Bias::gpiod_bias(bias) as i32)
+        };
+
+        if ret == -1 {
+            Err(Error::OperationFailed(
+                OperationType::LineSettingsSetBias,
+                Errno::last(),
+            ))
+        } else {
+            Ok(())
+        }
+    }
+
+    /// Get the bias setting.
+    fn bias(&self) -> Result<Option<Bias>> {
+        Bias::new(unsafe { gpiod::gpiod_line_settings_get_bias(self.settings) } as u32)
+    }
+
+    /// Set the drive setting.
+    fn set_drive(&mut self, drive: Drive) -> Result<()> {
+        let ret = unsafe {
+            gpiod::gpiod_line_settings_set_drive(self.settings, drive.gpiod_drive() as i32)
+        };
+
+        if ret == -1 {
+            Err(Error::OperationFailed(
+                OperationType::LineSettingsSetDrive,
+                Errno::last(),
+            ))
+        } else {
+            Ok(())
+        }
+    }
+
+    /// Get the drive setting.
+    fn drive(&self) -> Result<Drive> {
+        Drive::new(unsafe { gpiod::gpiod_line_settings_get_drive(self.settings) } as u32)
+    }
+
+    /// Set active-low setting.
+    fn set_active_low(&mut self, active_low: bool) {
+        unsafe { gpiod::gpiod_line_settings_set_active_low(self.settings, active_low) }
+    }
+
+    /// Check the active-low setting.
+    fn active_low(&self) -> bool {
+        unsafe { gpiod::gpiod_line_settings_get_active_low(self.settings) }
+    }
+
+    /// Set the debounce period setting.
+    fn set_debounce_period(&mut self, period: Duration) {
+        unsafe {
+            gpiod::gpiod_line_settings_set_debounce_period_us(
+                self.settings,
+                period.as_micros() as u64,
+            )
+        }
+    }
+
+    /// Get the debounce period.
+    fn debounce_period(&self) -> Result<Duration> {
+        Ok(Duration::from_micros(unsafe {
+            gpiod::gpiod_line_settings_get_debounce_period_us(self.settings)
+        }))
+    }
+
+    /// Set the event clock setting.
+    fn set_event_clock(&mut self, clock: EventClock) -> Result<()> {
+        let ret = unsafe {
+            gpiod::gpiod_line_settings_set_event_clock(self.settings, clock.gpiod_clock() as i32)
+        };
+
+        if ret == -1 {
+            Err(Error::OperationFailed(
+                OperationType::LineSettingsSetEventClock,
+                Errno::last(),
+            ))
+        } else {
+            Ok(())
+        }
+    }
+
+    /// Get the event clock setting.
+    fn event_clock(&self) -> Result<EventClock> {
+        EventClock::new(unsafe { gpiod::gpiod_line_settings_get_event_clock(self.settings) } as u32)
+    }
+
+    /// Set the output value setting.
+    fn set_output_value(&mut self, value: Value) -> Result<()> {
+        let ret =
+            unsafe { gpiod::gpiod_line_settings_set_output_value(self.settings, value.value()) };
+
+        if ret == -1 {
+            Err(Error::OperationFailed(
+                OperationType::LineSettingsSetOutputValue,
+                Errno::last(),
+            ))
+        } else {
+            Ok(())
+        }
+    }
+
+    /// Get the output value, 0 or 1.
+    fn output_value(&self) -> Result<Value> {
+        let value = unsafe { gpiod::gpiod_line_settings_get_output_value(self.settings) };
+
+        if value != 0 && value != 1 {
+            Err(Error::OperationFailed(
+                OperationType::LineSettingsGetOutVal,
+                Errno::last(),
+            ))
+        } else {
+            Value::new(value)
+        }
+    }
+}
+
+impl Drop for Settings {
+    /// Free the line settings object and release all associated resources.
+    fn drop(&mut self) {
+        unsafe { gpiod::gpiod_line_settings_free(self.settings) }
+    }
+}
diff --git a/bindings/rust/libgpiod/src/request_config.rs b/bindings/rust/libgpiod/src/request_config.rs
new file mode 100644
index 000000000000..760d9c755c86
--- /dev/null
+++ b/bindings/rust/libgpiod/src/request_config.rs
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
+//
+// Copyright 2022 Linaro Ltd. All Rights Reserved.
+//     Viresh Kumar <viresh.kumar@linaro.org>
+
+use libc::strlen;
+use std::os::raw::{c_char, c_ulong};
+use std::{slice, str};
+
+use vmm_sys_util::errno::Error as Errno;
+
+use super::{gpiod, Error, OperationType, 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. The mutators don't return error values. If the
+/// values are invalid, in general they are silently adjusted to acceptable
+/// ranges.
+
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Config {
+    config: *mut gpiod::gpiod_request_config,
+}
+
+impl Config {
+    /// Create a new request config object.
+    pub fn new() -> Result<Self> {
+        let config = unsafe { gpiod::gpiod_request_config_new() };
+        if config.is_null() {
+            return Err(Error::OperationFailed(
+                OperationType::RequestConfigNew,
+                Errno::last(),
+            ));
+        }
+
+        Ok(Self { config })
+    }
+
+    /// Private helper, Returns gpiod_request_config
+    pub(crate) fn config(&self) -> *mut gpiod::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 {
+            gpiod::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 consumer(&self) -> Result<&str> {
+        // SAFETY: The string returned by libgpiod is guaranteed to live as long
+        // as the `struct Config`.
+        let consumer = unsafe { gpiod::gpiod_request_config_get_consumer(self.config) };
+        if consumer.is_null() {
+            return Err(Error::OperationFailed(
+                OperationType::RequestConfigGetConsumer,
+                Errno::last(),
+            ));
+        }
+
+        // SAFETY: The string is guaranteed to be valid here by the C API.
+        str::from_utf8(unsafe {
+            slice::from_raw_parts(consumer as *const u8, strlen(consumer) as usize)
+        })
+        .map_err(Error::StringNotUtf8)
+    }
+
+    /// Set the size of the kernel event buffer for the request.
+    pub fn set_event_buffer_size(&self, size: usize) {
+        unsafe { gpiod::gpiod_request_config_set_event_buffer_size(self.config, size as c_ulong) }
+    }
+
+    /// Get the edge event buffer size setting for the request config.
+    pub fn event_buffer_size(&self) -> usize {
+        unsafe { gpiod::gpiod_request_config_get_event_buffer_size(self.config) as usize }
+    }
+}
+
+impl Drop for Config {
+    /// Free the request config object and release all associated resources.
+    fn drop(&mut self) {
+        unsafe { gpiod::gpiod_request_config_free(self.config) }
+    }
+}
-- 
2.31.1.272.g89b43f80a514


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH V7 4/8] libgpiod: Add rust examples
  2022-10-14 10:47 [PATCH V7 0/8] libgpiod: Add Rust bindings Viresh Kumar
                   ` (2 preceding siblings ...)
  2022-10-14 10:47 ` [PATCH V7 3/8] libgpiod: Add rust wrapper crate Viresh Kumar
@ 2022-10-14 10:47 ` Viresh Kumar
  2022-10-17 13:00   ` Kent Gibson
  2022-10-14 10:47 ` [PATCH V7 5/8] libgpiod: Add gpiosim rust crate Viresh Kumar
                   ` (4 subsequent siblings)
  8 siblings, 1 reply; 50+ messages in thread
From: Viresh Kumar @ 2022-10-14 10:47 UTC (permalink / raw)
  To: Linus Walleij, Bartosz Golaszewski
  Cc: Viresh Kumar, Vincent Guittot, linux-gpio, Kent Gibson,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

Add examples for the usage of the rust bindings, quite similar to the
ones in cxx bindings.

Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
---
 bindings/rust/libgpiod/examples/gpiodetect.rs | 30 ++++++
 bindings/rust/libgpiod/examples/gpiofind.rs   | 35 +++++++
 bindings/rust/libgpiod/examples/gpioget.rs    | 42 ++++++++
 bindings/rust/libgpiod/examples/gpioinfo.rs   | 95 +++++++++++++++++++
 bindings/rust/libgpiod/examples/gpiomon.rs    | 73 ++++++++++++++
 bindings/rust/libgpiod/examples/gpioset.rs    | 60 ++++++++++++
 bindings/rust/libgpiod/examples/gpiowatch.rs  | 54 +++++++++++
 7 files changed, 389 insertions(+)
 create mode 100644 bindings/rust/libgpiod/examples/gpiodetect.rs
 create mode 100644 bindings/rust/libgpiod/examples/gpiofind.rs
 create mode 100644 bindings/rust/libgpiod/examples/gpioget.rs
 create mode 100644 bindings/rust/libgpiod/examples/gpioinfo.rs
 create mode 100644 bindings/rust/libgpiod/examples/gpiomon.rs
 create mode 100644 bindings/rust/libgpiod/examples/gpioset.rs
 create mode 100644 bindings/rust/libgpiod/examples/gpiowatch.rs

diff --git a/bindings/rust/libgpiod/examples/gpiodetect.rs b/bindings/rust/libgpiod/examples/gpiodetect.rs
new file mode 100644
index 000000000000..f24ac72e2d48
--- /dev/null
+++ b/bindings/rust/libgpiod/examples/gpiodetect.rs
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
+//
+// Copyright 2022 Linaro Ltd. All Rights Reserved.
+//     Viresh Kumar <viresh.kumar@linaro.org>
+//
+// Simplified Rust implementation of gpiodetect tool.
+
+use std::env;
+use std::path::Path;
+
+use libgpiod::{gpiochip_devices, Error, Result};
+
+fn main() -> Result<()> {
+    let args: Vec<String> = env::args().collect();
+    if args.len() > 1 {
+        println!("Usage: {}", args[0]);
+        return Err(Error::InvalidArguments);
+    }
+
+    for chip in gpiochip_devices(&Path::new("/dev"))? {
+        println!(
+            "{} [{}] ({})",
+            chip.name()?,
+            chip.label()?,
+            chip.num_lines(),
+        );
+    }
+
+    Ok(())
+}
diff --git a/bindings/rust/libgpiod/examples/gpiofind.rs b/bindings/rust/libgpiod/examples/gpiofind.rs
new file mode 100644
index 000000000000..07e886bc3896
--- /dev/null
+++ b/bindings/rust/libgpiod/examples/gpiofind.rs
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
+//
+// Copyright 2022 Linaro Ltd. All Rights Reserved.
+//     Viresh Kumar <viresh.kumar@linaro.org>
+//
+// Simplified Rust implementation of gpiofind tool.
+
+use std::env;
+use std::path::Path;
+
+use libgpiod::{gpiochip_devices, Error, Result};
+
+fn main() -> Result<()> {
+    let args: Vec<String> = env::args().collect();
+    if args.len() != 2 {
+        println!("Usage: {} <line-name>", args[0]);
+        return Err(Error::InvalidArguments);
+    }
+
+    for chip in gpiochip_devices(&Path::new("/dev"))? {
+        let offset = chip.line_offset_from_name(&args[1]);
+        if offset.is_ok() {
+            println!(
+                "Line {} found: Chip: {}, offset: {}",
+                args[1],
+                chip.name()?,
+                offset?
+            );
+            return Ok(());
+        }
+    }
+
+    println!("Failed to find line: {}", args[1]);
+    Ok(())
+}
diff --git a/bindings/rust/libgpiod/examples/gpioget.rs b/bindings/rust/libgpiod/examples/gpioget.rs
new file mode 100644
index 000000000000..f4c111987d96
--- /dev/null
+++ b/bindings/rust/libgpiod/examples/gpioget.rs
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
+//
+// Copyright 2022 Linaro Ltd. All Rights Reserved.
+//     Viresh Kumar <viresh.kumar@linaro.org>
+//
+// Simplified Rust implementation of gpioget tool.
+
+use std::env;
+
+use libgpiod::{chip::Chip, line, request, Direction, Error, Offset, Result, SettingVal};
+
+fn main() -> Result<()> {
+    let args: Vec<String> = env::args().collect();
+    if args.len() < 3 {
+        println!("Usage: {} <chip> <line_offset0> ...", args[0]);
+        return Err(Error::InvalidArguments);
+    }
+
+    let mut lsettings = line::Settings::new()?;
+    let lconfig = line::Config::new()?;
+    let mut offsets = Vec::<Offset>::new();
+
+    for arg in &args[2..] {
+        let offset = arg.parse::<Offset>().map_err(|_| Error::InvalidArguments)?;
+        offsets.push(offset);
+    }
+
+    lsettings.set_prop(&[SettingVal::Direction(Direction::Input)])?;
+    lconfig.add_line_settings(&offsets, lsettings)?;
+
+    let path = format!("/dev/gpiochip{}", args[1]);
+    let chip = Chip::open(&path)?;
+
+    let rconfig = request::Config::new()?;
+    rconfig.set_consumer(&args[0]);
+
+    let request = chip.request_lines(&rconfig, &lconfig)?;
+    let map = request.values()?;
+
+    println!("{:?}", map);
+    Ok(())
+}
diff --git a/bindings/rust/libgpiod/examples/gpioinfo.rs b/bindings/rust/libgpiod/examples/gpioinfo.rs
new file mode 100644
index 000000000000..8b9fcb000f63
--- /dev/null
+++ b/bindings/rust/libgpiod/examples/gpioinfo.rs
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
+//
+// Copyright 2022 Linaro Ltd. All Rights Reserved.
+//     Viresh Kumar <viresh.kumar@linaro.org>
+//
+// Simplified Rust implementation of gpioinfo tool.
+
+use std::env;
+use std::path::Path;
+
+use libgpiod::{
+    chip::Chip, gpiochip_devices, is_gpiochip_device, Direction, Error, Offset, Result,
+};
+
+fn line_info(chip: &Chip, offset: Offset) -> Result<()> {
+    let info = chip.line_info(offset)?;
+    let off = info.offset();
+
+    let name = match info.name() {
+        Ok(name) => name,
+        _ => "unused",
+    };
+
+    let consumer = match info.consumer() {
+        Ok(name) => name,
+        _ => "unnamed",
+    };
+
+    let low = if info.is_active_low() {
+        "active-low"
+    } else {
+        "active-high"
+    };
+
+    let dir = match info.direction()? {
+        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
+    );
+
+    Ok(())
+}
+
+fn chip_info(chip: &Chip) -> Result<()> {
+    let ngpio = chip.num_lines();
+
+    println!("GPIO Chip name: {}", chip.name()?);
+    println!("\tlabel: {}", chip.label()?);
+    println!("\tpath: {}", chip.path()?);
+    println!("\tngpio: {}\n", ngpio);
+
+    println!("\tLine information:");
+
+    for offset in 0..ngpio {
+        line_info(chip, offset as Offset)?;
+    }
+    println!("\n");
+
+    Ok(())
+}
+
+fn main() -> Result<()> {
+    let args: Vec<String> = env::args().collect();
+    if args.len() > 2 {
+        println!("Usage: {}", args[0]);
+        return Err(Error::InvalidArguments);
+    }
+
+    if args.len() == 1 {
+        for chip in gpiochip_devices(&Path::new("/dev"))? {
+            chip_info(&chip)?;
+        }
+    } else {
+        let index = args[1]
+            .parse::<u32>()
+            .map_err(|_| Error::InvalidArguments)?;
+        let path = format!("/dev/gpiochip{}", index);
+        if is_gpiochip_device(&path) {
+            let chip = Chip::open(&path)?;
+
+            chip_info(&chip)?;
+        }
+    }
+
+    Ok(())
+}
diff --git a/bindings/rust/libgpiod/examples/gpiomon.rs b/bindings/rust/libgpiod/examples/gpiomon.rs
new file mode 100644
index 000000000000..2f3af2d2daa0
--- /dev/null
+++ b/bindings/rust/libgpiod/examples/gpiomon.rs
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
+//
+// Copyright 2022 Linaro Ltd. All Rights Reserved.
+//     Viresh Kumar <viresh.kumar@linaro.org>
+//
+// Simplified Rust implementation of the gpiomon tool.
+
+use std::env;
+
+use libgpiod::{
+    chip::Chip, edge, line, request, Edge, EdgeKind, Error, Offset, Result, SettingVal,
+};
+
+fn usage(name: &str) {
+    println!("Usage: {} <chip> <offset0> ...", name);
+}
+
+fn main() -> Result<()> {
+    let args: Vec<String> = env::args().collect();
+    if args.len() < 3 {
+        usage(&args[0]);
+        return Err(Error::InvalidArguments);
+    }
+
+    let mut lsettings = line::Settings::new()?;
+    let lconfig = line::Config::new()?;
+    let mut offsets = Vec::<Offset>::new();
+
+    for arg in &args[2..] {
+        let offset = arg.parse::<Offset>().map_err(|_| Error::InvalidArguments)?;
+        offsets.push(offset);
+    }
+
+    lsettings.set_prop(&[SettingVal::EdgeDetection(Some(Edge::Both))])?;
+    lconfig.add_line_settings(&offsets, lsettings)?;
+
+    let path = format!("/dev/gpiochip{}", args[1]);
+    let chip = Chip::open(&path)?;
+
+    let rconfig = request::Config::new()?;
+
+    let mut buffer = edge::event::Buffer::new(1)?;
+    let request = chip.request_lines(&rconfig, &lconfig)?;
+
+    loop {
+        match request.wait_edge_event(None) {
+            Err(x) => {
+                println!("{:?}", x);
+                return Err(Error::InvalidArguments);
+            }
+
+            Ok(false) => {
+                // This shouldn't happen as the call is blocking.
+                panic!();
+            }
+            Ok(true) => (),
+        }
+
+        let count = request.read_edge_events(&mut buffer)?;
+        if count == 1 {
+            let event = buffer.event(0)?;
+            println!(
+                "line: {} type: {}, time: {:?}",
+                event.line_offset(),
+                match event.event_type()? {
+                    EdgeKind::Rising => "Rising",
+                    EdgeKind::Falling => "Falling",
+                },
+                event.timestamp()
+            );
+        }
+    }
+}
diff --git a/bindings/rust/libgpiod/examples/gpioset.rs b/bindings/rust/libgpiod/examples/gpioset.rs
new file mode 100644
index 000000000000..56ed241ce9de
--- /dev/null
+++ b/bindings/rust/libgpiod/examples/gpioset.rs
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
+//
+// Copyright 2022 Linaro Ltd. All Rights Reserved.
+//     Viresh Kumar <viresh.kumar@linaro.org>
+//
+// Simplified Rust implementation of the gpioset tool.
+
+use std::env;
+use std::io::{stdin, Read};
+
+use libgpiod::{chip::Chip, line, request, Direction, Error, Offset, Result, SettingVal, Value};
+
+fn usage(name: &str) {
+    println!("Usage: {} <chip> <line_offset0>=<value0> ...", name);
+}
+
+fn main() -> Result<()> {
+    let args: Vec<String> = env::args().collect();
+    if args.len() < 3 {
+        usage(&args[0]);
+        return Err(Error::InvalidArguments);
+    }
+
+    let lconfig = line::Config::new()?;
+
+    for arg in &args[2..] {
+        let pair: Vec<&str> = arg.split('=').collect();
+        if pair.len() != 2 {
+            usage(&args[0]);
+            return Err(Error::InvalidArguments);
+        }
+
+        let offset = pair[0]
+            .parse::<Offset>()
+            .map_err(|_| Error::InvalidArguments)?;
+        let value = pair[1]
+            .parse::<i32>()
+            .map_err(|_| Error::InvalidArguments)?;
+
+        let mut lsettings = line::Settings::new()?;
+        lsettings.set_prop(&[
+            SettingVal::Direction(Direction::Output),
+            SettingVal::OutputValue(Value::new(value)?),
+        ])?;
+        lconfig.add_line_settings(&[offset], lsettings)?;
+    }
+
+    let path = format!("/dev/gpiochip{}", args[1]);
+    let chip = Chip::open(&path)?;
+
+    let rconfig = request::Config::new()?;
+    rconfig.set_consumer(&args[0]);
+
+    chip.request_lines(&rconfig, &lconfig)?;
+
+    // Wait for keypress, let user verify line status.
+    stdin().read_exact(&mut [0u8]).unwrap();
+
+    Ok(())
+}
diff --git a/bindings/rust/libgpiod/examples/gpiowatch.rs b/bindings/rust/libgpiod/examples/gpiowatch.rs
new file mode 100644
index 000000000000..030168ba9788
--- /dev/null
+++ b/bindings/rust/libgpiod/examples/gpiowatch.rs
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
+//
+// Copyright 2022 Linaro Ltd. All Rights Reserved.
+//     Viresh Kumar <viresh.kumar@linaro.org>
+//
+// Simplified Rust implementation of the gpiowatch tool.
+
+use std::env;
+
+use libgpiod::{chip::Chip, Error, Offset, Result};
+
+fn usage(name: &str) {
+    println!("Usage: {} <chip> <offset0> ...", name);
+}
+
+fn main() -> Result<()> {
+    let args: Vec<String> = env::args().collect();
+    if args.len() < 2 {
+        usage(&args[0]);
+        return Err(Error::InvalidArguments);
+    }
+
+    let path = format!("/dev/gpiochip{}", args[1]);
+    let offset = args[2]
+        .parse::<Offset>()
+        .map_err(|_| Error::InvalidArguments)?;
+
+    let chip = Chip::open(&path)?;
+    let _info = chip.watch_line_info(offset)?;
+
+    match chip.wait_info_event(None) {
+        Err(x) => {
+            println!("{:?}", x);
+            return Err(Error::InvalidArguments);
+        }
+
+        Ok(false) => {
+            // This shouldn't happen as the call is blocking.
+            panic!();
+        }
+        Ok(true) => (),
+    }
+
+    let event = chip.read_info_event()?;
+    println!(
+        "line: {} type: {:?}, time: {:?}",
+        offset,
+        event.event_type()?,
+        event.timestamp()
+    );
+
+    chip.unwatch(offset);
+    Ok(())
+}
-- 
2.31.1.272.g89b43f80a514


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH V7 5/8] libgpiod: Add gpiosim rust crate
  2022-10-14 10:47 [PATCH V7 0/8] libgpiod: Add Rust bindings Viresh Kumar
                   ` (3 preceding siblings ...)
  2022-10-14 10:47 ` [PATCH V7 4/8] libgpiod: Add rust examples Viresh Kumar
@ 2022-10-14 10:47 ` Viresh Kumar
  2022-10-17 13:00   ` Kent Gibson
  2022-10-14 10:47 ` [PATCH V7 6/8] gpiosim: Add pre generated rust bindings Viresh Kumar
                   ` (3 subsequent siblings)
  8 siblings, 1 reply; 50+ messages in thread
From: Viresh Kumar @ 2022-10-14 10:47 UTC (permalink / raw)
  To: Linus Walleij, Bartosz Golaszewski
  Cc: Viresh Kumar, Vincent Guittot, linux-gpio, Kent Gibson,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

This adds gpiosim rust crate, which provides helpers to emulate GPIO
chips.

Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
---
 bindings/rust/Cargo.toml         |   3 +-
 bindings/rust/gpiosim/Cargo.toml |  18 ++
 bindings/rust/gpiosim/build.rs   |  43 ++++
 bindings/rust/gpiosim/src/lib.rs |  25 +++
 bindings/rust/gpiosim/src/sim.rs | 323 +++++++++++++++++++++++++++++++
 5 files changed, 411 insertions(+), 1 deletion(-)
 create mode 100644 bindings/rust/gpiosim/Cargo.toml
 create mode 100644 bindings/rust/gpiosim/build.rs
 create mode 100644 bindings/rust/gpiosim/src/lib.rs
 create mode 100644 bindings/rust/gpiosim/src/sim.rs

diff --git a/bindings/rust/Cargo.toml b/bindings/rust/Cargo.toml
index 1e57ef2c0002..8721bc610b86 100644
--- a/bindings/rust/Cargo.toml
+++ b/bindings/rust/Cargo.toml
@@ -2,5 +2,6 @@
 
 members = [
     "libgpiod-sys",
-    "libgpiod"
+    "libgpiod",
+    "gpiosim"
 ]
diff --git a/bindings/rust/gpiosim/Cargo.toml b/bindings/rust/gpiosim/Cargo.toml
new file mode 100644
index 000000000000..d6b4cc34339b
--- /dev/null
+++ b/bindings/rust/gpiosim/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+name = "gpiosim"
+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 = { path = "../libgpiod" }
+vmm-sys-util = "=0.10.0"
+
+[features]
+generate = [ "bindgen" ]
+
+[build-dependencies]
+bindgen = { version = "0.59.1", optional = true }
+cc = "1.0.46"
diff --git a/bindings/rust/gpiosim/build.rs b/bindings/rust/gpiosim/build.rs
new file mode 100644
index 000000000000..460fb8c092c3
--- /dev/null
+++ b/bindings/rust/gpiosim/build.rs
@@ -0,0 +1,43 @@
+#[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() {
+    // Tell cargo to invalidate the built crate whenever following files change
+    println!("cargo:rerun-if-changed=../../../tests/gpiosim/gpiosim.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()
+        // The input header we would like to generate
+        // bindings for.
+        .header("../../../tests/gpiosim/gpiosim.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 main() {
+    #[cfg(feature = "generate")]
+    generate_bindings();
+
+    println!("cargo:rustc-link-lib=kmod");
+    println!("cargo:rustc-link-lib=mount");
+    println!("cargo:rustc-link-search=./../../tests/gpiosim/.libs/");
+    println!("cargo:rustc-link-lib=static=gpiosim");
+}
diff --git a/bindings/rust/gpiosim/src/lib.rs b/bindings/rust/gpiosim/src/lib.rs
new file mode 100644
index 000000000000..94d0ddb38e0f
--- /dev/null
+++ b/bindings/rust/gpiosim/src/lib.rs
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#[allow(non_camel_case_types, non_upper_case_globals)]
+#[cfg_attr(test, allow(deref_nullptr, non_snake_case))]
+#[allow(dead_code)]
+mod bindings_raw {
+    #[cfg(feature = "generate")]
+    include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
+
+    #[cfg(not(feature = "generate"))]
+    include!("bindings.rs");
+}
+
+use bindings_raw::*;
+
+pub use bindings_raw::{
+    GPIOSIM_HOG_DIR_INPUT, GPIOSIM_HOG_DIR_OUTPUT_HIGH, GPIOSIM_HOG_DIR_OUTPUT_LOW,
+    GPIOSIM_PULL_DOWN, GPIOSIM_PULL_UP, GPIOSIM_VALUE_ACTIVE, GPIOSIM_VALUE_INACTIVE,
+};
+
+#[allow(dead_code)]
+mod sim;
+
+#[allow(unused_imports)]
+pub use sim::*;
diff --git a/bindings/rust/gpiosim/src/sim.rs b/bindings/rust/gpiosim/src/sim.rs
new file mode 100644
index 000000000000..50977ea54ebb
--- /dev/null
+++ b/bindings/rust/gpiosim/src/sim.rs
@@ -0,0 +1,323 @@
+// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
+//
+// Copyright 2022 Linaro Ltd. All Rights Reserved.
+//     Viresh Kumar <viresh.kumar@linaro.org>
+
+use libc::strlen;
+use std::os::raw::c_char;
+use std::path::PathBuf;
+use std::{slice, str};
+
+use vmm_sys_util::errno::Error as Errno;
+
+use libgpiod::{Error, Offset, OperationType, Result};
+
+use crate::*;
+
+/// Sim Ctx
+#[derive(Debug)]
+struct SimCtx {
+    ctx: *mut gpiosim_ctx,
+}
+
+unsafe impl Send for SimCtx {}
+unsafe impl Sync for SimCtx {}
+
+impl SimCtx {
+    fn new() -> Result<Self> {
+        let ctx = unsafe { gpiosim_ctx_new() };
+        if ctx.is_null() {
+            return Err(Error::OperationFailed(
+                OperationType::SimCtxNew,
+                Errno::last(),
+            ));
+        }
+
+        Ok(Self { ctx })
+    }
+
+    fn ctx(&self) -> *mut gpiosim_ctx {
+        self.ctx
+    }
+}
+
+impl Drop for SimCtx {
+    fn drop(&mut self) {
+        unsafe { gpiosim_ctx_unref(self.ctx) }
+    }
+}
+
+/// Sim Dev
+#[derive(Debug)]
+struct SimDev {
+    dev: *mut gpiosim_dev,
+}
+
+unsafe impl Send for SimDev {}
+unsafe impl Sync for SimDev {}
+
+impl SimDev {
+    fn new(ctx: &SimCtx) -> Result<Self> {
+        let dev = unsafe { gpiosim_dev_new(ctx.ctx()) };
+        if dev.is_null() {
+            return Err(Error::OperationFailed(
+                OperationType::SimDevNew,
+                Errno::last(),
+            ));
+        }
+
+        Ok(Self { dev })
+    }
+
+    fn dev(&self) -> *mut gpiosim_dev {
+        self.dev
+    }
+
+    fn enable(&self) -> Result<()> {
+        let ret = unsafe { gpiosim_dev_enable(self.dev) };
+
+        if ret == -1 {
+            Err(Error::OperationFailed(
+                OperationType::SimDevEnable,
+                Errno::last(),
+            ))
+        } else {
+            Ok(())
+        }
+    }
+
+    fn disable(&self) -> Result<()> {
+        let ret = unsafe { gpiosim_dev_disable(self.dev) };
+
+        if ret == -1 {
+            Err(Error::OperationFailed(
+                OperationType::SimDevDisable,
+                Errno::last(),
+            ))
+        } else {
+            Ok(())
+        }
+    }
+}
+
+impl Drop for SimDev {
+    fn drop(&mut self) {
+        unsafe { gpiosim_dev_unref(self.dev) }
+    }
+}
+
+/// Sim Bank
+#[derive(Debug)]
+struct SimBank {
+    bank: *mut gpiosim_bank,
+}
+
+unsafe impl Send for SimBank {}
+unsafe impl Sync for SimBank {}
+
+impl SimBank {
+    fn new(dev: &SimDev) -> Result<Self> {
+        let bank = unsafe { gpiosim_bank_new(dev.dev()) };
+        if bank.is_null() {
+            return Err(Error::OperationFailed(
+                OperationType::SimBankNew,
+                Errno::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 { gpiosim_bank_get_chip_name(self.bank) };
+
+        // SAFETY: The string is guaranteed to be valid here.
+        str::from_utf8(unsafe { slice::from_raw_parts(name as *const u8, strlen(name) as usize) })
+            .map_err(Error::StringNotUtf8)
+    }
+
+    fn dev_path(&self) -> Result<PathBuf> {
+        // SAFETY: The string returned by gpiosim is guaranteed to live as long
+        // as the `struct SimBank`.
+        let path = unsafe { gpiosim_bank_get_dev_path(self.bank) };
+
+        // SAFETY: The string is guaranteed to be valid here.
+        let path = str::from_utf8(unsafe {
+            slice::from_raw_parts(path as *const u8, strlen(path) as usize)
+        })
+        .map_err(Error::StringNotUtf8)?;
+
+        Ok(PathBuf::from(path))
+    }
+
+    fn val(&self, offset: Offset) -> Result<u32> {
+        let ret = unsafe { gpiosim_bank_get_value(self.bank, offset) };
+
+        if ret == -1 {
+            Err(Error::OperationFailed(
+                OperationType::SimBankGetVal,
+                Errno::last(),
+            ))
+        } else {
+            Ok(ret as u32)
+        }
+    }
+
+    fn set_label(&self, label: &str) -> Result<()> {
+        // Null-terminate the string
+        let label = label.to_owned() + "\0";
+
+        let ret = unsafe { gpiosim_bank_set_label(self.bank, label.as_ptr() as *const c_char) };
+
+        if ret == -1 {
+            Err(Error::OperationFailed(
+                OperationType::SimBankSetLabel,
+                Errno::last(),
+            ))
+        } else {
+            Ok(())
+        }
+    }
+
+    fn set_num_lines(&self, num: u64) -> Result<()> {
+        let ret = unsafe { gpiosim_bank_set_num_lines(self.bank, num) };
+        if ret == -1 {
+            Err(Error::OperationFailed(
+                OperationType::SimBankSetNumLines,
+                Errno::last(),
+            ))
+        } else {
+            Ok(())
+        }
+    }
+
+    fn set_line_name(&self, offset: Offset, name: &str) -> Result<()> {
+        // Null-terminate the string
+        let name = name.to_owned() + "\0";
+
+        let ret = unsafe {
+            gpiosim_bank_set_line_name(self.bank, offset, name.as_ptr() as *const c_char)
+        };
+
+        if ret == -1 {
+            Err(Error::OperationFailed(
+                OperationType::SimBankSetLineName,
+                Errno::last(),
+            ))
+        } else {
+            Ok(())
+        }
+    }
+
+    fn set_pull(&self, offset: Offset, pull: i32) -> Result<()> {
+        let ret = unsafe { gpiosim_bank_set_pull(self.bank, offset, pull) };
+
+        if ret == -1 {
+            Err(Error::OperationFailed(
+                OperationType::SimBankSetPull,
+                Errno::last(),
+            ))
+        } else {
+            Ok(())
+        }
+    }
+
+    fn hog_line(&self, offset: Offset, name: &str, dir: i32) -> Result<()> {
+        // Null-terminate the string
+        let name = name.to_owned() + "\0";
+
+        let ret = unsafe {
+            gpiosim_bank_hog_line(self.bank, offset, name.as_ptr() as *const c_char, dir)
+        };
+
+        if ret == -1 {
+            Err(Error::OperationFailed(
+                OperationType::SimBankHogLine,
+                Errno::last(),
+            ))
+        } else {
+            Ok(())
+        }
+    }
+}
+
+impl Drop for SimBank {
+    fn drop(&mut self) {
+        unsafe { gpiosim_bank_unref(self.bank) }
+    }
+}
+
+/// GPIO SIM
+#[derive(Debug)]
+pub struct Sim {
+    ctx: SimCtx,
+    dev: SimDev,
+    bank: SimBank,
+}
+
+unsafe impl Send for Sim {}
+unsafe impl Sync for Sim {}
+
+impl Sim {
+    pub 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 fn chip_name(&self) -> &str {
+        self.bank.chip_name().unwrap()
+    }
+
+    pub fn dev_path(&self) -> PathBuf {
+        self.bank.dev_path().unwrap()
+    }
+
+    pub fn val(&self, offset: Offset) -> Result<u32> {
+        self.bank.val(offset)
+    }
+
+    pub fn set_label(&self, label: &str) -> Result<()> {
+        self.bank.set_label(label)
+    }
+
+    pub fn set_num_lines(&self, num: u64) -> Result<()> {
+        self.bank.set_num_lines(num)
+    }
+
+    pub fn set_line_name(&self, offset: Offset, name: &str) -> Result<()> {
+        self.bank.set_line_name(offset, name)
+    }
+
+    pub fn set_pull(&self, offset: Offset, pull: i32) -> Result<()> {
+        self.bank.set_pull(offset, pull)
+    }
+
+    pub fn hog_line(&self, offset: Offset, name: &str, dir: i32) -> Result<()> {
+        self.bank.hog_line(offset, name, dir)
+    }
+
+    pub fn enable(&self) -> Result<()> {
+        self.dev.enable()
+    }
+
+    pub fn disable(&self) -> Result<()> {
+        self.dev.disable()
+    }
+}
-- 
2.31.1.272.g89b43f80a514


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH V7 6/8] gpiosim: Add pre generated rust bindings
  2022-10-14 10:47 [PATCH V7 0/8] libgpiod: Add Rust bindings Viresh Kumar
                   ` (4 preceding siblings ...)
  2022-10-14 10:47 ` [PATCH V7 5/8] libgpiod: Add gpiosim rust crate Viresh Kumar
@ 2022-10-14 10:47 ` Viresh Kumar
  2022-10-14 10:47 ` [PATCH V7 7/8] libgpiod: Add rust tests Viresh Kumar
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 50+ messages in thread
From: Viresh Kumar @ 2022-10-14 10:47 UTC (permalink / raw)
  To: Linus Walleij, Bartosz Golaszewski
  Cc: Viresh Kumar, Vincent Guittot, linux-gpio, Kent Gibson,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

This adds a copy of pre generated bindings and adds the suggested way of
updating those in README.

Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
---
 bindings/rust/gpiosim/README.md       |  11 ++
 bindings/rust/gpiosim/src/bindings.rs | 180 ++++++++++++++++++++++++++
 2 files changed, 191 insertions(+)
 create mode 100644 bindings/rust/gpiosim/README.md
 create mode 100644 bindings/rust/gpiosim/src/bindings.rs

diff --git a/bindings/rust/gpiosim/README.md b/bindings/rust/gpiosim/README.md
new file mode 100644
index 000000000000..acb84543dbdc
--- /dev/null
+++ b/bindings/rust/gpiosim/README.md
@@ -0,0 +1,11 @@
+# Generated gpiosim 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/gpiosim/`
+2. run `cargo build --features generate`
+3. Copy the bindings 'cp ../target/debug/build/gpiosim-###/out/bindings.rs src/bindings.rs'
+4. Commit changes in `src/bindings.rs`
diff --git a/bindings/rust/gpiosim/src/bindings.rs b/bindings/rust/gpiosim/src/bindings.rs
new file mode 100644
index 000000000000..bac347f1aab9
--- /dev/null
+++ b/bindings/rust/gpiosim/src/bindings.rs
@@ -0,0 +1,180 @@
+/* automatically generated by rust-bindgen 0.59.2 */
+
+pub const true_: u32 = 1;
+pub const false_: u32 = 0;
+pub const __bool_true_false_are_defined: u32 = 1;
+pub type size_t = ::std::os::raw::c_ulong;
+pub type wchar_t = ::std::os::raw::c_int;
+#[repr(C)]
+#[repr(align(16))]
+#[derive(Debug, Copy, Clone)]
+pub struct max_align_t {
+    pub __clang_max_align_nonce1: ::std::os::raw::c_longlong,
+    pub __bindgen_padding_0: u64,
+    pub __clang_max_align_nonce2: u128,
+}
+#[test]
+fn bindgen_test_layout_max_align_t() {
+    assert_eq!(
+        ::std::mem::size_of::<max_align_t>(),
+        32usize,
+        concat!("Size of: ", stringify!(max_align_t))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<max_align_t>(),
+        16usize,
+        concat!("Alignment of ", stringify!(max_align_t))
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<max_align_t>())).__clang_max_align_nonce1 as *const _ as usize
+        },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(max_align_t),
+            "::",
+            stringify!(__clang_max_align_nonce1)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<max_align_t>())).__clang_max_align_nonce2 as *const _ as usize
+        },
+        16usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(max_align_t),
+            "::",
+            stringify!(__clang_max_align_nonce2)
+        )
+    );
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct gpiosim_ctx {
+    _unused: [u8; 0],
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct gpiosim_dev {
+    _unused: [u8; 0],
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct gpiosim_bank {
+    _unused: [u8; 0],
+}
+pub const GPIOSIM_VALUE_INACTIVE: ::std::os::raw::c_uint = 0;
+pub const GPIOSIM_VALUE_ACTIVE: ::std::os::raw::c_uint = 1;
+pub type _bindgen_ty_1 = ::std::os::raw::c_uint;
+pub const GPIOSIM_PULL_DOWN: ::std::os::raw::c_uint = 1;
+pub const GPIOSIM_PULL_UP: ::std::os::raw::c_uint = 2;
+pub type _bindgen_ty_2 = ::std::os::raw::c_uint;
+pub const GPIOSIM_HOG_DIR_INPUT: ::std::os::raw::c_uint = 1;
+pub const GPIOSIM_HOG_DIR_OUTPUT_HIGH: ::std::os::raw::c_uint = 2;
+pub const GPIOSIM_HOG_DIR_OUTPUT_LOW: ::std::os::raw::c_uint = 3;
+pub type _bindgen_ty_3 = ::std::os::raw::c_uint;
+extern "C" {
+    pub fn gpiosim_ctx_new() -> *mut gpiosim_ctx;
+}
+extern "C" {
+    pub fn gpiosim_ctx_ref(ctx: *mut gpiosim_ctx) -> *mut gpiosim_ctx;
+}
+extern "C" {
+    pub fn gpiosim_ctx_unref(ctx: *mut gpiosim_ctx);
+}
+extern "C" {
+    pub fn gpiosim_dev_new(ctx: *mut gpiosim_ctx) -> *mut gpiosim_dev;
+}
+extern "C" {
+    pub fn gpiosim_dev_ref(dev: *mut gpiosim_dev) -> *mut gpiosim_dev;
+}
+extern "C" {
+    pub fn gpiosim_dev_unref(dev: *mut gpiosim_dev);
+}
+extern "C" {
+    pub fn gpiosim_dev_get_ctx(dev: *mut gpiosim_dev) -> *mut gpiosim_ctx;
+}
+extern "C" {
+    pub fn gpiosim_dev_get_name(dev: *mut gpiosim_dev) -> *const ::std::os::raw::c_char;
+}
+extern "C" {
+    pub fn gpiosim_dev_enable(dev: *mut gpiosim_dev) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    pub fn gpiosim_dev_disable(dev: *mut gpiosim_dev) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    pub fn gpiosim_dev_is_live(dev: *mut gpiosim_dev) -> bool;
+}
+extern "C" {
+    pub fn gpiosim_bank_new(dev: *mut gpiosim_dev) -> *mut gpiosim_bank;
+}
+extern "C" {
+    pub fn gpiosim_bank_ref(bank: *mut gpiosim_bank) -> *mut gpiosim_bank;
+}
+extern "C" {
+    pub fn gpiosim_bank_unref(bank: *mut gpiosim_bank);
+}
+extern "C" {
+    pub fn gpiosim_bank_get_dev(bank: *mut gpiosim_bank) -> *mut gpiosim_dev;
+}
+extern "C" {
+    pub fn gpiosim_bank_get_chip_name(bank: *mut gpiosim_bank) -> *const ::std::os::raw::c_char;
+}
+extern "C" {
+    pub fn gpiosim_bank_get_dev_path(bank: *mut gpiosim_bank) -> *const ::std::os::raw::c_char;
+}
+extern "C" {
+    pub fn gpiosim_bank_set_label(
+        bank: *mut gpiosim_bank,
+        label: *const ::std::os::raw::c_char,
+    ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    pub fn gpiosim_bank_set_num_lines(
+        bank: *mut gpiosim_bank,
+        num_lines: size_t,
+    ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    pub fn gpiosim_bank_set_line_name(
+        bank: *mut gpiosim_bank,
+        offset: ::std::os::raw::c_uint,
+        name: *const ::std::os::raw::c_char,
+    ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    pub fn gpiosim_bank_hog_line(
+        bank: *mut gpiosim_bank,
+        offset: ::std::os::raw::c_uint,
+        name: *const ::std::os::raw::c_char,
+        direction: ::std::os::raw::c_int,
+    ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    pub fn gpiosim_bank_clear_hog(
+        bank: *mut gpiosim_bank,
+        offset: ::std::os::raw::c_uint,
+    ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    pub fn gpiosim_bank_get_value(
+        bank: *mut gpiosim_bank,
+        offset: ::std::os::raw::c_uint,
+    ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    pub fn gpiosim_bank_get_pull(
+        bank: *mut gpiosim_bank,
+        offset: ::std::os::raw::c_uint,
+    ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    pub fn gpiosim_bank_set_pull(
+        bank: *mut gpiosim_bank,
+        offset: ::std::os::raw::c_uint,
+        pull: ::std::os::raw::c_int,
+    ) -> ::std::os::raw::c_int;
+}
-- 
2.31.1.272.g89b43f80a514


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH V7 7/8] libgpiod: Add rust tests
  2022-10-14 10:47 [PATCH V7 0/8] libgpiod: Add Rust bindings Viresh Kumar
                   ` (5 preceding siblings ...)
  2022-10-14 10:47 ` [PATCH V7 6/8] gpiosim: Add pre generated rust bindings Viresh Kumar
@ 2022-10-14 10:47 ` Viresh Kumar
  2022-10-17 13:00   ` Kent Gibson
  2022-10-14 10:47 ` [PATCH V7 8/8] libgpiod: Integrate building of rust bindings with make Viresh Kumar
  2022-10-14 17:03 ` [PATCH V7 0/8] libgpiod: Add Rust bindings Miguel Ojeda
  8 siblings, 1 reply; 50+ messages in thread
From: Viresh Kumar @ 2022-10-14 10:47 UTC (permalink / raw)
  To: Linus Walleij, Bartosz Golaszewski
  Cc: Viresh Kumar, Vincent Guittot, linux-gpio, Kent Gibson,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

Add tests for the rust bindings, quite similar to the ones in cxx
bindings.

Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
---
 bindings/rust/libgpiod/Cargo.toml             |   3 +
 bindings/rust/libgpiod/tests/chip.rs          |  97 ++++
 bindings/rust/libgpiod/tests/common/config.rs | 134 +++++
 bindings/rust/libgpiod/tests/common/mod.rs    |  10 +
 bindings/rust/libgpiod/tests/edge_event.rs    | 296 ++++++++++
 bindings/rust/libgpiod/tests/info_event.rs    | 125 +++++
 bindings/rust/libgpiod/tests/line_config.rs   | 105 ++++
 bindings/rust/libgpiod/tests/line_info.rs     | 279 ++++++++++
 bindings/rust/libgpiod/tests/line_request.rs  | 519 ++++++++++++++++++
 bindings/rust/libgpiod/tests/line_settings.rs | 226 ++++++++
 .../rust/libgpiod/tests/request_config.rs     |  38 ++
 11 files changed, 1832 insertions(+)
 create mode 100644 bindings/rust/libgpiod/tests/chip.rs
 create mode 100644 bindings/rust/libgpiod/tests/common/config.rs
 create mode 100644 bindings/rust/libgpiod/tests/common/mod.rs
 create mode 100644 bindings/rust/libgpiod/tests/edge_event.rs
 create mode 100644 bindings/rust/libgpiod/tests/info_event.rs
 create mode 100644 bindings/rust/libgpiod/tests/line_config.rs
 create mode 100644 bindings/rust/libgpiod/tests/line_info.rs
 create mode 100644 bindings/rust/libgpiod/tests/line_request.rs
 create mode 100644 bindings/rust/libgpiod/tests/line_settings.rs
 create mode 100644 bindings/rust/libgpiod/tests/request_config.rs

diff --git a/bindings/rust/libgpiod/Cargo.toml b/bindings/rust/libgpiod/Cargo.toml
index f25242abb153..8abcaacdee67 100644
--- a/bindings/rust/libgpiod/Cargo.toml
+++ b/bindings/rust/libgpiod/Cargo.toml
@@ -11,3 +11,6 @@ libc = ">=0.2.39"
 libgpiod-sys = { path = "../libgpiod-sys" }
 thiserror = "1.0"
 vmm-sys-util = "=0.10.0"
+
+[dev-dependencies]
+gpiosim = { path = "../gpiosim" }
diff --git a/bindings/rust/libgpiod/tests/chip.rs b/bindings/rust/libgpiod/tests/chip.rs
new file mode 100644
index 000000000000..ce592331b5e8
--- /dev/null
+++ b/bindings/rust/libgpiod/tests/chip.rs
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
+//
+// Copyright 2022 Linaro Ltd. All Rights Reserved.
+//     Viresh Kumar <viresh.kumar@linaro.org>
+
+mod common;
+
+mod chip {
+    use libc::{ENODEV, ENOENT, ENOTTY};
+    use std::path::PathBuf;
+
+    use vmm_sys_util::errno::Error as Errno;
+
+    use gpiosim::Sim;
+    use libgpiod::{chip::Chip, Error as ChipError, OperationType};
+
+    mod open {
+        use super::*;
+
+        #[test]
+        fn nonexistent_file() {
+            assert_eq!(
+                Chip::open(&PathBuf::from("/dev/nonexistent")).unwrap_err(),
+                ChipError::OperationFailed(OperationType::ChipOpen, Errno::new(ENOENT))
+            );
+        }
+
+        #[test]
+        fn no_dev_file() {
+            assert_eq!(
+                Chip::open(&PathBuf::from("/tmp")).unwrap_err(),
+                ChipError::OperationFailed(OperationType::ChipOpen, Errno::new(ENOTTY))
+            );
+        }
+
+        #[test]
+        fn non_gpio_char_dev_file() {
+            assert_eq!(
+                Chip::open(&PathBuf::from("/dev/null")).unwrap_err(),
+                ChipError::OperationFailed(OperationType::ChipOpen, Errno::new(ENODEV))
+            );
+        }
+
+        #[test]
+        fn gpiosim_file() {
+            let sim = Sim::new(None, None, true).unwrap();
+            assert!(Chip::open(&sim.dev_path()).is_ok());
+        }
+    }
+
+    mod verify {
+        use super::*;
+        const NGPIO: u64 = 16;
+        const LABEL: &str = "foobar";
+
+        #[test]
+        fn basic_helpers() {
+            let sim = Sim::new(Some(NGPIO), Some(LABEL), true).unwrap();
+            let chip = Chip::open(&sim.dev_path()).unwrap();
+
+            assert_eq!(chip.label().unwrap(), LABEL);
+            assert_eq!(chip.name().unwrap(), sim.chip_name());
+            assert_eq!(chip.path().unwrap(), sim.dev_path().to_str().unwrap());
+            assert_eq!(chip.num_lines(), NGPIO as usize);
+            assert!(chip.fd().is_ok());
+        }
+
+        #[test]
+        fn find_line() {
+            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.line_offset_from_name("zero").unwrap(), 0);
+            assert_eq!(chip.line_offset_from_name("two").unwrap(), 2);
+            assert_eq!(chip.line_offset_from_name("three").unwrap(), 3);
+            assert_eq!(chip.line_offset_from_name("five").unwrap(), 5);
+
+            // Success with duplicate names, should return first entry
+            assert_eq!(chip.line_offset_from_name("ten").unwrap(), 10);
+
+            // Failure
+            assert_eq!(
+                chip.line_offset_from_name("nonexistent").unwrap_err(),
+                ChipError::OperationFailed(OperationType::ChipGetLine, Errno::new(ENOENT))
+            );
+        }
+    }
+}
diff --git a/bindings/rust/libgpiod/tests/common/config.rs b/bindings/rust/libgpiod/tests/common/config.rs
new file mode 100644
index 000000000000..ef4285067ebf
--- /dev/null
+++ b/bindings/rust/libgpiod/tests/common/config.rs
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
+//
+// Copyright 2022 Linaro Ltd. All Rights Reserved.
+//     Viresh Kumar <viresh.kumar@linaro.org>
+
+use std::sync::Arc;
+use std::time::Duration;
+
+use gpiosim::Sim;
+use libgpiod::{
+    chip::Chip, line, request, Bias, Direction, Drive, Edge, EventClock, Offset, Result,
+    SettingVal, Value,
+};
+
+pub(crate) struct TestConfig {
+    sim: Arc<Sim>,
+    chip: Option<Chip>,
+    request: Option<line::Request>,
+    rconfig: request::Config,
+    lconfig: line::Config,
+    lsettings: Option<line::Settings>,
+}
+
+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: request::Config::new().unwrap(),
+            lconfig: line::Config::new().unwrap(),
+            lsettings: Some(line::Settings::new().unwrap()),
+        })
+    }
+
+    pub(crate) fn set_pull(&self, offsets: &[Offset], pulls: &[u32]) {
+        for i in 0..pulls.len() {
+            self.sim.set_pull(offsets[i], pulls[i] as i32).unwrap();
+        }
+    }
+
+    pub(crate) fn rconfig_set_consumer(&self, consumer: &str) {
+        self.rconfig.set_consumer(consumer);
+    }
+
+    pub(crate) fn lconfig_val(&mut self, dir: Option<Direction>, val: Option<Value>) {
+        let mut settings = Vec::new();
+
+        if let Some(dir) = dir {
+            settings.push(SettingVal::Direction(dir));
+        }
+
+        if let Some(val) = val {
+            settings.push(SettingVal::OutputValue(val));
+        }
+
+        if !settings.is_empty() {
+            self.lsettings().set_prop(&settings).unwrap();
+        }
+    }
+
+    pub(crate) fn lconfig_bias(&mut self, dir: Direction, bias: Option<Bias>) {
+        let settings = vec![SettingVal::Direction(dir), SettingVal::Bias(bias)];
+        self.lsettings().set_prop(&settings).unwrap();
+    }
+
+    pub(crate) fn lconfig_clock(&mut self, clock: EventClock) {
+        let settings = vec![SettingVal::EventClock(clock)];
+        self.lsettings().set_prop(&settings).unwrap();
+    }
+
+    pub(crate) fn lconfig_debounce(&mut self, duration: Duration) {
+        let settings = vec![
+            SettingVal::Direction(Direction::Input),
+            SettingVal::DebouncePeriod(duration),
+        ];
+        self.lsettings().set_prop(&settings).unwrap();
+    }
+
+    pub(crate) fn lconfig_drive(&mut self, dir: Direction, drive: Drive) {
+        let settings = vec![SettingVal::Direction(dir), SettingVal::Drive(drive)];
+        self.lsettings().set_prop(&settings).unwrap();
+    }
+
+    pub(crate) fn lconfig_edge(&mut self, dir: Option<Direction>, edge: Option<Edge>) {
+        let mut settings = Vec::new();
+
+        if let Some(dir) = dir {
+            settings.push(SettingVal::Direction(dir));
+        }
+
+        settings.push(SettingVal::EdgeDetection(edge));
+        self.lsettings().set_prop(&settings).unwrap();
+    }
+
+    pub(crate) fn lconfig_add_settings(&mut self, offsets: &[Offset]) {
+        self.lconfig
+            .add_line_settings(offsets, self.lsettings.take().unwrap())
+            .unwrap()
+    }
+
+    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 lsettings(&mut self) -> &mut line::Settings {
+        self.lsettings.as_mut().unwrap()
+    }
+
+    pub(crate) fn request(&self) -> &line::Request {
+        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;
+    }
+}
diff --git a/bindings/rust/libgpiod/tests/common/mod.rs b/bindings/rust/libgpiod/tests/common/mod.rs
new file mode 100644
index 000000000000..8904f7121b4c
--- /dev/null
+++ b/bindings/rust/libgpiod/tests/common/mod.rs
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
+//
+// Copyright 2022 Linaro Ltd. All Rights Reserved.
+//     Viresh Kumar <viresh.kumar@linaro.org>
+
+#[allow(dead_code)]
+mod config;
+
+#[allow(unused_imports)]
+pub(crate) use config::*;
diff --git a/bindings/rust/libgpiod/tests/edge_event.rs b/bindings/rust/libgpiod/tests/edge_event.rs
new file mode 100644
index 000000000000..7dc4b489103b
--- /dev/null
+++ b/bindings/rust/libgpiod/tests/edge_event.rs
@@ -0,0 +1,296 @@
+// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
+//
+// Copyright 2022 Linaro Ltd. All Rights Reserved.
+//     Viresh Kumar <viresh.kumar@linaro.org>
+
+mod common;
+
+mod edge_event {
+    use std::sync::Arc;
+    use std::thread::{sleep, spawn};
+    use std::time::Duration;
+
+    use crate::common::*;
+    use gpiosim::Sim;
+    use gpiosim::{GPIOSIM_PULL_DOWN, GPIOSIM_PULL_UP};
+    use libgpiod::{edge, Edge, EdgeKind, Offset};
+
+    const NGPIO: u64 = 8;
+
+    mod buffer_capacity {
+        use super::*;
+
+        #[test]
+        fn default_capacity() {
+            assert_eq!(edge::event::Buffer::new(0).unwrap().capacity(), 64);
+        }
+
+        #[test]
+        fn user_defined_capacity() {
+            assert_eq!(edge::event::Buffer::new(123).unwrap().capacity(), 123);
+        }
+
+        #[test]
+        fn max_capacity() {
+            assert_eq!(edge::event::Buffer::new(1024 * 2).unwrap().capacity(), 1024);
+        }
+    }
+
+    mod trigger {
+        use super::*;
+
+        // Helpers to generate events
+        fn trigger_falling_and_rising_edge(sim: Arc<Sim>, offset: Offset) {
+            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: [Offset; 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: Offset) {
+            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: Offset = 2;
+            let mut buf = edge::event::Buffer::new(0).unwrap();
+            let mut config = TestConfig::new(NGPIO).unwrap();
+            config.lconfig_edge(None, Some(Edge::Both));
+            config.lconfig_add_settings(&[GPIO]);
+            config.request_lines().unwrap();
+
+            // Generate events
+            trigger_falling_and_rising_edge(config.sim(), GPIO);
+
+            // Rising event
+            assert!(config
+                .request()
+                .wait_edge_event(Some(Duration::from_secs(1)))
+                .unwrap());
+
+            assert_eq!(config.request().read_edge_events(&mut buf).unwrap(), 1);
+            assert_eq!(buf.len(), 1);
+
+            let event = buf.event(0).unwrap();
+            let ts_rising = event.timestamp();
+            assert_eq!(event.event_type().unwrap(), EdgeKind::Rising);
+            assert_eq!(event.line_offset(), GPIO);
+            drop(event);
+
+            // Falling event
+            assert!(config
+                .request()
+                .wait_edge_event(Some(Duration::from_secs(1)))
+                .unwrap());
+
+            assert_eq!(config.request().read_edge_events(&mut buf).unwrap(), 1);
+            assert_eq!(buf.len(), 1);
+
+            let event = buf.event(0).unwrap();
+            let ts_falling = event.timestamp();
+            assert_eq!(event.event_type().unwrap(), EdgeKind::Falling);
+            assert_eq!(event.line_offset(), GPIO);
+
+            // No events available
+            assert!(!config
+                .request()
+                .wait_edge_event(Some(Duration::from_millis(100)))
+                .unwrap());
+
+            assert!(ts_falling > ts_rising);
+        }
+
+        #[test]
+        fn rising_edge() {
+            const GPIO: Offset = 6;
+            let mut buf = edge::event::Buffer::new(0).unwrap();
+            let mut config = TestConfig::new(NGPIO).unwrap();
+            config.lconfig_edge(None, Some(Edge::Rising));
+            config.lconfig_add_settings(&[GPIO]);
+            config.request_lines().unwrap();
+
+            // Generate events
+            trigger_falling_and_rising_edge(config.sim(), GPIO);
+
+            // Rising event
+            assert!(config
+                .request()
+                .wait_edge_event(Some(Duration::from_secs(1)))
+                .unwrap());
+
+            assert_eq!(config.request().read_edge_events(&mut buf).unwrap(), 1);
+            assert_eq!(buf.len(), 1);
+
+            let event = buf.event(0).unwrap();
+            assert_eq!(event.event_type().unwrap(), EdgeKind::Rising);
+            assert_eq!(event.line_offset(), GPIO);
+
+            // No events available
+            assert!(!config
+                .request()
+                .wait_edge_event(Some(Duration::from_millis(100)))
+                .unwrap());
+        }
+
+        #[test]
+        fn falling_edge() {
+            const GPIO: Offset = 7;
+            let mut buf = edge::event::Buffer::new(0).unwrap();
+            let mut config = TestConfig::new(NGPIO).unwrap();
+            config.lconfig_edge(None, Some(Edge::Falling));
+            config.lconfig_add_settings(&[GPIO]);
+            config.request_lines().unwrap();
+
+            // Generate events
+            trigger_falling_and_rising_edge(config.sim(), GPIO);
+
+            // Falling event
+            assert!(config
+                .request()
+                .wait_edge_event(Some(Duration::from_secs(1)))
+                .unwrap());
+
+            assert_eq!(config.request().read_edge_events(&mut buf).unwrap(), 1);
+            assert_eq!(buf.len(), 1);
+
+            let event = buf.event(0).unwrap();
+            assert_eq!(event.event_type().unwrap(), EdgeKind::Falling);
+            assert_eq!(event.line_offset(), GPIO);
+
+            // No events available
+            assert!(!config
+                .request()
+                .wait_edge_event(Some(Duration::from_millis(100)))
+                .unwrap());
+        }
+
+        #[test]
+        fn edge_sequence() {
+            const GPIO: [u32; 2] = [0, 1];
+            let mut config = TestConfig::new(NGPIO).unwrap();
+            config.lconfig_edge(None, Some(Edge::Both));
+            config.lconfig_add_settings(&GPIO);
+            config.request_lines().unwrap();
+
+            // Generate events
+            trigger_rising_edge_events_on_two_offsets(config.sim(), GPIO);
+
+            // Rising event GPIO 0
+            let mut buf = edge::event::Buffer::new(0).unwrap();
+            assert!(config
+                .request()
+                .wait_edge_event(Some(Duration::from_secs(1)))
+                .unwrap());
+
+            assert_eq!(config.request().read_edge_events(&mut buf).unwrap(), 1);
+            assert_eq!(buf.len(), 1);
+
+            let event = buf.event(0).unwrap();
+            assert_eq!(event.event_type().unwrap(), EdgeKind::Rising);
+            assert_eq!(event.line_offset(), GPIO[0]);
+            assert_eq!(event.global_seqno(), 1);
+            assert_eq!(event.line_seqno(), 1);
+            drop(event);
+
+            // Rising event GPIO 1
+            assert!(config
+                .request()
+                .wait_edge_event(Some(Duration::from_secs(1)))
+                .unwrap());
+
+            assert_eq!(config.request().read_edge_events(&mut buf).unwrap(), 1);
+            assert_eq!(buf.len(), 1);
+
+            let event = buf.event(0).unwrap();
+            assert_eq!(event.event_type().unwrap(), EdgeKind::Rising);
+            assert_eq!(event.line_offset(), GPIO[1]);
+            assert_eq!(event.global_seqno(), 2);
+            assert_eq!(event.line_seqno(), 1);
+
+            // No events available
+            assert!(!config
+                .request()
+                .wait_edge_event(Some(Duration::from_millis(100)))
+                .unwrap());
+        }
+
+        #[test]
+        fn multiple_events() {
+            const GPIO: Offset = 1;
+            let mut buf = edge::event::Buffer::new(0).unwrap();
+            let mut config = TestConfig::new(NGPIO).unwrap();
+            config.lconfig_edge(None, Some(Edge::Both));
+            config.lconfig_add_settings(&[GPIO]);
+            config.request_lines().unwrap();
+
+            // Generate events
+            trigger_multiple_events(config.sim(), GPIO);
+
+            // Read multiple events
+            assert!(config
+                .request()
+                .wait_edge_event(Some(Duration::from_secs(1)))
+                .unwrap());
+
+            assert_eq!(config.request().read_edge_events(&mut buf).unwrap(), 3);
+            assert_eq!(buf.len(), 3);
+
+            let mut global_seqno = 1;
+            let mut line_seqno = 1;
+
+            // Verify sequence number of events
+            for i in 0..3 {
+                let event = buf.event(i).unwrap();
+                assert_eq!(event.line_offset(), GPIO);
+                assert_eq!(event.global_seqno(), global_seqno);
+                assert_eq!(event.line_seqno(), line_seqno);
+
+                global_seqno += 1;
+                line_seqno += 1;
+            }
+        }
+
+        #[test]
+        fn over_capacity() {
+            const GPIO: Offset = 2;
+            let mut buf = edge::event::Buffer::new(2).unwrap();
+            let mut config = TestConfig::new(NGPIO).unwrap();
+            config.lconfig_edge(None, Some(Edge::Both));
+            config.lconfig_add_settings(&[GPIO]);
+            config.request_lines().unwrap();
+
+            // Generate events
+            trigger_multiple_events(config.sim(), GPIO);
+
+            // Read multiple events
+            assert!(config
+                .request()
+                .wait_edge_event(Some(Duration::from_secs(1)))
+                .unwrap());
+
+            assert_eq!(config.request().read_edge_events(&mut buf).unwrap(), 2);
+            assert_eq!(buf.len(), 2);
+        }
+    }
+}
diff --git a/bindings/rust/libgpiod/tests/info_event.rs b/bindings/rust/libgpiod/tests/info_event.rs
new file mode 100644
index 000000000000..8c342992d74e
--- /dev/null
+++ b/bindings/rust/libgpiod/tests/info_event.rs
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
+//
+// Copyright 2022 Linaro Ltd. All Rights Reserved.
+//     Viresh Kumar <viresh.kumar@linaro.org>
+
+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 Errno;
+
+    use gpiosim::Sim;
+    use libgpiod::{
+        chip::Chip, line, request, Direction, Error as ChipError, InfoChangeKind, Offset,
+        OperationType, SettingVal,
+    };
+
+    fn request_reconfigure_line(chip: Arc<Chip>) {
+        spawn(move || {
+            sleep(Duration::from_millis(10));
+
+            let lconfig1 = line::Config::new().unwrap();
+            let lsettings = line::Settings::new().unwrap();
+            lconfig1.add_line_settings(&[7], lsettings).unwrap();
+            let rconfig = request::Config::new().unwrap();
+
+            let request = chip.request_lines(&rconfig, &lconfig1).unwrap();
+
+            sleep(Duration::from_millis(10));
+
+            let lconfig2 = line::Config::new().unwrap();
+            let mut lsettings = line::Settings::new().unwrap();
+            lsettings
+                .set_prop(&[SettingVal::Direction(Direction::Output)])
+                .unwrap();
+            lconfig2.add_line_settings(&[7], lsettings).unwrap();
+
+            request.reconfigure_lines(&lconfig2).unwrap();
+
+            sleep(Duration::from_millis(10));
+        });
+    }
+
+    mod watch {
+        use super::*;
+        const NGPIO: u64 = 8;
+        const GPIO: Offset = 7;
+
+        #[test]
+        fn line_info() {
+            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(OperationType::ChipWatchLineInfo, Errno::new(EINVAL))
+            );
+
+            let info = chip.watch_line_info(GPIO).unwrap();
+            assert_eq!(info.offset(), GPIO);
+
+            // No events available
+            assert!(!chip
+                .wait_info_event(Some(Duration::from_millis(100)))
+                .unwrap());
+        }
+
+        #[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.direction().unwrap(), Direction::Input);
+
+            // Generate events
+            request_reconfigure_line(chip.clone());
+
+            // Line requested event
+            assert!(chip.wait_info_event(Some(Duration::from_secs(1))).unwrap());
+            let event = chip.read_info_event().unwrap();
+            let ts_req = event.timestamp();
+
+            assert_eq!(event.event_type().unwrap(), InfoChangeKind::LineRequested);
+            assert_eq!(
+                event.line_info().unwrap().direction().unwrap(),
+                Direction::Input
+            );
+
+            // Line changed event
+            assert!(chip.wait_info_event(Some(Duration::from_secs(1))).unwrap());
+            let event = chip.read_info_event().unwrap();
+            let ts_rec = event.timestamp();
+
+            assert_eq!(
+                event.event_type().unwrap(),
+                InfoChangeKind::LineConfigChanged
+            );
+            assert_eq!(
+                event.line_info().unwrap().direction().unwrap(),
+                Direction::Output
+            );
+
+            // Line released event
+            assert!(chip.wait_info_event(Some(Duration::from_secs(1))).unwrap());
+            let event = chip.read_info_event().unwrap();
+            let ts_rel = event.timestamp();
+
+            assert_eq!(event.event_type().unwrap(), InfoChangeKind::LineReleased);
+
+            // No events available
+            assert!(!chip
+                .wait_info_event(Some(Duration::from_millis(100)))
+                .unwrap());
+
+            // Check timestamps are really monotonic.
+            assert!(ts_rel > ts_rec);
+            assert!(ts_rec > ts_req);
+        }
+    }
+}
diff --git a/bindings/rust/libgpiod/tests/line_config.rs b/bindings/rust/libgpiod/tests/line_config.rs
new file mode 100644
index 000000000000..e65cd1d23f9b
--- /dev/null
+++ b/bindings/rust/libgpiod/tests/line_config.rs
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
+//
+// Copyright 2022 Linaro Ltd. All Rights Reserved.
+//     Viresh Kumar <viresh.kumar@linaro.org>
+
+mod common;
+
+mod line_config {
+    use libgpiod::{
+        line, Bias, Direction, Drive, Edge, EventClock, SettingKind, SettingVal, Value,
+    };
+
+    #[test]
+    fn settings() {
+        let mut lsettings1 = line::Settings::new().unwrap();
+        lsettings1
+            .set_prop(&[SettingVal::Direction(Direction::Input)])
+            .unwrap();
+        lsettings1
+            .set_prop(&[SettingVal::EdgeDetection(Some(Edge::Both))])
+            .unwrap();
+        lsettings1
+            .set_prop(&[SettingVal::Bias(Some(Bias::PullDown))])
+            .unwrap();
+        lsettings1
+            .set_prop(&[SettingVal::Drive(Drive::PushPull)])
+            .unwrap();
+
+        let mut lsettings2 = line::Settings::new().unwrap();
+        lsettings2
+            .set_prop(&[SettingVal::Direction(Direction::Output)])
+            .unwrap();
+        lsettings2.set_prop(&[SettingVal::ActiveLow(true)]).unwrap();
+        lsettings2
+            .set_prop(&[SettingVal::EventClock(EventClock::Realtime)])
+            .unwrap();
+        lsettings2
+            .set_prop(&[SettingVal::OutputValue(Value::Active)])
+            .unwrap();
+
+        // Add settings for multiple lines
+        let lconfig = line::Config::new().unwrap();
+        lconfig.add_line_settings(&[0, 1, 2], lsettings1).unwrap();
+        lconfig.add_line_settings(&[4, 5], lsettings2).unwrap();
+
+        // Retrieve settings
+        let lsettings = lconfig.line_settings(1).unwrap();
+        assert_eq!(
+            lsettings.prop(SettingKind::Direction).unwrap(),
+            SettingVal::Direction(Direction::Input)
+        );
+        assert_eq!(
+            lsettings.prop(SettingKind::EdgeDetection).unwrap(),
+            SettingVal::EdgeDetection(Some(Edge::Both))
+        );
+        assert_eq!(
+            lsettings.prop(SettingKind::Bias).unwrap(),
+            SettingVal::Bias(Some(Bias::PullDown))
+        );
+        assert_eq!(
+            lsettings.prop(SettingKind::Drive).unwrap(),
+            SettingVal::Drive(Drive::PushPull)
+        );
+
+        let lsettings = lconfig.line_settings(5).unwrap();
+        assert_eq!(
+            lsettings.prop(SettingKind::Direction).unwrap(),
+            SettingVal::Direction(Direction::Output)
+        );
+        assert_eq!(
+            lsettings.prop(SettingKind::ActiveLow).unwrap(),
+            SettingVal::ActiveLow(true)
+        );
+        assert_eq!(
+            lsettings.prop(SettingKind::EventClock).unwrap(),
+            SettingVal::EventClock(EventClock::Realtime)
+        );
+        assert_eq!(
+            lsettings.prop(SettingKind::OutputValue).unwrap(),
+            SettingVal::OutputValue(Value::Active)
+        );
+    }
+
+    #[test]
+    fn offsets() {
+        let mut lsettings1 = line::Settings::new().unwrap();
+        lsettings1
+            .set_prop(&[SettingVal::Direction(Direction::Input)])
+            .unwrap();
+
+        let mut lsettings2 = line::Settings::new().unwrap();
+        lsettings2
+            .set_prop(&[SettingVal::EventClock(EventClock::Realtime)])
+            .unwrap();
+
+        // Add settings for multiple lines
+        let lconfig = line::Config::new().unwrap();
+        lconfig.add_line_settings(&[0, 1, 2], lsettings1).unwrap();
+        lconfig.add_line_settings(&[4, 5], lsettings2).unwrap();
+
+        // Verify offsets
+        let offsets = lconfig.offsets().unwrap();
+        assert_eq!(offsets, [0, 1, 2, 4, 5]);
+    }
+}
diff --git a/bindings/rust/libgpiod/tests/line_info.rs b/bindings/rust/libgpiod/tests/line_info.rs
new file mode 100644
index 000000000000..22edeb4da263
--- /dev/null
+++ b/bindings/rust/libgpiod/tests/line_info.rs
@@ -0,0 +1,279 @@
+// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
+//
+// Copyright 2022 Linaro Ltd. All Rights Reserved.
+//     Viresh Kumar <viresh.kumar@linaro.org>
+
+mod common;
+
+mod line_info {
+    use libc::EINVAL;
+    use std::time::Duration;
+
+    use vmm_sys_util::errno::Error as Errno;
+
+    use crate::common::*;
+    use gpiosim::Sim;
+    use gpiosim::{GPIOSIM_HOG_DIR_INPUT, GPIOSIM_HOG_DIR_OUTPUT_HIGH, GPIOSIM_HOG_DIR_OUTPUT_LOW};
+    use libgpiod::{
+        chip::Chip, Bias, Direction, Drive, Edge, Error as ChipError, EventClock, OperationType,
+    };
+
+    const NGPIO: u64 = 8;
+
+    mod properties {
+        use super::*;
+
+        #[test]
+        fn default() {
+            let sim = Sim::new(Some(NGPIO), None, false).unwrap();
+            sim.set_line_name(4, "four").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();
+
+            let info4 = chip.line_info(4).unwrap();
+            assert_eq!(info4.offset(), 4);
+            assert_eq!(info4.name().unwrap(), "four");
+            assert!(info4.is_used());
+            assert_eq!(info4.consumer().unwrap(), "hog4");
+            assert_eq!(info4.direction().unwrap(), Direction::Output);
+            assert!(!info4.is_active_low());
+            assert_eq!(info4.bias().unwrap(), None);
+            assert_eq!(info4.drive().unwrap(), Drive::PushPull);
+            assert_eq!(info4.edge_detection().unwrap(), None);
+            assert_eq!(info4.event_clock().unwrap(), EventClock::Monotonic);
+            assert!(!info4.is_debounced());
+            assert_eq!(info4.debounce_period(), Duration::from_millis(0));
+
+            assert_eq!(
+                chip.line_info(NGPIO as u32).unwrap_err(),
+                ChipError::OperationFailed(OperationType::ChipGetLineInfo, Errno::new(EINVAL))
+            );
+        }
+
+        #[test]
+        fn name_and_offset() {
+            let sim = Sim::new(Some(NGPIO), None, false).unwrap();
+
+            // Line 0 has no name
+            for i in 1..NGPIO {
+                sim.set_line_name(i as u32, &i.to_string()).unwrap();
+            }
+            sim.enable().unwrap();
+
+            let chip = Chip::open(&sim.dev_path()).unwrap();
+            let info = chip.line_info(0).unwrap();
+
+            assert_eq!(info.offset(), 0);
+            assert_eq!(
+                info.name().unwrap_err(),
+                ChipError::NullString("GPIO line's name")
+            );
+
+            for i in 1..NGPIO {
+                let info = chip.line_info(i as u32).unwrap();
+
+                assert_eq!(info.offset(), i as u32);
+                assert_eq!(info.name().unwrap(), &i.to_string());
+            }
+        }
+
+        #[test]
+        fn is_used() {
+            let sim = Sim::new(Some(NGPIO), None, false).unwrap();
+            sim.hog_line(0, "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(0).unwrap();
+            assert!(info.is_used());
+
+            let info = chip.line_info(1).unwrap();
+            assert!(!info.is_used());
+        }
+
+        #[test]
+        fn consumer() {
+            let sim = Sim::new(Some(NGPIO), None, false).unwrap();
+            sim.hog_line(0, "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(0).unwrap();
+            assert_eq!(info.consumer().unwrap(), "hog");
+
+            let info = chip.line_info(1).unwrap();
+            assert_eq!(
+                info.consumer().unwrap_err(),
+                ChipError::NullString("GPIO line's consumer name")
+            );
+        }
+
+        #[test]
+        fn direction() {
+            let sim = Sim::new(Some(NGPIO), None, false).unwrap();
+            sim.hog_line(0, "hog", GPIOSIM_HOG_DIR_INPUT as i32)
+                .unwrap();
+            sim.hog_line(1, "hog", GPIOSIM_HOG_DIR_OUTPUT_HIGH as i32)
+                .unwrap();
+            sim.hog_line(2, "hog", GPIOSIM_HOG_DIR_OUTPUT_LOW as i32)
+                .unwrap();
+            sim.enable().unwrap();
+
+            let chip = Chip::open(&sim.dev_path()).unwrap();
+
+            let info = chip.line_info(0).unwrap();
+            assert_eq!(info.direction().unwrap(), Direction::Input);
+
+            let info = chip.line_info(1).unwrap();
+            assert_eq!(info.direction().unwrap(), Direction::Output);
+
+            let info = chip.line_info(2).unwrap();
+            assert_eq!(info.direction().unwrap(), Direction::Output);
+        }
+
+        #[test]
+        fn bias() {
+            let mut config = TestConfig::new(NGPIO).unwrap();
+            config.lconfig_add_settings(&[0]);
+            config.request_lines().unwrap();
+            let info = config.chip().line_info(0).unwrap();
+            assert_eq!(info.bias().unwrap(), None);
+
+            let mut config = TestConfig::new(NGPIO).unwrap();
+            config.lconfig_bias(Direction::Input, Some(Bias::PullUp));
+            config.lconfig_add_settings(&[0]);
+            config.request_lines().unwrap();
+            let info = config.chip().line_info(0).unwrap();
+            assert_eq!(info.bias().unwrap(), Some(Bias::PullUp));
+
+            let mut config = TestConfig::new(NGPIO).unwrap();
+            config.lconfig_bias(Direction::Input, Some(Bias::PullDown));
+            config.lconfig_add_settings(&[0]);
+            config.request_lines().unwrap();
+            let info = config.chip().line_info(0).unwrap();
+            assert_eq!(info.bias().unwrap(), Some(Bias::PullDown));
+
+            let mut config = TestConfig::new(NGPIO).unwrap();
+            config.lconfig_bias(Direction::Input, Some(Bias::Disabled));
+            config.lconfig_add_settings(&[0]);
+            config.request_lines().unwrap();
+            let info = config.chip().line_info(0).unwrap();
+            assert_eq!(info.bias().unwrap(), Some(Bias::Disabled));
+        }
+
+        #[test]
+        fn drive() {
+            let mut config = TestConfig::new(NGPIO).unwrap();
+            config.lconfig_add_settings(&[0]);
+            config.request_lines().unwrap();
+            let info = config.chip().line_info(0).unwrap();
+            assert_eq!(info.drive().unwrap(), Drive::PushPull);
+
+            let mut config = TestConfig::new(NGPIO).unwrap();
+            config.lconfig_drive(Direction::Input, Drive::PushPull);
+            config.lconfig_add_settings(&[0]);
+            config.request_lines().unwrap();
+            let info = config.chip().line_info(0).unwrap();
+            assert_eq!(info.drive().unwrap(), Drive::PushPull);
+
+            let mut config = TestConfig::new(NGPIO).unwrap();
+            config.lconfig_drive(Direction::Output, Drive::OpenDrain);
+            config.lconfig_add_settings(&[0]);
+            config.request_lines().unwrap();
+            let info = config.chip().line_info(0).unwrap();
+            assert_eq!(info.drive().unwrap(), Drive::OpenDrain);
+
+            let mut config = TestConfig::new(NGPIO).unwrap();
+            config.lconfig_drive(Direction::Output, Drive::OpenSource);
+            config.lconfig_add_settings(&[0]);
+            config.request_lines().unwrap();
+            let info = config.chip().line_info(0).unwrap();
+            assert_eq!(info.drive().unwrap(), Drive::OpenSource);
+        }
+
+        #[test]
+        fn edge() {
+            let mut config = TestConfig::new(NGPIO).unwrap();
+            config.lconfig_add_settings(&[0]);
+            config.request_lines().unwrap();
+            let info = config.chip().line_info(0).unwrap();
+            assert_eq!(info.edge_detection().unwrap(), None);
+
+            let mut config = TestConfig::new(NGPIO).unwrap();
+            config.lconfig_edge(Some(Direction::Input), Some(Edge::Both));
+            config.lconfig_add_settings(&[0]);
+            config.request_lines().unwrap();
+            let info = config.chip().line_info(0).unwrap();
+            assert_eq!(info.edge_detection().unwrap(), Some(Edge::Both));
+
+            let mut config = TestConfig::new(NGPIO).unwrap();
+            config.lconfig_edge(Some(Direction::Input), Some(Edge::Rising));
+            config.lconfig_add_settings(&[0]);
+            config.request_lines().unwrap();
+            let info = config.chip().line_info(0).unwrap();
+            assert_eq!(info.edge_detection().unwrap(), Some(Edge::Rising));
+
+            let mut config = TestConfig::new(NGPIO).unwrap();
+            config.lconfig_edge(Some(Direction::Input), Some(Edge::Falling));
+            config.lconfig_add_settings(&[0]);
+            config.request_lines().unwrap();
+            let info = config.chip().line_info(0).unwrap();
+            assert_eq!(info.edge_detection().unwrap(), Some(Edge::Falling));
+        }
+
+        #[test]
+        fn clock() {
+            let mut config = TestConfig::new(NGPIO).unwrap();
+            config.lconfig_add_settings(&[0]);
+            config.request_lines().unwrap();
+            let info = config.chip().line_info(0).unwrap();
+            assert_eq!(info.event_clock().unwrap(), EventClock::Monotonic);
+
+            let mut config = TestConfig::new(NGPIO).unwrap();
+            config.lconfig_clock(EventClock::Monotonic);
+            config.lconfig_add_settings(&[0]);
+            config.request_lines().unwrap();
+            let info = config.chip().line_info(0).unwrap();
+            assert_eq!(info.event_clock().unwrap(), EventClock::Monotonic);
+
+            let mut config = TestConfig::new(NGPIO).unwrap();
+            config.lconfig_clock(EventClock::Realtime);
+            config.lconfig_add_settings(&[0]);
+            config.request_lines().unwrap();
+            let info = config.chip().line_info(0).unwrap();
+            assert_eq!(info.event_clock().unwrap(), EventClock::Realtime);
+
+            let mut config = TestConfig::new(NGPIO).unwrap();
+            config.lconfig_clock(EventClock::HTE);
+            config.lconfig_add_settings(&[0]);
+            config.request_lines().unwrap();
+            let info = config.chip().line_info(0).unwrap();
+            assert_eq!(info.event_clock().unwrap(), EventClock::HTE);
+        }
+
+        #[test]
+        fn debounce() {
+            let mut config = TestConfig::new(NGPIO).unwrap();
+            config.lconfig_add_settings(&[0]);
+            config.request_lines().unwrap();
+            let info = config.chip().line_info(0).unwrap();
+            assert!(!info.is_debounced());
+            assert_eq!(info.debounce_period(), Duration::from_millis(0));
+
+            let mut config = TestConfig::new(NGPIO).unwrap();
+            config.lconfig_debounce(Duration::from_millis(100));
+            config.lconfig_add_settings(&[0]);
+            config.request_lines().unwrap();
+            let info = config.chip().line_info(0).unwrap();
+            assert!(info.is_debounced());
+            assert_eq!(info.debounce_period(), Duration::from_millis(100));
+        }
+    }
+}
diff --git a/bindings/rust/libgpiod/tests/line_request.rs b/bindings/rust/libgpiod/tests/line_request.rs
new file mode 100644
index 000000000000..e933fe7cb74a
--- /dev/null
+++ b/bindings/rust/libgpiod/tests/line_request.rs
@@ -0,0 +1,519 @@
+// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
+//
+// Copyright 2022 Linaro Ltd. All Rights Reserved.
+//     Viresh Kumar <viresh.kumar@linaro.org>
+
+mod common;
+
+mod line_request {
+    use libc::{E2BIG, EINVAL};
+    use std::time::Duration;
+
+    use vmm_sys_util::errno::Error as Errno;
+
+    use crate::common::*;
+    use gpiosim::{
+        GPIOSIM_PULL_DOWN, GPIOSIM_PULL_UP, GPIOSIM_VALUE_ACTIVE, GPIOSIM_VALUE_INACTIVE,
+    };
+    use libgpiod::{
+        line, Bias, Direction, Drive, Edge, Error as ChipError, EventClock, Offset, OperationType,
+        SettingVal, Value, ValueMap,
+    };
+
+    const NGPIO: u64 = 8;
+
+    mod invalid_arguments {
+        use super::*;
+
+        #[test]
+        fn no_offsets() {
+            let mut config = TestConfig::new(NGPIO).unwrap();
+
+            assert_eq!(
+                config.request_lines().unwrap_err(),
+                ChipError::OperationFailed(OperationType::LineRequest, Errno::new(EINVAL))
+            );
+        }
+
+        #[test]
+        fn out_of_bound_offsets() {
+            let mut config = TestConfig::new(NGPIO).unwrap();
+            config.lconfig_add_settings(&[2, 0, 8, 4]);
+
+            assert_eq!(
+                config.request_lines().unwrap_err(),
+                ChipError::OperationFailed(OperationType::LineRequest, Errno::new(EINVAL))
+            );
+        }
+
+        #[test]
+        fn dir_out_edge_failure() {
+            let mut config = TestConfig::new(NGPIO).unwrap();
+            config.lconfig_edge(Some(Direction::Output), Some(Edge::Both));
+            config.lconfig_add_settings(&[0]);
+
+            assert_eq!(
+                config.request_lines().unwrap_err(),
+                ChipError::OperationFailed(OperationType::LineRequest, Errno::new(EINVAL))
+            );
+        }
+    }
+
+    mod verify {
+        use super::*;
+
+        #[test]
+        fn custom_consumer() {
+            const GPIO: Offset = 2;
+            const CONSUMER: &str = "foobar";
+            let mut config = TestConfig::new(NGPIO).unwrap();
+            config.rconfig_set_consumer(CONSUMER);
+            config.lconfig_add_settings(&[GPIO]);
+            config.request_lines().unwrap();
+
+            let info = config.chip().line_info(GPIO).unwrap();
+
+            assert!(info.is_used());
+            assert_eq!(info.consumer().unwrap(), CONSUMER);
+        }
+
+        #[test]
+        fn empty_consumer() {
+            const GPIO: Offset = 2;
+            let mut config = TestConfig::new(NGPIO).unwrap();
+            config.lconfig_add_settings(&[GPIO]);
+            config.request_lines().unwrap();
+
+            let info = config.chip().line_info(GPIO).unwrap();
+
+            assert!(info.is_used());
+            assert_eq!(info.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.lconfig_val(Some(Direction::Input), None);
+            config.lconfig_add_settings(&offsets);
+            config.request_lines().unwrap();
+
+            let request = config.request();
+
+            // Single values read properly
+            assert_eq!(request.value(7).unwrap(), Value::Active);
+
+            // Values read properly
+            let map = request.values().unwrap();
+            for i in 0..offsets.len() {
+                assert_eq!(
+                    *map.get(offsets[i].into()).unwrap(),
+                    match pulls[i] {
+                        GPIOSIM_PULL_DOWN => Value::InActive,
+                        _ => Value::Active,
+                    }
+                );
+            }
+
+            // Subset of values read properly
+            let map = request.values_subset(&[2, 0, 6]).unwrap();
+            assert_eq!(*map.get(2).unwrap(), Value::InActive);
+            assert_eq!(*map.get(0).unwrap(), Value::InActive);
+            assert_eq!(*map.get(6).unwrap(), Value::Active);
+
+            // Value read properly after reconfigure
+            let mut lsettings = line::Settings::new().unwrap();
+            lsettings.set_prop(&[SettingVal::ActiveLow(true)]).unwrap();
+            let lconfig = line::Config::new().unwrap();
+            lconfig.add_line_settings(&offsets, lsettings).unwrap();
+            request.reconfigure_lines(&lconfig).unwrap();
+            assert_eq!(request.value(7).unwrap(), Value::InActive);
+        }
+
+        #[test]
+        fn set_output_values() {
+            let offsets = [0, 1, 3, 4];
+            let mut config = TestConfig::new(NGPIO).unwrap();
+            config.lconfig_val(Some(Direction::Output), Some(Value::Active));
+            config.lconfig_add_settings(&offsets);
+            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);
+            assert_eq!(config.sim().val(4).unwrap(), GPIOSIM_VALUE_ACTIVE);
+
+            // Default
+            assert_eq!(config.sim().val(2).unwrap(), GPIOSIM_VALUE_INACTIVE);
+        }
+
+        #[test]
+        fn update_output_values() {
+            let offsets = [0, 1, 3, 4];
+            let mut config = TestConfig::new(NGPIO).unwrap();
+            config.lconfig_val(Some(Direction::Output), Some(Value::InActive));
+            config.lconfig_add_settings(&offsets);
+            config.request_lines().unwrap();
+            let request = config.request();
+
+            // Set single value
+            request.set_value(1, Value::Active).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, Value::InActive).unwrap();
+            assert_eq!(config.sim().val(1).unwrap(), GPIOSIM_VALUE_INACTIVE);
+
+            // Set values of subset
+            let mut map = ValueMap::new();
+            map.insert(4, Value::Active);
+            map.insert(3, Value::Active);
+            request.set_values_subset(map).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);
+
+            let mut map = ValueMap::new();
+            map.insert(4, Value::InActive);
+            map.insert(3, Value::InActive);
+            request.set_values_subset(map).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(&[
+                    Value::Active,
+                    Value::InActive,
+                    Value::Active,
+                    Value::InActive,
+                ])
+                .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(&[
+                    Value::InActive,
+                    Value::InActive,
+                    Value::InActive,
+                    Value::InActive,
+                ])
+                .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];
+            let mut config = TestConfig::new(NGPIO).unwrap();
+            config.lconfig_bias(Direction::Input, Some(Bias::PullUp));
+            config.lconfig_add_settings(&offsets);
+            config.request_lines().unwrap();
+            config.request();
+
+            // Set single value
+            assert_eq!(config.sim().val(3).unwrap(), GPIOSIM_VALUE_ACTIVE);
+        }
+
+        #[test]
+        fn no_events() {
+            let mut config = TestConfig::new(NGPIO).unwrap();
+            config.lconfig_edge(None, Some(Edge::Both));
+            config.lconfig_add_settings(&[0]);
+            config.request_lines().unwrap();
+
+            // No events available
+            assert!(!config
+                .request()
+                .wait_edge_event(Some(Duration::from_millis(100)))
+                .unwrap());
+        }
+    }
+
+    mod reconfigure {
+        use super::*;
+
+        #[test]
+        fn e2big() {
+            let offsets = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
+            let mut config = TestConfig::new(16).unwrap();
+            config.lconfig_add_settings(&offsets);
+            config.request_lines().unwrap();
+
+            let request = config.request();
+
+            // Reconfigure
+            let mut lsettings = line::Settings::new().unwrap();
+            lsettings
+                .set_prop(&[SettingVal::Direction(Direction::Input)])
+                .unwrap();
+            let lconfig = line::Config::new().unwrap();
+
+            // The uAPI config has only 10 attribute slots, this should pass.
+            for offset in offsets {
+                lsettings
+                    .set_prop(&[SettingVal::DebouncePeriod(Duration::from_millis(
+                        (100 + offset).into(),
+                    ))])
+                    .unwrap();
+                lconfig
+                    .add_line_settings(&[offset as Offset], lsettings.settings_clone().unwrap())
+                    .unwrap();
+            }
+
+            assert!(request.reconfigure_lines(&lconfig).is_ok());
+
+            // The uAPI config has only 10 attribute slots, and this is the 11th entry.
+            // This should fail with E2BIG.
+            lsettings
+                .set_prop(&[SettingVal::DebouncePeriod(Duration::from_millis(100 + 11))])
+                .unwrap();
+            lconfig.add_line_settings(&[11], lsettings).unwrap();
+
+            assert_eq!(
+                request.reconfigure_lines(&lconfig).unwrap_err(),
+                ChipError::OperationFailed(
+                    OperationType::LineRequestReconfigLines,
+                    Errno::new(E2BIG),
+                )
+            );
+        }
+
+        #[test]
+        fn bias() {
+            let mut config = TestConfig::new(NGPIO).unwrap();
+            config.lconfig_add_settings(&[0]);
+            config.request_lines().unwrap();
+            let info = config.chip().line_info(0).unwrap();
+            assert_eq!(info.bias().unwrap(), None);
+
+            let request = config.request();
+
+            // Reconfigure
+            let lconfig = line::Config::new().unwrap();
+            let mut lsettings = line::Settings::new().unwrap();
+            lsettings
+                .set_prop(&[
+                    SettingVal::Direction(Direction::Input),
+                    SettingVal::Bias(Some(Bias::PullUp)),
+                ])
+                .unwrap();
+            lconfig.add_line_settings(&[0], lsettings).unwrap();
+            request.reconfigure_lines(&lconfig).unwrap();
+            let info = config.chip().line_info(0).unwrap();
+            assert_eq!(info.bias().unwrap(), Some(Bias::PullUp));
+
+            let lconfig = line::Config::new().unwrap();
+            let mut lsettings = line::Settings::new().unwrap();
+            lsettings
+                .set_prop(&[
+                    SettingVal::Direction(Direction::Input),
+                    SettingVal::Bias(Some(Bias::PullDown)),
+                ])
+                .unwrap();
+            lconfig.add_line_settings(&[0], lsettings).unwrap();
+            request.reconfigure_lines(&lconfig).unwrap();
+            let info = config.chip().line_info(0).unwrap();
+            assert_eq!(info.bias().unwrap(), Some(Bias::PullDown));
+
+            let lconfig = line::Config::new().unwrap();
+            let mut lsettings = line::Settings::new().unwrap();
+            lsettings
+                .set_prop(&[
+                    SettingVal::Direction(Direction::Input),
+                    SettingVal::Bias(Some(Bias::Disabled)),
+                ])
+                .unwrap();
+            lconfig.add_line_settings(&[0], lsettings).unwrap();
+            request.reconfigure_lines(&lconfig).unwrap();
+            let info = config.chip().line_info(0).unwrap();
+            assert_eq!(info.bias().unwrap(), Some(Bias::Disabled));
+        }
+
+        #[test]
+        fn drive() {
+            let mut config = TestConfig::new(NGPIO).unwrap();
+            config.lconfig_add_settings(&[0]);
+            config.request_lines().unwrap();
+            let info = config.chip().line_info(0).unwrap();
+            assert_eq!(info.drive().unwrap(), Drive::PushPull);
+
+            let request = config.request();
+
+            // Reconfigure
+            let lconfig = line::Config::new().unwrap();
+            let mut lsettings = line::Settings::new().unwrap();
+            lsettings
+                .set_prop(&[
+                    SettingVal::Direction(Direction::Input),
+                    SettingVal::Drive(Drive::PushPull),
+                ])
+                .unwrap();
+            lconfig.add_line_settings(&[0], lsettings).unwrap();
+            request.reconfigure_lines(&lconfig).unwrap();
+            let info = config.chip().line_info(0).unwrap();
+            assert_eq!(info.drive().unwrap(), Drive::PushPull);
+
+            let lconfig = line::Config::new().unwrap();
+            let mut lsettings = line::Settings::new().unwrap();
+            lsettings
+                .set_prop(&[
+                    SettingVal::Direction(Direction::Output),
+                    SettingVal::Drive(Drive::OpenDrain),
+                ])
+                .unwrap();
+            lconfig.add_line_settings(&[0], lsettings).unwrap();
+            request.reconfigure_lines(&lconfig).unwrap();
+            let info = config.chip().line_info(0).unwrap();
+            assert_eq!(info.drive().unwrap(), Drive::OpenDrain);
+
+            let lconfig = line::Config::new().unwrap();
+            let mut lsettings = line::Settings::new().unwrap();
+            lsettings
+                .set_prop(&[
+                    SettingVal::Direction(Direction::Output),
+                    SettingVal::Drive(Drive::OpenSource),
+                ])
+                .unwrap();
+            lconfig.add_line_settings(&[0], lsettings).unwrap();
+            request.reconfigure_lines(&lconfig).unwrap();
+            let info = config.chip().line_info(0).unwrap();
+            assert_eq!(info.drive().unwrap(), Drive::OpenSource);
+        }
+
+        #[test]
+        fn edge() {
+            let mut config = TestConfig::new(NGPIO).unwrap();
+            config.lconfig_add_settings(&[0]);
+            config.request_lines().unwrap();
+            let info = config.chip().line_info(0).unwrap();
+            assert_eq!(info.edge_detection().unwrap(), None);
+
+            let request = config.request();
+
+            // Reconfigure
+            let lconfig = line::Config::new().unwrap();
+            let mut lsettings = line::Settings::new().unwrap();
+            lsettings
+                .set_prop(&[
+                    SettingVal::Direction(Direction::Input),
+                    SettingVal::EdgeDetection(Some(Edge::Both)),
+                ])
+                .unwrap();
+            lconfig.add_line_settings(&[0], lsettings).unwrap();
+            request.reconfigure_lines(&lconfig).unwrap();
+            let info = config.chip().line_info(0).unwrap();
+            assert_eq!(info.edge_detection().unwrap(), Some(Edge::Both));
+
+            let lconfig = line::Config::new().unwrap();
+            let mut lsettings = line::Settings::new().unwrap();
+            lsettings
+                .set_prop(&[
+                    SettingVal::Direction(Direction::Input),
+                    SettingVal::EdgeDetection(Some(Edge::Rising)),
+                ])
+                .unwrap();
+            lconfig.add_line_settings(&[0], lsettings).unwrap();
+            request.reconfigure_lines(&lconfig).unwrap();
+            let info = config.chip().line_info(0).unwrap();
+            assert_eq!(info.edge_detection().unwrap(), Some(Edge::Rising));
+
+            let lconfig = line::Config::new().unwrap();
+            let mut lsettings = line::Settings::new().unwrap();
+            lsettings
+                .set_prop(&[
+                    SettingVal::Direction(Direction::Input),
+                    SettingVal::EdgeDetection(Some(Edge::Falling)),
+                ])
+                .unwrap();
+            lconfig.add_line_settings(&[0], lsettings).unwrap();
+            request.reconfigure_lines(&lconfig).unwrap();
+            let info = config.chip().line_info(0).unwrap();
+            assert_eq!(info.edge_detection().unwrap(), Some(Edge::Falling));
+        }
+
+        #[test]
+        fn event_clock() {
+            let mut config = TestConfig::new(NGPIO).unwrap();
+            config.lconfig_add_settings(&[0]);
+            config.request_lines().unwrap();
+            let info = config.chip().line_info(0).unwrap();
+            assert_eq!(info.event_clock().unwrap(), EventClock::Monotonic);
+
+            let request = config.request();
+
+            // Reconfigure
+            let lconfig = line::Config::new().unwrap();
+            let mut lsettings = line::Settings::new().unwrap();
+            lsettings
+                .set_prop(&[SettingVal::EventClock(EventClock::Monotonic)])
+                .unwrap();
+            lconfig.add_line_settings(&[0], lsettings).unwrap();
+            request.reconfigure_lines(&lconfig).unwrap();
+            let info = config.chip().line_info(0).unwrap();
+            assert_eq!(info.event_clock().unwrap(), EventClock::Monotonic);
+
+            let lconfig = line::Config::new().unwrap();
+            let mut lsettings = line::Settings::new().unwrap();
+            lsettings
+                .set_prop(&[SettingVal::EventClock(EventClock::Realtime)])
+                .unwrap();
+            lconfig.add_line_settings(&[0], lsettings).unwrap();
+            request.reconfigure_lines(&lconfig).unwrap();
+            let info = config.chip().line_info(0).unwrap();
+            assert_eq!(info.event_clock().unwrap(), EventClock::Realtime);
+
+            let lconfig = line::Config::new().unwrap();
+            let mut lsettings = line::Settings::new().unwrap();
+            lsettings
+                .set_prop(&[SettingVal::EventClock(EventClock::HTE)])
+                .unwrap();
+            lconfig.add_line_settings(&[0], lsettings).unwrap();
+            request.reconfigure_lines(&lconfig).unwrap();
+            let info = config.chip().line_info(0).unwrap();
+            assert_eq!(info.event_clock().unwrap(), EventClock::HTE);
+        }
+
+        #[test]
+        fn debounce() {
+            let mut config = TestConfig::new(NGPIO).unwrap();
+            config.lconfig_add_settings(&[0]);
+            config.request_lines().unwrap();
+            let info = config.chip().line_info(0).unwrap();
+            assert!(!info.is_debounced());
+            assert_eq!(info.debounce_period(), Duration::from_millis(0));
+
+            let request = config.request();
+
+            // Reconfigure
+            let lconfig = line::Config::new().unwrap();
+            let mut lsettings = line::Settings::new().unwrap();
+            lsettings
+                .set_prop(&[
+                    SettingVal::Direction(Direction::Input),
+                    SettingVal::DebouncePeriod(Duration::from_millis(100)),
+                ])
+                .unwrap();
+            lconfig.add_line_settings(&[0], lsettings).unwrap();
+            request.reconfigure_lines(&lconfig).unwrap();
+            let info = config.chip().line_info(0).unwrap();
+            assert!(info.is_debounced());
+            assert_eq!(info.debounce_period(), Duration::from_millis(100));
+        }
+    }
+}
diff --git a/bindings/rust/libgpiod/tests/line_settings.rs b/bindings/rust/libgpiod/tests/line_settings.rs
new file mode 100644
index 000000000000..3f84bf328ab7
--- /dev/null
+++ b/bindings/rust/libgpiod/tests/line_settings.rs
@@ -0,0 +1,226 @@
+// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
+//
+// Copyright 2022 Linaro Ltd. All Rights Reserved.
+//     Viresh Kumar <viresh.kumar@linaro.org>
+
+mod common;
+
+mod line_settings {
+    use std::time::Duration;
+
+    use libgpiod::{
+        line, Bias, Direction, Drive, Edge, EventClock, SettingKind, SettingVal, Value,
+    };
+
+    #[test]
+    fn direction() {
+        let mut lsettings = line::Settings::new().unwrap();
+        assert_eq!(
+            lsettings.prop(SettingKind::Direction).unwrap(),
+            SettingVal::Direction(Direction::AsIs)
+        );
+
+        lsettings
+            .set_prop(&[SettingVal::Direction(Direction::Input)])
+            .unwrap();
+        assert_eq!(
+            lsettings.prop(SettingKind::Direction).unwrap(),
+            SettingVal::Direction(Direction::Input)
+        );
+
+        lsettings
+            .set_prop(&[SettingVal::Direction(Direction::Output)])
+            .unwrap();
+        assert_eq!(
+            lsettings.prop(SettingKind::Direction).unwrap(),
+            SettingVal::Direction(Direction::Output)
+        );
+    }
+
+    #[test]
+    fn edge_detection() {
+        let mut lsettings = line::Settings::new().unwrap();
+        assert_eq!(
+            lsettings.prop(SettingKind::EdgeDetection).unwrap(),
+            SettingVal::EdgeDetection(None)
+        );
+
+        lsettings
+            .set_prop(&[SettingVal::EdgeDetection(Some(Edge::Both))])
+            .unwrap();
+        assert_eq!(
+            lsettings.prop(SettingKind::EdgeDetection).unwrap(),
+            SettingVal::EdgeDetection(Some(Edge::Both))
+        );
+
+        lsettings
+            .set_prop(&[SettingVal::EdgeDetection(Some(Edge::Rising))])
+            .unwrap();
+        assert_eq!(
+            lsettings.prop(SettingKind::EdgeDetection).unwrap(),
+            SettingVal::EdgeDetection(Some(Edge::Rising))
+        );
+
+        lsettings
+            .set_prop(&[SettingVal::EdgeDetection(Some(Edge::Falling))])
+            .unwrap();
+        assert_eq!(
+            lsettings.prop(SettingKind::EdgeDetection).unwrap(),
+            SettingVal::EdgeDetection(Some(Edge::Falling))
+        );
+    }
+
+    #[test]
+    fn bias() {
+        let mut lsettings = line::Settings::new().unwrap();
+        assert_eq!(
+            lsettings.prop(SettingKind::Bias).unwrap(),
+            SettingVal::Bias(None)
+        );
+
+        lsettings
+            .set_prop(&[SettingVal::Bias(Some(Bias::PullDown))])
+            .unwrap();
+        assert_eq!(
+            lsettings.prop(SettingKind::Bias).unwrap(),
+            SettingVal::Bias(Some(Bias::PullDown))
+        );
+
+        lsettings
+            .set_prop(&[SettingVal::Bias(Some(Bias::PullUp))])
+            .unwrap();
+        assert_eq!(
+            lsettings.prop(SettingKind::Bias).unwrap(),
+            SettingVal::Bias(Some(Bias::PullUp))
+        );
+    }
+
+    #[test]
+    fn drive() {
+        let mut lsettings = line::Settings::new().unwrap();
+        assert_eq!(
+            lsettings.prop(SettingKind::Drive).unwrap(),
+            SettingVal::Drive(Drive::PushPull)
+        );
+
+        lsettings
+            .set_prop(&[SettingVal::Drive(Drive::PushPull)])
+            .unwrap();
+        assert_eq!(
+            lsettings.prop(SettingKind::Drive).unwrap(),
+            SettingVal::Drive(Drive::PushPull)
+        );
+
+        lsettings
+            .set_prop(&[SettingVal::Drive(Drive::OpenDrain)])
+            .unwrap();
+        assert_eq!(
+            lsettings.prop(SettingKind::Drive).unwrap(),
+            SettingVal::Drive(Drive::OpenDrain)
+        );
+
+        lsettings
+            .set_prop(&[SettingVal::Drive(Drive::OpenSource)])
+            .unwrap();
+        assert_eq!(
+            lsettings.prop(SettingKind::Drive).unwrap(),
+            SettingVal::Drive(Drive::OpenSource)
+        );
+    }
+
+    #[test]
+    fn active_low() {
+        let mut lsettings = line::Settings::new().unwrap();
+        assert_eq!(
+            lsettings.prop(SettingKind::ActiveLow).unwrap(),
+            SettingVal::ActiveLow(false)
+        );
+
+        lsettings.set_prop(&[SettingVal::ActiveLow(true)]).unwrap();
+        assert_eq!(
+            lsettings.prop(SettingKind::ActiveLow).unwrap(),
+            SettingVal::ActiveLow(true)
+        );
+
+        lsettings.set_prop(&[SettingVal::ActiveLow(false)]).unwrap();
+        assert_eq!(
+            lsettings.prop(SettingKind::ActiveLow).unwrap(),
+            SettingVal::ActiveLow(false)
+        );
+    }
+
+    #[test]
+    fn debounce_period() {
+        let mut lsettings = line::Settings::new().unwrap();
+        assert_eq!(
+            lsettings.prop(SettingKind::DebouncePeriod).unwrap(),
+            SettingVal::DebouncePeriod(Duration::from_millis(0))
+        );
+
+        lsettings
+            .set_prop(&[SettingVal::DebouncePeriod(Duration::from_millis(5))])
+            .unwrap();
+        assert_eq!(
+            lsettings.prop(SettingKind::DebouncePeriod).unwrap(),
+            SettingVal::DebouncePeriod(Duration::from_millis(5))
+        );
+    }
+
+    #[test]
+    fn event_clock() {
+        let mut lsettings = line::Settings::new().unwrap();
+        assert_eq!(
+            lsettings.prop(SettingKind::EventClock).unwrap(),
+            SettingVal::EventClock(EventClock::Monotonic)
+        );
+
+        lsettings
+            .set_prop(&[SettingVal::EventClock(EventClock::Realtime)])
+            .unwrap();
+        assert_eq!(
+            lsettings.prop(SettingKind::EventClock).unwrap(),
+            SettingVal::EventClock(EventClock::Realtime)
+        );
+
+        lsettings
+            .set_prop(&[SettingVal::EventClock(EventClock::HTE)])
+            .unwrap();
+        assert_eq!(
+            lsettings.prop(SettingKind::EventClock).unwrap(),
+            SettingVal::EventClock(EventClock::HTE)
+        );
+
+        lsettings
+            .set_prop(&[SettingVal::EventClock(EventClock::Monotonic)])
+            .unwrap();
+        assert_eq!(
+            lsettings.prop(SettingKind::EventClock).unwrap(),
+            SettingVal::EventClock(EventClock::Monotonic)
+        );
+    }
+
+    #[test]
+    fn output_value() {
+        let mut lsettings = line::Settings::new().unwrap();
+        assert_eq!(
+            lsettings.prop(SettingKind::OutputValue).unwrap(),
+            SettingVal::OutputValue(Value::InActive)
+        );
+
+        lsettings
+            .set_prop(&[SettingVal::OutputValue(Value::Active)])
+            .unwrap();
+        assert_eq!(
+            lsettings.prop(SettingKind::OutputValue).unwrap(),
+            SettingVal::OutputValue(Value::Active)
+        );
+
+        lsettings
+            .set_prop(&[SettingVal::OutputValue(Value::InActive)])
+            .unwrap();
+        assert_eq!(
+            lsettings.prop(SettingKind::OutputValue).unwrap(),
+            SettingVal::OutputValue(Value::InActive)
+        );
+    }
+}
diff --git a/bindings/rust/libgpiod/tests/request_config.rs b/bindings/rust/libgpiod/tests/request_config.rs
new file mode 100644
index 000000000000..d9892ee99101
--- /dev/null
+++ b/bindings/rust/libgpiod/tests/request_config.rs
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
+//
+// Copyright 2022 Linaro Ltd. All Rights Reserved.
+//     Viresh Kumar <viresh.kumar@linaro.org>
+
+mod common;
+
+mod request_config {
+    use vmm_sys_util::errno::Error as Errno;
+
+    use libgpiod::{request, Error as ChipError, OperationType};
+
+    mod verify {
+        use super::*;
+
+        #[test]
+        fn default() {
+            let rconfig = request::Config::new().unwrap();
+
+            assert_eq!(rconfig.event_buffer_size(), 0);
+            assert_eq!(
+                rconfig.consumer().unwrap_err(),
+                ChipError::OperationFailed(OperationType::RequestConfigGetConsumer, Errno::new(0))
+            );
+        }
+
+        #[test]
+        fn initialized() {
+            const CONSUMER: &str = "foobar";
+            let rconfig = request::Config::new().unwrap();
+            rconfig.set_consumer(CONSUMER);
+            rconfig.set_event_buffer_size(64);
+
+            assert_eq!(rconfig.event_buffer_size(), 64);
+            assert_eq!(rconfig.consumer().unwrap(), CONSUMER);
+        }
+    }
+}
-- 
2.31.1.272.g89b43f80a514


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH V7 8/8] libgpiod: Integrate building of rust bindings with make
  2022-10-14 10:47 [PATCH V7 0/8] libgpiod: Add Rust bindings Viresh Kumar
                   ` (6 preceding siblings ...)
  2022-10-14 10:47 ` [PATCH V7 7/8] libgpiod: Add rust tests Viresh Kumar
@ 2022-10-14 10:47 ` Viresh Kumar
  2022-10-14 17:03 ` [PATCH V7 0/8] libgpiod: Add Rust bindings Miguel Ojeda
  8 siblings, 0 replies; 50+ messages in thread
From: Viresh Kumar @ 2022-10-14 10:47 UTC (permalink / raw)
  To: Linus Walleij, Bartosz Golaszewski
  Cc: Viresh Kumar, Vincent Guittot, linux-gpio, Kent Gibson,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

Lets make build rust bindings as well.

Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
---
 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

diff --git a/README b/README
index 814a0f161fd2..68b5d69f9b66 100644
--- a/README
+++ b/README
@@ -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
 -------
 
diff --git a/TODO b/TODO
index 8bb4d8f3ad56..cf4fd7b4a962 100644
--- a/TODO
+++ b/TODO
@@ -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
 
diff --git a/bindings/Makefile.am b/bindings/Makefile.am
index 8f8c762f254f..004ae23dbc58 100644
--- a/bindings/Makefile.am
+++ b/bindings/Makefile.am
@@ -14,3 +14,9 @@ if WITH_BINDINGS_PYTHON
 SUBDIRS += python
 
 endif
+
+if WITH_BINDINGS_RUST
+
+SUBDIRS += rust
+
+endif
diff --git a/bindings/rust/Makefile.am b/bindings/rust/Makefile.am
new file mode 100644
index 000000000000..79a52bc691ae
--- /dev/null
+++ b/bindings/rust/Makefile.am
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# SPDX-FileCopyrightText: 2022 Viresh Kumar <viresh.kumar@linaro.org>
+
+command = cargo build --release --lib
+
+if WITH_TESTS
+command += --tests
+endif
+
+if WITH_EXAMPLES
+command += --examples
+endif
+
+all:
+	$(command)
+
+clean:
+	cargo clean
diff --git a/configure.ac b/configure.ac
index 6ac1d8ed8a40..37cfdb3644f7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -212,6 +212,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
@@ -246,6 +261,7 @@ AC_CONFIG_FILES([Makefile
 		 bindings/python/Makefile
 		 bindings/python/examples/Makefile
 		 bindings/python/tests/Makefile
+		 bindings/rust/Makefile
 		 man/Makefile])
 
 AC_OUTPUT
-- 
2.31.1.272.g89b43f80a514


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* Re: [PATCH V7 0/8] libgpiod: Add Rust bindings
  2022-10-14 10:47 [PATCH V7 0/8] libgpiod: Add Rust bindings Viresh Kumar
                   ` (7 preceding siblings ...)
  2022-10-14 10:47 ` [PATCH V7 8/8] libgpiod: Integrate building of rust bindings with make Viresh Kumar
@ 2022-10-14 17:03 ` Miguel Ojeda
  2022-10-20 13:29   ` Björn Roy Baron
  8 siblings, 1 reply; 50+ messages in thread
From: Miguel Ojeda @ 2022-10-14 17:03 UTC (permalink / raw)
  To: Viresh Kumar
  Cc: Linus Walleij, Bartosz Golaszewski, Vincent Guittot, linux-gpio,
	Kent Gibson, Alex Bennée, stratos-dev, Gerard Ryan,
	Wedson Almeida Filho, Alex Gaynor, Boqun Feng, Gary Guo,
	Björn Roy Baron, y86-dev

On Fri, Oct 14, 2022 at 12:47 PM Viresh Kumar <viresh.kumar@linaro.org> wrote:
>
> 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 v7
>
> [I have pushed v6 and v5 there too, in case someone wants to look at the
> changes].

Fixing Wedson's address (the old one is not valid anymore, which is
why I guess he got dropped at some point in other threads) and Cc'ing
others in case they want to take a look.

Cheers,
Miguel

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH V7 1/8] libgpiod: Add libgpiod-sys rust crate
  2022-10-14 10:47 ` [PATCH V7 1/8] libgpiod: Add libgpiod-sys rust crate Viresh Kumar
@ 2022-10-17 12:59   ` Kent Gibson
  2022-10-18 11:22     ` Viresh Kumar
  0 siblings, 1 reply; 50+ messages in thread
From: Kent Gibson @ 2022-10-17 12:59 UTC (permalink / raw)
  To: Viresh Kumar
  Cc: Linus Walleij, Bartosz Golaszewski, Vincent Guittot, linux-gpio,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

On Fri, Oct 14, 2022 at 04:17:18PM +0530, Viresh Kumar wrote:
> This adds libgpiod-sys rust crate, which provides FFI (foreign function
> interface) bindings for libgpiod APIs.
> 

For the whole series: 

Mail subject should start with [libgpiod v2]

Commits should begin with "bindings: rust: ", rather than "libgpiod:" as
the latter is self-evident.

> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
> ---
>  .gitignore                            |  5 ++++
>  bindings/rust/Cargo.toml              |  5 ++++
>  bindings/rust/libgpiod-sys/Cargo.toml | 15 ++++++++++
>  bindings/rust/libgpiod-sys/build.rs   | 41 +++++++++++++++++++++++++++
>  bindings/rust/libgpiod-sys/src/lib.rs | 13 +++++++++
>  5 files changed, 79 insertions(+)
>  create mode 100644 bindings/rust/Cargo.toml
>  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
> 
> diff --git a/.gitignore b/.gitignore
> index 6c08415b390d..9f2fcf440c5d 100644
> --- a/.gitignore
> +++ b/.gitignore
> @@ -35,3 +35,8 @@ stamp-h1
>  # profiling
>  *.gcda
>  *.gcno
> +
> +# Added by cargo
> +
> +target
> +Cargo.lock
> diff --git a/bindings/rust/Cargo.toml b/bindings/rust/Cargo.toml
> new file mode 100644
> index 000000000000..d0b3a3c88ff1
> --- /dev/null
> +++ b/bindings/rust/Cargo.toml
> @@ -0,0 +1,5 @@
> +[workspace]
> +
> +members = [
> +    "libgpiod-sys",
> +]
> diff --git a/bindings/rust/libgpiod-sys/Cargo.toml b/bindings/rust/libgpiod-sys/Cargo.toml
> new file mode 100644
> index 000000000000..77f82719d269
> --- /dev/null
> +++ b/bindings/rust/libgpiod-sys/Cargo.toml
> @@ -0,0 +1,15 @@
> +[package]
> +name = "libgpiod-sys"
> +version = "0.1.0"
> +edition = "2018"
> +

Add license and other relevant keys as per the link you helpfully include...

> +# 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"
> diff --git a/bindings/rust/libgpiod-sys/build.rs b/bindings/rust/libgpiod-sys/build.rs
> new file mode 100644
> index 000000000000..98863686c7af
> --- /dev/null
> +++ b/bindings/rust/libgpiod-sys/build.rs
> @@ -0,0 +1,41 @@
> +#[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() {
> +    // Tell cargo to invalidate the built crate whenever following files change
> +    println!("cargo:rerun-if-changed=../../../include/gpiod.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()
> +        // The input header we would like to generate
> +        // bindings for.
> +        .header("../../../include/gpiod.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 main() {
> +    #[cfg(feature = "generate")]
> +    generate_bindings();
> +
> +    println!("cargo:rustc-link-search=./../../lib/.libs/");
> +    println!("cargo:rustc-link-lib=static=gpiod");
> +}
> diff --git a/bindings/rust/libgpiod-sys/src/lib.rs b/bindings/rust/libgpiod-sys/src/lib.rs
> new file mode 100644
> index 000000000000..1ca355b5f5ac
> --- /dev/null
> +++ b/bindings/rust/libgpiod-sys/src/lib.rs
> @@ -0,0 +1,13 @@
> +// SPDX-License-Identifier: GPL-2.0
> +

GPL-2.0?

Elsewhere you use Apache 2.0 AND BSD-3-Clause for rust code.

Cheers,
Kent.

> +#[allow(non_camel_case_types, non_upper_case_globals)]
> +#[cfg_attr(test, allow(deref_nullptr, non_snake_case))]
> +
> +mod bindings_raw {
> +    #[cfg(feature = "generate")]
> +    include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
> +
> +    #[cfg(not(feature = "generate"))]
> +    include!("bindings.rs");
> +}
> +pub use bindings_raw::*;
> -- 
> 2.31.1.272.g89b43f80a514
> 

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH V7 3/8] libgpiod: Add rust wrapper crate
  2022-10-14 10:47 ` [PATCH V7 3/8] libgpiod: Add rust wrapper crate Viresh Kumar
@ 2022-10-17 12:59   ` Kent Gibson
  2022-10-21 11:22     ` Viresh Kumar
  2022-10-18  3:27   ` Kent Gibson
  1 sibling, 1 reply; 50+ messages in thread
From: Kent Gibson @ 2022-10-17 12:59 UTC (permalink / raw)
  To: Viresh Kumar
  Cc: Linus Walleij, Bartosz Golaszewski, Vincent Guittot, linux-gpio,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

On Fri, Oct 14, 2022 at 04:17:20PM +0530, Viresh Kumar wrote:
> Add rust wrapper crate, around the libpiod-sys crate added earlier, to
> provide a convenient interface for the users.
> 
> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
> ---
>  bindings/rust/Cargo.toml                     |   1 +
>  bindings/rust/libgpiod/Cargo.toml            |  13 +
>  bindings/rust/libgpiod/src/chip.rs           | 253 ++++++++++
>  bindings/rust/libgpiod/src/edge_event.rs     | 101 ++++
>  bindings/rust/libgpiod/src/event_buffer.rs   |  66 +++
>  bindings/rust/libgpiod/src/info_event.rs     |  68 +++
>  bindings/rust/libgpiod/src/lib.rs            | 471 +++++++++++++++++++
>  bindings/rust/libgpiod/src/line_config.rs    | 118 +++++
>  bindings/rust/libgpiod/src/line_info.rs      | 180 +++++++
>  bindings/rust/libgpiod/src/line_request.rs   | 246 ++++++++++
>  bindings/rust/libgpiod/src/line_settings.rs  | 277 +++++++++++
>  bindings/rust/libgpiod/src/request_config.rs |  96 ++++
>  12 files changed, 1890 insertions(+)
>  create mode 100644 bindings/rust/libgpiod/Cargo.toml
>  create mode 100644 bindings/rust/libgpiod/src/chip.rs
>  create mode 100644 bindings/rust/libgpiod/src/edge_event.rs
>  create mode 100644 bindings/rust/libgpiod/src/event_buffer.rs
>  create mode 100644 bindings/rust/libgpiod/src/info_event.rs
>  create mode 100644 bindings/rust/libgpiod/src/lib.rs
>  create mode 100644 bindings/rust/libgpiod/src/line_config.rs
>  create mode 100644 bindings/rust/libgpiod/src/line_info.rs
>  create mode 100644 bindings/rust/libgpiod/src/line_request.rs
>  create mode 100644 bindings/rust/libgpiod/src/line_settings.rs
>  create mode 100644 bindings/rust/libgpiod/src/request_config.rs
> 
> diff --git a/bindings/rust/Cargo.toml b/bindings/rust/Cargo.toml
> index d0b3a3c88ff1..1e57ef2c0002 100644
> --- a/bindings/rust/Cargo.toml
> +++ b/bindings/rust/Cargo.toml
> @@ -2,4 +2,5 @@
>  
>  members = [
>      "libgpiod-sys",
> +    "libgpiod"
>  ]
> diff --git a/bindings/rust/libgpiod/Cargo.toml b/bindings/rust/libgpiod/Cargo.toml
> new file mode 100644
> index 000000000000..f25242abb153
> --- /dev/null
> +++ b/bindings/rust/libgpiod/Cargo.toml
> @@ -0,0 +1,13 @@
> +[package]
> +name = "libgpiod"
> +version = "0.1.0"
> +edition = "2018"

Add license and other relevant keys as per the link.

> +
> +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
> +
> +[dependencies]
> +intmap = "2.0.0"
> +libc = ">=0.2.39"
> +libgpiod-sys = { path = "../libgpiod-sys" }
> +thiserror = "1.0"
> +vmm-sys-util = "=0.10.0"
> diff --git a/bindings/rust/libgpiod/src/chip.rs b/bindings/rust/libgpiod/src/chip.rs
> new file mode 100644
> index 000000000000..4f52c3f141f4
> --- /dev/null
> +++ b/bindings/rust/libgpiod/src/chip.rs
> @@ -0,0 +1,253 @@
> +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
> +//
> +// Copyright 2022 Linaro Ltd. All Rights Reserved.
> +//     Viresh Kumar <viresh.kumar@linaro.org>
> +
> +use libc::strlen;

Avoid importing function names.  Idiomatically, modules and types are ok,
but not functions.  So says the Rust book.

> +use std::os::raw::c_char;
> +use std::path::Path;
> +use std::sync::Arc;
> +use std::time::Duration;
> +use std::{slice, str};
> +
> +use vmm_sys_util::errno::Error as Errno;

Why vmm_sys_util::errno::Error rather than, say, errno::Errno?

> +
> +use super::{gpiod, info, line, request, Error, Offset, OperationType, Result};
> +
> +#[derive(Clone, Debug, Eq, PartialEq)]
> +pub(crate) struct Internal {
> +    chip: *mut gpiod::gpiod_chip,
> +}
> +

Derived implmentation of Clone on an object containing a raw pointer and
a Drop that frees that pointer?  That wont end well.

If you have an Arc managing references to it below, why the need for Clone?

Types that wrap C types that should impl Clone have a C _copy function,
and require an explicit impl that calls it, not a derived.

> +impl Internal {
> +    /// Find a chip by path.
> +    pub(crate) fn open<P: AsRef<Path>>(path: &P) -> Result<Self> {
> +        // Null-terminate the string
> +        let path = path.as_ref().to_string_lossy() + "\0";
> +
> +        let chip = unsafe { gpiod::gpiod_chip_open(path.as_ptr() as *const c_char) };

All unsafes should have a preceding SAFETY comment.

> +        if chip.is_null() {
> +            return Err(Error::OperationFailed(
> +                OperationType::ChipOpen,
> +                Errno::last(),
> +            ));
> +        }
> +
> +        Ok(Self { chip })
> +    }
> +
> +    /// Private helper, Returns gpiod_chip
> +    pub(crate) fn chip(&self) -> *mut gpiod::gpiod_chip {
> +        self.chip
> +    }

Just make self.chip itself pub(crate)?

> +}
> +
> +impl Drop for Internal {
> +    /// Close the chip and release all associated resources.
> +    fn drop(&mut self) {
> +        unsafe { gpiod::gpiod_chip_close(self.chip) }
> +    }
> +}
> +
> +/// 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.
> +#[derive(Clone, Debug, Eq, PartialEq)]
> +pub struct Chip {
> +    ichip: Arc<Internal>,
> +    info: Info,
> +}
> +

Doesn't info need an Arc as well? (see struct Info below).

Actually, does it make sense to be able to Clone the Chip?
Two threads could then watch different lines and
wait_info_event() from the one chip.
That isn't going to work, at least not the way you would want.

> +unsafe impl Send for Chip {}
> +unsafe impl Sync for Chip {}
> +

Send makes sense to me, but not Sync, due to the waiting for info events.
Similarly for line_request::Request.
At least not without some additional synchronisation/locking, and I'm not
sure we want to go there.

> +impl Chip {
> +    /// Find a chip by path.
> +    pub fn open<P: AsRef<Path>>(path: &P) -> Result<Self> {
> +        let ichip = Arc::new(Internal::open(path)?);
> +        let info = Info::new(ichip.clone())?;
> +

info is not always necessary, e.g. if the user just want to request a
line.  Maybe lazy load it?  Assuming you want to keep a copy cached with
the Chip at all.

> +        Ok(Self { ichip, info })
> +    }
> +
> +    /// Get the chip name as represented in the kernel.
> +    pub fn name(&self) -> Result<&str> {
> +        self.info.name()
> +    }
> +
> +    /// Get the chip label as represented in the kernel.
> +    pub fn label(&self) -> Result<&str> {
> +        self.info.label()
> +    }
> +
> +    /// Get the number of GPIO lines exposed by the chip.
> +    pub fn num_lines(&self) -> usize {
> +        self.info.num_lines()
> +    }
> +
> +    /// Get the path used to find the chip.
> +    pub fn path(&self) -> Result<&str> {
> +        // SAFETY: The string returned by libgpiod is guaranteed to live as long
> +        // as the `struct Chip`.
> +        let path = unsafe { gpiod::gpiod_chip_get_path(self.ichip.chip()) };
> +
> +        // SAFETY: The string is guaranteed to be valid here by the C API.
> +        str::from_utf8(unsafe { slice::from_raw_parts(path as *const u8, strlen(path) as usize) })
> +            .map_err(Error::StringNotUtf8)
> +    }
> +
> +    /// Get information about the chip.
> +    pub fn info(&self) -> Result<Info> {
> +        Info::new(self.ichip.clone())
> +    }

You already wrap all the methods of Info, so why the need to return a
copy as well? Do you really need both?

And the functions of Info are currently pub(crate) so what could they do
with it anyway?

> +
> +    /// Get a snapshot of information about the line.
> +    pub fn line_info(&self, offset: Offset) -> Result<line::Info> {
> +        line::Info::new(self.ichip.clone(), offset)
> +    }
> +
> +    /// Get the current snapshot of information about the line at given offset and start watching
> +    /// it for future changes.
> +    pub fn watch_line_info(&self, offset: Offset) -> Result<line::Info> {
> +        line::Info::new_watch(self.ichip.clone(), offset)
> +    }
> +
> +    /// Stop watching a line
> +    pub fn unwatch(&self, offset: Offset) {
> +        unsafe {
> +            gpiod::gpiod_chip_unwatch_line_info(self.ichip.chip(), offset);
> +        }
> +    }
> +
> +    /// 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 fd(&self) -> Result<u32> {
> +        let fd = unsafe { gpiod::gpiod_chip_get_fd(self.ichip.chip()) };
> +
> +        if fd < 0 {
> +            Err(Error::OperationFailed(
> +                OperationType::ChipGetFd,
> +                Errno::last(),
> +            ))
> +        } else {
> +            Ok(fd as u32)
> +        }
> +    }
> +
> +    /// Wait for line status events on any of the watched lines on the chip.
> +    pub fn wait_info_event(&self, timeout: Option<Duration>) -> Result<bool> {
> +        let timeout = match timeout {
> +            Some(x) => x.as_nanos() as i64,
> +            // Block indefinitely
> +            None => -1,
> +        };
> +
> +        let ret = unsafe { gpiod::gpiod_chip_wait_info_event(self.ichip.chip(), timeout) };
> +
> +        match ret {
> +            -1 => Err(Error::OperationFailed(
> +                OperationType::ChipWaitInfoEvent,
> +                Errno::last(),
> +            )),
> +            0 => Ok(false),
> +            _ => Ok(true),
> +        }
> +    }
> +
> +    /// 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<info::Event> {
> +        info::Event::new(&self.ichip)
> +    }
> +
> +    /// Map a GPIO line's name to its offset within the chip.
> +    pub fn line_offset_from_name(&self, name: &str) -> Result<Offset> {
> +        // Null-terminate the string
> +        let name = name.to_owned() + "\0";
> +
> +        let ret = unsafe {
> +            gpiod::gpiod_chip_get_line_offset_from_name(
> +                self.ichip.chip(),
> +                name.as_ptr() as *const c_char,
> +            )
> +        };
> +
> +        if ret == -1 {
> +            Err(Error::OperationFailed(
> +                OperationType::ChipGetLine,
> +                Errno::last(),
> +            ))

The OperationType identifies the underlying C call that caused the
error?  So it should be ChipGetLineOffsetFromName?

> +        } else {
> +            Ok(ret as u32)
> +        }
> +    }
> +
> +    /// Request a set of lines for exclusive usage.
> +    pub fn request_lines(
> +        &self,
> +        rconfig: &request::Config,
> +        lconfig: &line::Config,
> +    ) -> Result<line::Request> {
> +        line::Request::new(&self.ichip, rconfig, lconfig)
> +    }
> +}
> +
> +/// GPIO chip Information
> +#[derive(Clone, Debug, Eq, PartialEq)]
> +pub struct Info {
> +    info: *mut gpiod::gpiod_chip_info,
> +}
> +

Does struct Info even need to be pub?

Clone/Drop wrapping a raw pointer again.

> +impl Info {
> +    /// Find a GPIO chip by path.
> +    pub(crate) fn new(chip: Arc<Internal>) -> Result<Self> {
> +        let info = unsafe { gpiod::gpiod_chip_get_info(chip.chip()) };
> +        if info.is_null() {
> +            return Err(Error::OperationFailed(
> +                OperationType::ChipInfoGet,
> +                Errno::last(),
> +            ));
> +        }
> +
> +        Ok(Self { info })
> +    }

Do these functions need to be pub(crate)?  They are only used within the
module (by Chip), and are visible to that anyway.

> +
> +    /// 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 { gpiod::gpiod_chip_info_get_name(self.info) };
> +
> +        // SAFETY: The string is guaranteed to be valid here by the C API.
> +        str::from_utf8(unsafe { slice::from_raw_parts(name as *const u8, strlen(name) as usize) })
> +            .map_err(Error::StringNotUtf8)
> +    }
> +
> +    /// 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 { gpiod::gpiod_chip_info_get_label(self.info) };
> +
> +        // SAFETY: The string is guaranteed to be valid here by the C API.
> +        str::from_utf8(unsafe { slice::from_raw_parts(label as *const u8, strlen(label) as usize) })
> +            .map_err(Error::StringNotUtf8)
> +    }
> +
> +    /// Get the number of GPIO lines exposed by the chip.
> +    pub(crate) fn num_lines(&self) -> usize {
> +        unsafe { gpiod::gpiod_chip_info_get_num_lines(self.info) as usize }
> +    }
> +}
> +
> +impl Drop for Info {
> +    /// Close the GPIO chip info and release all associated resources.
> +    fn drop(&mut self) {
> +        unsafe { gpiod::gpiod_chip_info_free(self.info) }
> +    }
> +}
> diff --git a/bindings/rust/libgpiod/src/edge_event.rs b/bindings/rust/libgpiod/src/edge_event.rs
> new file mode 100644
> index 000000000000..12c0fd73f778
> --- /dev/null
> +++ b/bindings/rust/libgpiod/src/edge_event.rs
> @@ -0,0 +1,101 @@
> +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
> +//
> +// Copyright 2022 Linaro Ltd. All Rights Reserved.
> +//     Viresh Kumar <viresh.kumar@linaro.org>
> +
> +use std::time::Duration;
> +
> +use vmm_sys_util::errno::Error as Errno;
> +
> +use super::{edge::event::Buffer, gpiod, EdgeKind, Error, Offset, OperationType, 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.
> +
> +#[derive(Debug, Eq, PartialEq)]
> +pub struct Event<'b> {
> +    buffer: Option<&'b Buffer>,
> +    event: *mut gpiod::gpiod_edge_event,
> +}
> +
> +impl<'b> Event<'b> {
> +    /// Get an event stored in the buffer.
> +    pub(crate) fn new(buffer: &'b Buffer, index: u64) -> Result<Self> {
> +        let event = unsafe { gpiod::gpiod_edge_event_buffer_get_event(buffer.buffer(), index) };
> +        if event.is_null() {
> +            return Err(Error::OperationFailed(
> +                OperationType::EdgeEventBufferGetEvent,
> +                Errno::last(),
> +            ));
> +        }
> +
> +        Ok(Self {
> +            buffer: Some(buffer),
> +            event,
> +        })
> +    }
> +
> +    pub fn event_clone(event: &Event) -> Result<Self> {
> +        let event = unsafe { gpiod::gpiod_edge_event_copy(event.event) };
> +        if event.is_null() {
> +            return Err(Error::OperationFailed(
> +                OperationType::EdgeEventCopy,
> +                Errno::last(),
> +            ));
> +        }
> +
> +        Ok(Self {
> +            buffer: None,
> +            event,
> +        })
> +    }
> +
> +    /// Get the event type.
> +    pub fn event_type(&self) -> Result<EdgeKind> {
> +        EdgeKind::new(unsafe { gpiod::gpiod_edge_event_get_event_type(self.event) } as u32)
> +    }
> +
> +    /// Get the timestamp of the event.
> +    pub fn timestamp(&self) -> Duration {
> +        Duration::from_nanos(unsafe { gpiod::gpiod_edge_event_get_timestamp_ns(self.event) })
> +    }
> +
> +    /// Get the offset of the line on which the event was triggered.
> +    pub fn line_offset(&self) -> Offset {
> +        unsafe { gpiod::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 global_seqno(&self) -> u64 {
> +        unsafe { gpiod::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 line_seqno(&self) -> u64 {
> +        unsafe { gpiod::gpiod_edge_event_get_line_seqno(self.event) }
> +    }
> +}
> +
> +impl<'b> Drop for Event<'b> {
> +    /// Free the edge event.
> +    fn drop(&mut self) {
> +        // Free the event only if a copy is made
> +        if self.buffer.is_none() {
> +            unsafe { gpiod::gpiod_edge_event_free(self.event) };
> +        }
> +    }
> +}
> diff --git a/bindings/rust/libgpiod/src/event_buffer.rs b/bindings/rust/libgpiod/src/event_buffer.rs
> new file mode 100644
> index 000000000000..11c8b5e1d7c9
> --- /dev/null
> +++ b/bindings/rust/libgpiod/src/event_buffer.rs
> @@ -0,0 +1,66 @@
> +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
> +//
> +// Copyright 2022 Linaro Ltd. All Rights Reserved.
> +//     Viresh Kumar <viresh.kumar@linaro.org>
> +
> +use std::os::raw::c_ulong;
> +
> +use vmm_sys_util::errno::Error as Errno;
> +
> +use super::{edge, gpiod, Error, OperationType, Result};
> +
> +/// Line edge events buffer
> +#[derive(Clone, Debug, Eq, PartialEq)]
> +pub struct Buffer {
> +    buffer: *mut gpiod::gpiod_edge_event_buffer,
> +}

Clone/Drop wrapping a raw pointer again.

> +
> +impl Buffer {
> +    /// 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: usize) -> Result<Self> {
> +        let buffer = unsafe { gpiod::gpiod_edge_event_buffer_new(capacity as c_ulong) };
> +        if buffer.is_null() {
> +            return Err(Error::OperationFailed(
> +                OperationType::EdgeEventBufferNew,
> +                Errno::last(),
> +            ));
> +        }
> +
> +        Ok(Self { buffer })
> +    }
> +
> +    /// Private helper, Returns gpiod_edge_event_buffer
> +    pub(crate) fn buffer(&self) -> *mut gpiod::gpiod_edge_event_buffer {
> +        self.buffer
> +    }

Just make self.buffer pub(crate)?  Why is the idea of passing a raw
pointer around making my skin crawl?
Or even better, move the guts of
line_request::Request::read_edge_events() into a helper function here
that it can call so it doesn't need access to self.buffer.

> +
> +    /// Get the capacity of the event buffer.
> +    pub fn capacity(&self) -> usize {
> +        unsafe { gpiod::gpiod_edge_event_buffer_get_capacity(self.buffer()) as usize }
> +    }
> +
> +    /// Read an event stored in the buffer.
> +    pub fn event(&self, index: u64) -> Result<edge::Event> {
> +        edge::Event::new(self, index)
> +    }

What is to prevent reading an event from a slot that has not yet been
populated?  I realise doing that is possible in C, but it should be
prevented in the Rust bindings.

> +
> +    /// Get the number of events the buffer contains.
> +    pub fn len(&self) -> usize {
> +        unsafe { gpiod::gpiod_edge_event_buffer_get_num_events(self.buffer()) as usize }
> +    }
> +
> +    /// Check if buffer is empty.
> +    pub fn is_empty(&self) -> bool {
> +        self.len() == 0
> +    }
> +}
> +
> +impl Drop for Buffer {
> +    /// Free the edge event buffer and release all associated resources.
> +    fn drop(&mut self) {
> +        unsafe { gpiod::gpiod_edge_event_buffer_free(self.buffer) };
> +    }
> +}
> diff --git a/bindings/rust/libgpiod/src/info_event.rs b/bindings/rust/libgpiod/src/info_event.rs
> new file mode 100644
> index 000000000000..d8be87df6679
> --- /dev/null
> +++ b/bindings/rust/libgpiod/src/info_event.rs
> @@ -0,0 +1,68 @@
> +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
> +//
> +// Copyright 2022 Linaro Ltd. All Rights Reserved.
> +//     Viresh Kumar <viresh.kumar@linaro.org>
> +
> +use std::sync::Arc;
> +use std::time::Duration;
> +
> +use vmm_sys_util::errno::Error as Errno;
> +
> +use super::{chip, gpiod, line, Error, InfoChangeKind, OperationType, 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.
> +
> +#[derive(Clone, Debug, Eq, PartialEq)]
> +pub struct Event {
> +    event: *mut gpiod::gpiod_info_event,
> +}
> +

Clone/Drop wrapping a raw pointer again.

> +impl Event {
> +    /// Get a single chip's line's status change event.
> +    pub(crate) fn new(ichip: &Arc<chip::Internal>) -> Result<Self> {
> +        let event = unsafe { gpiod::gpiod_chip_read_info_event(ichip.chip()) };
> +        if event.is_null() {
> +            return Err(Error::OperationFailed(
> +                OperationType::ChipReadInfoEvent,
> +                Errno::last(),
> +            ));
> +        }
> +
> +        Ok(Self { event })
> +    }

The new() should be:

    pub(crate) fn new(event: *mut gpiod::gpiod_info_event) -> Self {
        Self {event}
    }

The guts should be in Chip - where this new() is called.

> +
> +    /// Private helper, Returns gpiod_info_event
> +    pub(crate) fn event(&self) -> *mut gpiod::gpiod_info_event {
> +        self.event
> +    }
> +
> +    /// Get the event type of the status change event.
> +    pub fn event_type(&self) -> Result<InfoChangeKind> {
> +        InfoChangeKind::new(unsafe { gpiod::gpiod_info_event_get_event_type(self.event) } as u32)
> +    }
> +
> +    /// Get the timestamp of the event, read from the monotonic clock.
> +    pub fn timestamp(&self) -> Duration {
> +        Duration::from_nanos(unsafe { gpiod::gpiod_info_event_get_timestamp_ns(self.event) })
> +    }
> +
> +    /// Get the line-info object associated with the event.
> +    pub fn line_info(&self) -> Result<line::Info> {
> +        line::Info::new_from_event(self)
> +    }
> +}
> +
> +impl Drop for Event {
> +    /// Free the info event object and release all associated resources.
> +    fn drop(&mut self) {
> +        unsafe { gpiod::gpiod_info_event_free(self.event) }
> +    }
> +}
> diff --git a/bindings/rust/libgpiod/src/lib.rs b/bindings/rust/libgpiod/src/lib.rs
> new file mode 100644
> index 000000000000..c5d974a9f4ea
> --- /dev/null
> +++ b/bindings/rust/libgpiod/src/lib.rs
> @@ -0,0 +1,471 @@
> +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
> +//
> +// Rust wrappers for GPIOD APIs
> +//
> +// Copyright 2022 Linaro Ltd. All Rights Reserved.
> +//     Viresh Kumar <viresh.kumar@linaro.org>
> +
> +//! 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.
> +
> +/// GPIO chip related definitions.
> +pub mod chip;
> +
> +mod edge_event;
> +mod event_buffer;
> +
> +/// GPIO chip edge event related definitions.
             ^
            line

> +pub mod edge {
> +    pub use crate::edge_event::*;
> +
> +    /// GPIO chip edge event buffer related definitions.
> +    pub mod event {
> +        pub use crate::event_buffer::*;
> +    }
> +}
> +

Not sure why event_buffer needs to be nested inside an event module.
event_buffer should be in the request module.

edge_event is a tougher call, either line or request.  Probably the
latter.

> +mod info_event;
> +
> +/// GPIO chip info event related definitions.
> +pub mod info {
> +    pub use crate::info_event::*;
> +}
> +

Why is this a top level module, not part of chip?

> +mod line_config;
> +mod line_info;
> +mod line_request;
> +mod line_settings;
> +
> +/// GPIO chip line related definitions.
> +pub mod line {
> +    pub use crate::line_config::*;
> +    pub use crate::line_info::*;
> +    pub use crate::line_request::*;
> +    pub use crate::line_settings::*;
> +}

The line_request should be in the request module?

> +
> +mod request_config;
> +
> +/// GPIO chip request related definitions.
> +pub mod request {
> +    pub use crate::request_config::*;
> +}
> +
> +use libgpiod_sys as gpiod;
> +
> +use intmap::IntMap;
> +use libc::strlen;
> +use std::fs;
> +use std::os::raw::c_char;
> +use std::path::Path;
> +use std::time::Duration;
> +use std::{fmt, slice, str};
> +
> +use thiserror::Error as ThisError;
> +use vmm_sys_util::errno::Error as Errno;
> +
> +/// Operation types, used with OperationFailed() Error.
> +#[derive(Copy, Clone, Debug, Eq, PartialEq)]
> +pub enum OperationType {
> +    ChipOpen,
> +    ChipGetFd,
> +    ChipWaitInfoEvent,
> +    ChipGetLine,
> +    ChipGetLineInfo,
> +    ChipInfoGet,
> +    ChipReadInfoEvent,
> +    ChipWatchLineInfo,
> +    EdgeEventBufferGetEvent,
> +    EdgeEventCopy,
> +    EdgeEventBufferNew,
> +    InfoEventGetLineInfo,
> +    LineConfigNew,
> +    LineConfigAddSettings,
> +    LineConfigGetOffsets,
> +    LineConfigGetSetting,
> +    LineRequest,
> +    LineRequestReconfigLines,
> +    LineRequestGetVal,
> +    LineRequestGetValSubset,
> +    LineRequestSetVal,
> +    LineRequestSetValSubset,
> +    LineRequestReadEdgeEvent,
> +    LineRequestWaitEdgeEvent,
> +    LineSettingsNew,
> +    LineSettingsCopy,
> +    LineSettingsGetOutVal,
> +    LineSettingsSetDirection,
> +    LineSettingsSetEdgeDetection,
> +    LineSettingsSetBias,
> +    LineSettingsSetDrive,
> +    LineSettingsSetActiveLow,
> +    LineSettingsSetDebouncePeriod,
> +    LineSettingsSetEventClock,
> +    LineSettingsSetOutputValue,
> +    RequestConfigNew,
> +    RequestConfigGetConsumer,
> +    SimBankGetVal,
> +    SimBankNew,
> +    SimBankSetLabel,
> +    SimBankSetNumLines,
> +    SimBankSetLineName,
> +    SimBankSetPull,
> +    SimBankHogLine,
> +    SimCtxNew,
> +    SimDevNew,
> +    SimDevEnable,
> +    SimDevDisable,
> +}
> +
> +impl fmt::Display for OperationType {
> +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
> +        write!(f, "{:?}", self)
> +    }
> +}
> +
> +/// 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 get {0}")]
> +    NullString(&'static str),
> +    #[error("String not utf8: {0:?}")]
> +    StringNotUtf8(str::Utf8Error),
> +    #[error("Invalid enum {0} value: {1}")]
> +    InvalidEnumValue(&'static str, u32),
> +    #[error("Operation {0} Failed: {1}")]
> +    OperationFailed(OperationType, Errno),
> +    #[error("Invalid Arguments")]
> +    InvalidArguments,
> +    #[error("Std Io Error")]
> +    IoError,
> +}
> +

The remaining types in this file look specific to lines and should be in
the line module?

> +/// Value settings.
> +#[derive(Copy, Clone, Debug, Eq, PartialEq)]
> +pub enum Value {
> +    /// Active
> +    Active,
> +    /// Inactive
> +    InActive,
> +}
> +
> +/// Maps offset to Value.
> +pub type ValueMap = IntMap<Value>;
> +
> +impl Value {
> +    pub fn new(val: i32) -> Result<Self> {
> +        match val {
> +            0 => Ok(Value::InActive),
> +            1 => Ok(Value::Active),
> +            _ => Err(Error::InvalidEnumValue("Value", val as u32)),
> +        }
> +    }

Move the Ok outside the match, and return the Err immediately.
Not a biggy here, but where there are more variants it makes it easier
to read, if nothing else.

> +
> +    fn value(&self) -> i32 {
> +        match self {
> +            Value::Active => 1,
> +            Value::InActive => 0,
> +        }
> +    }
> +}
> +
> +/// Offset type.
> +pub type Offset = u32;
> +
> +/// Direction settings.
> +#[derive(Copy, Clone, Debug, Eq, PartialEq)]
> +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 {
> +            gpiod::GPIOD_LINE_DIRECTION_AS_IS => Ok(Direction::AsIs),
> +            gpiod::GPIOD_LINE_DIRECTION_INPUT => Ok(Direction::Input),
> +            gpiod::GPIOD_LINE_DIRECTION_OUTPUT => Ok(Direction::Output),
> +            _ => Err(Error::InvalidEnumValue("Direction", dir)),
> +        }
> +    }
> +
> +    fn gpiod_direction(&self) -> u32 {
> +        match self {
> +            Direction::AsIs => gpiod::GPIOD_LINE_DIRECTION_AS_IS,
> +            Direction::Input => gpiod::GPIOD_LINE_DIRECTION_INPUT,
> +            Direction::Output => gpiod::GPIOD_LINE_DIRECTION_OUTPUT,
> +        }
> +    }
> +}
> +
> +/// Internal bias settings.
> +#[derive(Copy, Clone, Debug, Eq, PartialEq)]
> +pub enum Bias {
> +    /// The internal bias is disabled.
> +    Disabled,
> +    /// The internal pull-up bias is enabled.
> +    PullUp,
> +    /// The internal pull-down bias is enabled.
> +    PullDown,
> +}
> +
> +impl Bias {
> +    fn new(bias: u32) -> Result<Option<Self>> {
> +        match bias {
> +            gpiod::GPIOD_LINE_BIAS_UNKNOWN => Ok(None),
> +            gpiod::GPIOD_LINE_BIAS_AS_IS => Ok(None),
> +            gpiod::GPIOD_LINE_BIAS_DISABLED => Ok(Some(Bias::Disabled)),
> +            gpiod::GPIOD_LINE_BIAS_PULL_UP => Ok(Some(Bias::PullUp)),
> +            gpiod::GPIOD_LINE_BIAS_PULL_DOWN => Ok(Some(Bias::PullDown)),
> +            _ => Err(Error::InvalidEnumValue("Bias", bias)),
> +        }
> +    }
> +
> +    fn gpiod_bias(bias: Option<Bias>) -> u32 {
> +        match bias {
> +            None => gpiod::GPIOD_LINE_BIAS_AS_IS,
> +            Some(bias) => match bias {
> +                Bias::Disabled => gpiod::GPIOD_LINE_BIAS_DISABLED,
> +                Bias::PullUp => gpiod::GPIOD_LINE_BIAS_PULL_UP,
> +                Bias::PullDown => gpiod::GPIOD_LINE_BIAS_PULL_DOWN,
> +            },
> +        }
> +    }
> +}
> +
> +/// Drive settings.
> +#[derive(Copy, Clone, Debug, Eq, PartialEq)]
> +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 {
> +            gpiod::GPIOD_LINE_DRIVE_PUSH_PULL => Ok(Drive::PushPull),
> +            gpiod::GPIOD_LINE_DRIVE_OPEN_DRAIN => Ok(Drive::OpenDrain),
> +            gpiod::GPIOD_LINE_DRIVE_OPEN_SOURCE => Ok(Drive::OpenSource),
> +            _ => Err(Error::InvalidEnumValue("Drive", drive)),
> +        }
> +    }
> +
> +    fn gpiod_drive(&self) -> u32 {
> +        match self {
> +            Drive::PushPull => gpiod::GPIOD_LINE_DRIVE_PUSH_PULL,
> +            Drive::OpenDrain => gpiod::GPIOD_LINE_DRIVE_OPEN_DRAIN,
> +            Drive::OpenSource => gpiod::GPIOD_LINE_DRIVE_OPEN_SOURCE,
> +        }
> +    }
> +}
> +
> +/// Edge detection settings.
> +#[derive(Copy, Clone, Debug, Eq, PartialEq)]
> +pub enum Edge {
> +    /// Line detects rising edge events.
> +    Rising,
> +    /// Line detects falling edge events.
> +    Falling,
> +    /// Line detects both rising and falling edge events.
> +    Both,
> +}
> +
> +impl Edge {
> +    fn new(edge: u32) -> Result<Option<Self>> {
> +        match edge {
> +            gpiod::GPIOD_LINE_EDGE_NONE => Ok(None),
> +            gpiod::GPIOD_LINE_EDGE_RISING => Ok(Some(Edge::Rising)),
> +            gpiod::GPIOD_LINE_EDGE_FALLING => Ok(Some(Edge::Falling)),
> +            gpiod::GPIOD_LINE_EDGE_BOTH => Ok(Some(Edge::Both)),
> +            _ => Err(Error::InvalidEnumValue("Edge", edge)),
> +        }
> +    }
> +
> +    fn gpiod_edge(edge: Option<Edge>) -> u32 {
> +        match edge {
> +            None => gpiod::GPIOD_LINE_EDGE_NONE,
> +            Some(edge) => match edge {
> +                Edge::Rising => gpiod::GPIOD_LINE_EDGE_RISING,
> +                Edge::Falling => gpiod::GPIOD_LINE_EDGE_FALLING,
> +                Edge::Both => gpiod::GPIOD_LINE_EDGE_BOTH,
> +            },
> +        }
> +    }
> +}
> +
> +/// Line setting kind.
> +#[derive(Copy, Clone, Debug, Eq, PartialEq)]
> +pub enum SettingKind {
> +    /// Line direction.
> +    Direction,
> +    /// Bias.
> +    Bias,
> +    /// Drive.
> +    Drive,
> +    /// Edge detection.
> +    EdgeDetection,
> +    /// Active-low setting.
> +    ActiveLow,
> +    /// Debounce period.
> +    DebouncePeriod,
> +    /// Event clock type.
> +    EventClock,
> +    /// Output value.
> +    OutputValue,
> +}
> +
> +/// Line settings.
> +#[derive(Copy, Clone, Debug, Eq, PartialEq)]
> +pub enum SettingVal {
> +    /// Line direction.
> +    Direction(Direction),
> +    /// Bias.
> +    Bias(Option<Bias>),
> +    /// Drive.
> +    Drive(Drive),
> +    /// Edge detection.
> +    EdgeDetection(Option<Edge>),
> +    /// Active-low setting.
> +    ActiveLow(bool),
> +    /// Debounce period.
> +    DebouncePeriod(Duration),
> +    /// Event clock type.
> +    EventClock(EventClock),
> +    /// Output value.
> +    OutputValue(Value),
> +}
> +
> +impl fmt::Display for SettingVal {
> +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
> +        write!(f, "{:?}", self)
> +    }
> +}
> +
> +/// Event clock settings.
> +#[derive(Copy, Clone, Debug, Eq, PartialEq)]
> +pub enum EventClock {
> +    /// Line uses the monotonic clock for edge event timestamps.
> +    Monotonic,
> +    /// Line uses the realtime clock for edge event timestamps.
> +    Realtime,
> +    /// Line uses the hardware timestamp engine clock for edge event timestamps.
> +    HTE,
> +}
> +
> +impl EventClock {
> +    fn new(clock: u32) -> Result<Self> {
> +        match clock {
> +            gpiod::GPIOD_LINE_EVENT_CLOCK_MONOTONIC => Ok(EventClock::Monotonic),
> +            gpiod::GPIOD_LINE_EVENT_CLOCK_REALTIME => Ok(EventClock::Realtime),
> +            gpiod::GPIOD_LINE_EVENT_CLOCK_HTE => Ok(EventClock::HTE),
> +            _ => Err(Error::InvalidEnumValue("Eventclock", clock)),
> +        }
> +    }
> +
> +    fn gpiod_clock(&self) -> u32 {
> +        match self {
> +            EventClock::Monotonic => gpiod::GPIOD_LINE_EVENT_CLOCK_MONOTONIC,
> +            EventClock::Realtime => gpiod::GPIOD_LINE_EVENT_CLOCK_REALTIME,
> +            EventClock::HTE => gpiod::GPIOD_LINE_EVENT_CLOCK_HTE,
> +        }
> +    }
> +}
> +
> +/// Line status change event types.
> +#[derive(Copy, Clone, Debug, Eq, PartialEq)]
> +pub enum InfoChangeKind {
> +    /// Line has been requested.
> +    LineRequested,
> +    /// Previously requested line has been released.
> +    LineReleased,
> +    /// Line configuration has changed.
> +    LineConfigChanged,
> +}
> +
> +impl InfoChangeKind {
> +    fn new(kind: u32) -> Result<Self> {
> +        match kind {
> +            gpiod::GPIOD_INFO_EVENT_LINE_REQUESTED => Ok(InfoChangeKind::LineRequested),
> +            gpiod::GPIOD_INFO_EVENT_LINE_RELEASED => Ok(InfoChangeKind::LineReleased),
> +            gpiod::GPIOD_INFO_EVENT_LINE_CONFIG_CHANGED => Ok(InfoChangeKind::LineConfigChanged),
> +            _ => Err(Error::InvalidEnumValue("InfoChangeKind", kind)),
> +        }
> +    }
> +}
> +
> +/// Edge event types.
> +#[derive(Copy, Clone, Debug, Eq, PartialEq)]
> +pub enum EdgeKind {
> +    /// Rising edge event.
> +    Rising,
> +    /// Falling edge event.
> +    Falling,
> +}
> +
> +impl EdgeKind {
> +    fn new(kind: u32) -> Result<Self> {
> +        match kind {
> +            gpiod::GPIOD_EDGE_EVENT_RISING_EDGE => Ok(EdgeKind::Rising),
> +            gpiod::GPIOD_EDGE_EVENT_FALLING_EDGE => Ok(EdgeKind::Falling),
> +            _ => Err(Error::InvalidEnumValue("EdgeEvent", kind)),
> +        }
> +    }
> +}
> +
> +/// Various libgpiod-related functions.
> +
> +/// Check if the file pointed to by path is a GPIO chip character device.
> +///
> +/// Returns true if the file exists and is a GPIO chip character device or a
> +/// symbolic link to it.
> +pub fn is_gpiochip_device<P: AsRef<Path>>(path: &P) -> bool {
> +    // Null-terminate the string
> +    let path = path.as_ref().to_string_lossy() + "\0";
> +
> +    unsafe { gpiod::gpiod_is_gpiochip_device(path.as_ptr() as *const c_char) }
> +}
> +
> +/// Iterator for GPIO devices.
> +pub fn gpiochip_devices<P: AsRef<Path>>(path: &P) -> Result<Vec<chip::Chip>> {
> +    let mut chips = Vec::new();
> +
> +    for entry in fs::read_dir(path).map_err(|_| Error::IoError)?.flatten() {
> +        let path = entry.path();
> +
> +        if is_gpiochip_device(&path) {
> +            chips.push(chip::Chip::open(&path)?);
> +        }
> +    }

If there is a symlink to a device both in the path, or multiple symlinks
to the same device, you will get the same chip several times in the
returned Vec?

Some mention of the order in which devices are returned?
If you are returning a Vec then sorted might be nice, else state they
are not sorted.

And strictly speaking a Vec is not an iterator. It can be iterated over,
but Vec.iter() is its iterator.

> +
> +    Ok(chips)
> +}
> +
> +/// Get the API version of the library as a human-readable string.
> +pub fn version_string() -> Result<&'static str> {
> +    // SAFETY: The string returned by libgpiod is guaranteed to live forever.
> +    let version = unsafe { gpiod::gpiod_version_string() };
> +
> +    if version.is_null() {
> +        return Err(Error::NullString("GPIO library version"));
> +    }
> +
> +    // SAFETY: The string is guaranteed to be valid here by the C API.
> +    str::from_utf8(unsafe { slice::from_raw_parts(version as *const u8, strlen(version) as usize) })
> +        .map_err(Error::StringNotUtf8)
> +}

What if the Rust bindings version differ from the libgpiod version?

> diff --git a/bindings/rust/libgpiod/src/line_config.rs b/bindings/rust/libgpiod/src/line_config.rs
> new file mode 100644
> index 000000000000..ea25452fd1d6
> --- /dev/null
> +++ b/bindings/rust/libgpiod/src/line_config.rs
> @@ -0,0 +1,118 @@
> +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
> +//
> +// Copyright 2022 Linaro Ltd. All Rights Reserved.
> +//     Viresh Kumar <viresh.kumar@linaro.org>
> +
> +use std::os::raw::c_ulong;
> +use std::slice::from_raw_parts;
> +
> +use vmm_sys_util::errno::Error as Errno;
> +
> +use super::{gpiod, line::Settings, Error, Offset, OperationType, 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 empty. Using it in a request will lead to an
> +/// error. In order to a line-config to become useful, it needs to be assigned
                       ^
                      for

> +/// at least one offset-to-settings mapping by calling
> +/// ::gpiod_line_config_add_line_settings.
> +///
> +/// When calling ::gpiod_chip_request_lines, the library will request all
> +/// offsets that were assigned settings in the order that they were assigned.
> +
> +#[derive(Clone, Debug, Eq, PartialEq)]
> +pub struct Config {
> +    config: *mut gpiod::gpiod_line_config,
> +}
> +

Clone/Drop wrapping a raw pointer again.

> +impl Config {
> +    /// Create a new line config object.
> +    pub fn new() -> Result<Self> {
> +        let config = unsafe { gpiod::gpiod_line_config_new() };
> +
> +        if config.is_null() {
> +            return Err(Error::OperationFailed(
> +                OperationType::LineConfigNew,
> +                Errno::last(),
> +            ));
> +        }
> +
> +        Ok(Self { config })
> +    }
> +
> +    /// Private helper, Returns gpiod_line_config
> +    pub(crate) fn config(&self) -> *mut gpiod::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 { gpiod::gpiod_line_config_reset(self.config) }
> +    }
> +
> +    /// Add line settings for a set of offsets.
> +    pub fn add_line_settings(&self, offsets: &[Offset], settings: Settings) -> Result<()> {
> +        let ret = unsafe {
> +            gpiod::gpiod_line_config_add_line_settings(
> +                self.config,
> +                offsets.as_ptr(),
> +                offsets.len() as c_ulong,
> +                settings.settings(),
> +            )
> +        };
> +
> +        if ret == -1 {
> +            Err(Error::OperationFailed(
> +                OperationType::LineConfigAddSettings,
> +                Errno::last(),
> +            ))
> +        } else {
> +            Ok(())
> +        }
> +    }
> +
> +    /// Get line settings for offset.
> +    pub fn line_settings(&self, offset: Offset) -> Result<Settings> {
> +        let settings = unsafe { gpiod::gpiod_line_config_get_line_settings(self.config, offset) };
> +
> +        if settings.is_null() {
> +            return Err(Error::OperationFailed(
> +                OperationType::LineConfigGetSetting,
                                                      ^
                                               Settings

> +                Errno::last(),
> +            ));
> +        }
> +
> +        Ok(Settings::new_with_settings(settings))
> +    }
> +
> +    /// Get configured offsets.
> +    pub fn offsets(&self) -> Result<Vec<Offset>> {
> +        let mut num: u64 = 0;
> +        let mut offsets: *mut Offset = std::ptr::null_mut();
> +
> +        let ret =
> +            unsafe { gpiod::gpiod_line_config_get_offsets(self.config, &mut num, &mut offsets) };
> +
> +        if ret == -1 {
> +            Err(Error::OperationFailed(
> +                OperationType::LineConfigGetOffsets,
> +                Errno::last(),
> +            ))
> +        } else {
> +            Ok(unsafe { from_raw_parts(offsets as *const Offset, num as usize).to_vec() })
> +        }
> +    }
> +}
> +
> +impl Drop for Config {
> +    /// Free the line config object and release all associated resources.
> +    fn drop(&mut self) {
> +        unsafe { gpiod::gpiod_line_config_free(self.config) }
> +    }
> +}
> diff --git a/bindings/rust/libgpiod/src/line_info.rs b/bindings/rust/libgpiod/src/line_info.rs
> new file mode 100644
> index 000000000000..9db51fc30efd
> --- /dev/null
> +++ b/bindings/rust/libgpiod/src/line_info.rs
> @@ -0,0 +1,180 @@
> +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
> +//
> +// Copyright 2022 Linaro Ltd. All Rights Reserved.
> +//     Viresh Kumar <viresh.kumar@linaro.org>
> +
> +use libc::strlen;
> +use std::sync::Arc;
> +use std::time::Duration;
> +use std::{slice, str};
> +
> +use vmm_sys_util::errno::Error as Errno;
> +
> +use super::{
> +    chip, gpiod, info, Bias, Direction, Drive, Edge, Error, EventClock, Offset, OperationType,
> +    Result,
> +};
> +
> +/// 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.
> +
> +#[derive(Clone, Debug, Eq, PartialEq)]
> +pub struct Info {
> +    info: *mut gpiod::gpiod_line_info,
> +    info_event: bool,
> +}
> +

Clone/Drop wrapping a raw pointer again.

> +impl Info {
> +    /// Get a snapshot of information about the line and optionally start watching it for changes.
> +    pub(crate) fn new(ichip: Arc<chip::Internal>, offset: Offset) -> Result<Self> {
> +        let info = unsafe { gpiod::gpiod_chip_get_line_info(ichip.chip(), offset) };
> +
> +        if info.is_null() {
> +            return Err(Error::OperationFailed(
> +                OperationType::ChipGetLineInfo,
> +                Errno::last(),
> +            ));
> +        }
> +
> +        Ok(Self {
> +            info,
> +            info_event: false,
> +        })
> +    }

Move the guts into chip where this is called.  This should just be a
bare contructor:

pub(crate) fn new(info: *mut gpiod::gpiod_line_info, free_me_seymour: bool)

> +
> +    pub(crate) fn new_watch(ichip: Arc<chip::Internal>, offset: Offset) -> Result<Self> {
> +        let info = unsafe { gpiod::gpiod_chip_watch_line_info(ichip.chip(), offset) };
> +
> +        if info.is_null() {
> +            return Err(Error::OperationFailed(
> +                OperationType::ChipWatchLineInfo,
> +                Errno::last(),
> +            ));
> +        }
> +
> +        Ok(Self {
> +            info,
> +            info_event: false,
> +        })
> +    }

Move the guts into chip where this is called, and call the new() above.

> +
> +    /// Get the Line info object associated with an event.
> +    pub(crate) fn new_from_event(event: &info::Event) -> Result<Self> {
> +        let info = unsafe { gpiod::gpiod_info_event_get_line_info(event.event()) };
> +
> +        if info.is_null() {
> +            return Err(Error::OperationFailed(
> +                OperationType::InfoEventGetLineInfo,
> +                Errno::last(),
> +            ));
> +        }
> +
> +        Ok(Self {
> +            info,
> +            info_event: true,
> +        })
> +    }

Move the guts into info_events where this is called, and call the new()
above.

> +
> +    /// 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 offset(&self) -> Offset {
> +        unsafe { gpiod::gpiod_line_info_get_offset(self.info) }
> +    }
> +
> +    /// Get GPIO line's name.
> +    pub fn name(&self) -> Result<&str> {
> +        // SAFETY: The string returned by libgpiod is guaranteed to live as long
> +        // as the `struct Info`.
> +        let name = unsafe { gpiod::gpiod_line_info_get_name(self.info) };
> +        if name.is_null() {
> +            return Err(Error::NullString("GPIO line's name"));
> +        }
> +
> +        // SAFETY: The string is guaranteed to be valid here by the C API.
> +        str::from_utf8(unsafe { slice::from_raw_parts(name as *const u8, strlen(name) as usize) })
> +            .map_err(Error::StringNotUtf8)
> +    }
> +
> +    /// 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 { gpiod::gpiod_line_info_is_used(self.info) }
> +    }
> +
> +    /// Get the GPIO line's consumer name.
> +    pub fn consumer(&self) -> Result<&str> {
> +        // SAFETY: The string returned by libgpiod is guaranteed to live as long
> +        // as the `struct Info`.
> +        let name = unsafe { gpiod::gpiod_line_info_get_consumer(self.info) };
> +        if name.is_null() {
> +            return Err(Error::NullString("GPIO line's consumer name"));
> +        }
> +
> +        // SAFETY: The string is guaranteed to be valid here by the C API.
> +        str::from_utf8(unsafe { slice::from_raw_parts(name as *const u8, strlen(name) as usize) })
> +            .map_err(Error::StringNotUtf8)
> +    }
> +
> +    /// Get the GPIO line's direction.
> +    pub fn direction(&self) -> Result<Direction> {
> +        Direction::new(unsafe { gpiod::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 { gpiod::gpiod_line_info_is_active_low(self.info) }
> +    }
> +
> +    /// Get the GPIO line's bias setting.
> +    pub fn bias(&self) -> Result<Option<Bias>> {
> +        Bias::new(unsafe { gpiod::gpiod_line_info_get_bias(self.info) } as u32)
> +    }
> +
> +    /// Get the GPIO line's drive setting.
> +    pub fn drive(&self) -> Result<Drive> {
> +        Drive::new(unsafe { gpiod::gpiod_line_info_get_drive(self.info) } as u32)
> +    }
> +
> +    /// Get the current edge detection setting of the line.
> +    pub fn edge_detection(&self) -> Result<Option<Edge>> {
> +        Edge::new(unsafe { gpiod::gpiod_line_info_get_edge_detection(self.info) } as u32)
> +    }
> +
> +    /// Get the current event clock setting used for edge event timestamps.
> +    pub fn event_clock(&self) -> Result<EventClock> {
> +        EventClock::new(unsafe { gpiod::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 { gpiod::gpiod_line_info_is_debounced(self.info) }
> +    }
> +
> +    /// Get the debounce period of the line.
> +    pub fn debounce_period(&self) -> Duration {
> +        Duration::from_micros(unsafe { gpiod::gpiod_line_info_get_debounce_period_us(self.info) })
> +    }
> +}
> +
> +impl Drop for Info {
> +    fn drop(&mut self) {
> +        // We must not free the Line info object created from `struct info::Event` by calling
> +        // libgpiod API.
> +        if !self.info_event {
> +            unsafe { gpiod::gpiod_line_info_free(self.info) }
> +        }
> +    }
> +}
> diff --git a/bindings/rust/libgpiod/src/line_request.rs b/bindings/rust/libgpiod/src/line_request.rs
> new file mode 100644
> index 000000000000..e4ff951aef29
> --- /dev/null
> +++ b/bindings/rust/libgpiod/src/line_request.rs
> @@ -0,0 +1,246 @@
> +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
> +//
> +// Copyright 2022 Linaro Ltd. All Rights Reserved.
> +//     Viresh Kumar <viresh.kumar@linaro.org>
> +
> +use libc::EINVAL;
> +use std::os::raw::c_ulong;
> +use std::sync::Arc;
> +use std::time::Duration;
> +
> +use vmm_sys_util::errno::Error as Errno;
> +
> +use super::{
> +    chip, edge, gpiod, line, request, Error, Offset, OperationType, Result, Value, ValueMap,
> +};
> +
> +/// Line request operations
> +///
> +/// Allows interaction with a set of requested lines.
> +#[derive(Clone, Debug, Eq, PartialEq)]
> +pub struct Request {
> +    request: *mut gpiod::gpiod_line_request,
> +}
> +

Clone/Drop wrapping a raw pointer again.

Does it make sense to be able to clone a request?
As per Chip, concerns being about reading events concurrently, as well
as the freeing of the underlying resources.

> +impl Request {
> +    /// Request a set of lines for exclusive usage.
> +    pub(crate) fn new(
> +        ichip: &Arc<chip::Internal>,
> +        rconfig: &request::Config,
> +        lconfig: &line::Config,
> +    ) -> Result<Self> {
> +        let request = unsafe {
> +            gpiod::gpiod_chip_request_lines(ichip.chip(), rconfig.config(), lconfig.config())
> +        };
> +
> +        if request.is_null() {
> +            return Err(Error::OperationFailed(
> +                OperationType::LineRequest,
                                     ^
                                ChipRequestLines

> +                Errno::last(),
> +            ));
> +        }
> +
> +        Ok(Self { request })
> +    }

As with other constructors, this should be a bare constructor, and the
guts should be in Chip where this function is called.

> +
> +    /// Get the number of lines in the request.
> +    pub fn num_lines(&self) -> usize {
> +        unsafe { gpiod::gpiod_line_request_get_num_lines(self.request) as usize }
> +    }
> +
> +    /// Get the offsets of lines in the request.
> +    pub fn offsets(&self) -> Vec<Offset> {
> +        let mut offsets = vec![0; self.num_lines() as usize];
> +
> +        unsafe { gpiod::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 value(&self, offset: Offset) -> Result<Value> {
> +        let value = unsafe { gpiod::gpiod_line_request_get_value(self.request, offset) };
> +
> +        if value != 0 && value != 1 {
> +            Err(Error::OperationFailed(
> +                OperationType::LineRequestGetVal,
> +                Errno::last(),
> +            ))
> +        } else {
> +            Value::new(value)
> +        }
> +    }
> +
> +    /// Get values of a subset of lines associated with the request.
> +    pub fn values_subset(&self, offsets: &[Offset]) -> Result<ValueMap> {
> +        let mut values = vec![0; offsets.len()];
> +
> +        let ret = unsafe {
> +            gpiod::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(
> +                OperationType::LineRequestGetValSubset,
> +                Errno::last(),
> +            ))
> +        } else {
> +            let mut map = ValueMap::new();
> +
> +            for (i, val) in values.iter().enumerate() {
> +                map.insert(offsets[i].into(), Value::new(*val)?);
> +            }
> +
> +            Ok(map)
> +        }
> +    }
> +
> +    /// Get values of all lines associated with the request.
> +    pub fn values(&self) -> Result<ValueMap> {
> +        self.values_subset(&self.offsets())
> +    }
> +
> +    /// Set the value of a single line associated with the request.
> +    pub fn set_value(&self, offset: Offset, value: Value) -> Result<()> {
> +        let ret =
> +            unsafe { gpiod::gpiod_line_request_set_value(self.request, offset, value.value()) };
> +
> +        if ret == -1 {
> +            Err(Error::OperationFailed(
> +                OperationType::LineRequestSetVal,
> +                Errno::last(),
> +            ))
> +        } else {
> +            Ok(())
> +        }
> +    }
> +
> +    /// Get values of a subset of lines associated with the request.
> +    pub fn set_values_subset(&self, map: ValueMap) -> Result<()> {
> +        let mut offsets = Vec::new();
> +        let mut values = Vec::new();
> +
> +        for (offset, value) in map {
> +            offsets.push(offset as u32);
> +            values.push(value.value());
> +        }
> +
> +        let ret = unsafe {
> +            gpiod::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(
> +                OperationType::LineRequestSetValSubset,
> +                Errno::last(),
> +            ))
> +        } else {
> +            Ok(())
> +        }
> +    }
> +
> +    /// Get values of all lines associated with the request.
> +    pub fn set_values(&self, values: &[Value]) -> Result<()> {
> +        if values.len() != self.num_lines() as usize {
> +            return Err(Error::OperationFailed(
> +                OperationType::LineRequestSetVal,
> +                Errno::new(EINVAL),
> +            ));
> +        }

The error implies a C call failed, but that isn't the case.
Make it a plain InvalidArgument?

> +
> +        let mut new_values = Vec::new();
> +        for value in values {
> +            new_values.push(value.value());
> +        }
> +
> +        let ret =
> +            unsafe { gpiod::gpiod_line_request_set_values(self.request, new_values.as_ptr()) };
> +
> +        if ret == -1 {
> +            Err(Error::OperationFailed(
> +                OperationType::LineRequestSetVal,
> +                Errno::last(),
> +            ))
> +        } else {
> +            Ok(())
> +        }
> +    }
> +
> +    /// Update the configuration of lines associated with the line request.
> +    pub fn reconfigure_lines(&self, lconfig: &line::Config) -> Result<()> {
> +        let ret =
> +            unsafe { gpiod::gpiod_line_request_reconfigure_lines(self.request, lconfig.config()) };
> +
> +        if ret == -1 {
> +            Err(Error::OperationFailed(
> +                OperationType::LineRequestReconfigLines,
> +                Errno::last(),
> +            ))
> +        } else {
> +            Ok(())
> +        }
> +    }
> +
> +    /// Get the file descriptor associated with the line request.
> +    pub fn fd(&self) -> u32 {
> +        unsafe { gpiod::gpiod_line_request_get_fd(self.request) as u32 }
> +    }

impl AsRawFd?

> +
> +    /// Wait for edge events on any of the lines associated with the request.
> +    pub fn wait_edge_event(&self, timeout: Option<Duration>) -> Result<bool> {
> +        let timeout = match timeout {
> +            Some(x) => x.as_nanos() as i64,
> +            // Block indefinitely
> +            None => -1,
> +        };
> +
> +        let ret = unsafe { gpiod::gpiod_line_request_wait_edge_event(self.request, timeout) };
> +
> +        match ret {
> +            -1 => Err(Error::OperationFailed(
> +                OperationType::LineRequestWaitEdgeEvent,
> +                Errno::last(),
> +            )),
> +            0 => Ok(false),
> +            _ => Ok(true),
> +        }
> +    }
> +
> +    /// 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_events(&self, buffer: &mut edge::event::Buffer) -> Result<u32> {

What is to stop the user requesting events between the len returned
here and the end of the buffer?  Why knows what state they are in.

Would be better to return an object that can only access the relevant
events in the buffer.

I've got an alternative for the event buffer implementation - will
describe that at the end.

> +        let ret = unsafe {
> +            gpiod::gpiod_line_request_read_edge_event(
> +                self.request,
> +                buffer.buffer(),
> +                buffer.capacity() as u64,
> +            )
> +        };
> +
> +        if ret == -1 {
> +            Err(Error::OperationFailed(
> +                OperationType::LineRequestReadEdgeEvent,
> +                Errno::last(),
> +            ))
> +        } else {
> +            Ok(ret as u32)
> +        }
> +    }
> +}
> +
> +impl Drop for Request {
> +    /// Release the requested lines and free all associated resources.
> +    fn drop(&mut self) {
> +        unsafe { gpiod::gpiod_line_request_release(self.request) }
> +    }
> +}
> diff --git a/bindings/rust/libgpiod/src/line_settings.rs b/bindings/rust/libgpiod/src/line_settings.rs
> new file mode 100644
> index 000000000000..2c3090132ea5
> --- /dev/null
> +++ b/bindings/rust/libgpiod/src/line_settings.rs
> @@ -0,0 +1,277 @@
> +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
> +//
> +// Copyright 2022 Linaro Ltd. All Rights Reserved.
> +//     Viresh Kumar <viresh.kumar@linaro.org>
> +
> +use std::time::Duration;
> +
> +use vmm_sys_util::errno::Error as Errno;
> +
> +use super::{
> +    gpiod, Bias, Direction, Drive, Edge, Error, EventClock, OperationType, Result, SettingKind,
> +    SettingVal, Value,
> +};
> +
> +/// Line settings objects.
> +///
> +/// Line settings object contains a set of line properties that can be used
> +/// when requesting lines or reconfiguring an existing request.
> +///
> +/// Mutators in general can only fail if the new property value is invalid. The
> +/// return values can be safely ignored - the object remains valid even after
> +/// a mutator fails and simply uses the sane default appropriate for given
> +/// property.
> +
> +#[derive(Clone, Debug, Eq, PartialEq)]
> +pub struct Settings {
> +    settings: *mut gpiod::gpiod_line_settings,
> +}
> +

Clone/Drop wrapping a raw pointer again.

> +impl Settings {
> +    /// Create a new line settings object.
> +    pub fn new() -> Result<Self> {
> +        let settings = unsafe { gpiod::gpiod_line_settings_new() };
> +
> +        if settings.is_null() {
> +            return Err(Error::OperationFailed(
> +                OperationType::LineSettingsNew,
> +                Errno::last(),
> +            ));
> +        }
> +
> +        Ok(Self { settings })
> +    }
> +
> +    pub fn new_with_settings(settings: *mut gpiod::gpiod_line_settings) -> Self {
> +        Self { settings }
> +    }
> +
> +    /// Private helper, Returns gpiod_line_settings
> +    pub(crate) fn settings(&self) -> *mut gpiod::gpiod_line_settings {
> +        self.settings
> +    }

Just make self.settings pub(crate)?

> +
> +    /// Resets the line settings object to its default values.
> +    pub fn reset(&mut self) {
> +        unsafe { gpiod::gpiod_line_settings_reset(self.settings) }
> +    }
> +
> +    /// Makes copy of the settings object.
> +    pub fn settings_clone(&self) -> Result<Self> {
> +        let settings = unsafe { gpiod::gpiod_line_settings_copy(self.settings) };
> +        if settings.is_null() {
> +            return Err(Error::OperationFailed(
> +                OperationType::LineSettingsCopy,
> +                Errno::last(),
> +            ));
> +        }
> +
> +        Ok(Self { settings })
> +    }

This clone is fine - the derived one is not.

> +
> +    /// Set line prop setting.
> +    pub fn set_prop(&mut self, values: &[SettingVal]) -> Result<()> {

Rename values to props, particularly as values is one of the props.

> +        for value in values {
> +            match value {
> +                SettingVal::Direction(val) => self.set_direction(*val)?,
> +                SettingVal::EdgeDetection(val) => self.set_edge_detection(*val)?,
> +                SettingVal::Bias(val) => self.set_bias(*val)?,
> +                SettingVal::Drive(val) => self.set_drive(*val)?,
> +                SettingVal::ActiveLow(val) => self.set_active_low(*val),
> +                SettingVal::DebouncePeriod(val) => self.set_debounce_period(*val),
> +                SettingVal::EventClock(val) => self.set_event_clock(*val)?,
> +                SettingVal::OutputValue(val) => self.set_output_value(*val)?,
> +            }
> +        }
> +
> +        Ok(())
> +    }

Make mutators -> Result<&mut Self> so they can be chained.

> +
> +    /// Get the line prop setting.
> +    pub fn prop(&self, property: SettingKind) -> Result<SettingVal> {
> +        Ok(match property {
> +            SettingKind::Direction => SettingVal::Direction(self.direction()?),
> +            SettingKind::EdgeDetection => SettingVal::EdgeDetection(self.edge_detection()?),
> +            SettingKind::Bias => SettingVal::Bias(self.bias()?),
> +            SettingKind::Drive => SettingVal::Drive(self.drive()?),
> +            SettingKind::ActiveLow => SettingVal::ActiveLow(self.active_low()),
> +            SettingKind::DebouncePeriod => SettingVal::DebouncePeriod(self.debounce_period()?),
> +            SettingKind::EventClock => SettingVal::EventClock(self.event_clock()?),
> +            SettingKind::OutputValue => SettingVal::OutputValue(self.output_value()?),
> +        })
> +    }
> +
> +    /// Set the line direction.
> +    fn set_direction(&mut self, direction: Direction) -> Result<()> {
> +        let ret = unsafe {
> +            gpiod::gpiod_line_settings_set_direction(
> +                self.settings,
> +                direction.gpiod_direction() as i32,
> +            )
> +        };
> +
> +        if ret == -1 {
> +            Err(Error::OperationFailed(
> +                OperationType::LineSettingsSetDirection,
> +                Errno::last(),
> +            ))
> +        } else {
> +            Ok(())
> +        }
> +    }

These mutators might as well be pub as well.  Then the caller has the
option of using the set_prop(&[settings]) or chaining mutators.
Similarly the accessors.

> +
> +    /// Get the direction setting.
> +    fn direction(&self) -> Result<Direction> {
> +        Direction::new(unsafe { gpiod::gpiod_line_settings_get_direction(self.settings) } as u32)
> +    }
> +
> +    /// Set the edge event detection setting.
> +    fn set_edge_detection(&mut self, edge: Option<Edge>) -> Result<()> {
> +        let ret = unsafe {
> +            gpiod::gpiod_line_settings_set_edge_detection(
> +                self.settings,
> +                Edge::gpiod_edge(edge) as i32,
> +            )
> +        };
> +
> +        if ret == -1 {
> +            Err(Error::OperationFailed(
> +                OperationType::LineSettingsSetEdgeDetection,
> +                Errno::last(),
> +            ))
> +        } else {
> +            Ok(())
> +        }
> +    }
> +
> +    /// Get the edge event detection setting.
> +    fn edge_detection(&self) -> Result<Option<Edge>> {
> +        Edge::new(unsafe { gpiod::gpiod_line_settings_get_edge_detection(self.settings) } as u32)
> +    }
> +
> +    /// Set the bias setting.
> +    fn set_bias(&mut self, bias: Option<Bias>) -> Result<()> {
> +        let ret = unsafe {
> +            gpiod::gpiod_line_settings_set_bias(self.settings, Bias::gpiod_bias(bias) as i32)
> +        };
> +
> +        if ret == -1 {
> +            Err(Error::OperationFailed(
> +                OperationType::LineSettingsSetBias,
> +                Errno::last(),
> +            ))
> +        } else {
> +            Ok(())
> +        }
> +    }
> +
> +    /// Get the bias setting.
> +    fn bias(&self) -> Result<Option<Bias>> {
> +        Bias::new(unsafe { gpiod::gpiod_line_settings_get_bias(self.settings) } as u32)
> +    }
> +
> +    /// Set the drive setting.
> +    fn set_drive(&mut self, drive: Drive) -> Result<()> {
> +        let ret = unsafe {
> +            gpiod::gpiod_line_settings_set_drive(self.settings, drive.gpiod_drive() as i32)
> +        };
> +
> +        if ret == -1 {
> +            Err(Error::OperationFailed(
> +                OperationType::LineSettingsSetDrive,
> +                Errno::last(),
> +            ))
> +        } else {
> +            Ok(())
> +        }
> +    }
> +
> +    /// Get the drive setting.
> +    fn drive(&self) -> Result<Drive> {
> +        Drive::new(unsafe { gpiod::gpiod_line_settings_get_drive(self.settings) } as u32)
> +    }
> +
> +    /// Set active-low setting.
> +    fn set_active_low(&mut self, active_low: bool) {
> +        unsafe { gpiod::gpiod_line_settings_set_active_low(self.settings, active_low) }
> +    }
> +
> +    /// Check the active-low setting.
> +    fn active_low(&self) -> bool {
> +        unsafe { gpiod::gpiod_line_settings_get_active_low(self.settings) }
> +    }
> +
> +    /// Set the debounce period setting.
> +    fn set_debounce_period(&mut self, period: Duration) {
> +        unsafe {
> +            gpiod::gpiod_line_settings_set_debounce_period_us(
> +                self.settings,
> +                period.as_micros() as u64,
> +            )
> +        }
> +    }
> +
> +    /// Get the debounce period.
> +    fn debounce_period(&self) -> Result<Duration> {
> +        Ok(Duration::from_micros(unsafe {
> +            gpiod::gpiod_line_settings_get_debounce_period_us(self.settings)
> +        }))
> +    }
> +
> +    /// Set the event clock setting.
> +    fn set_event_clock(&mut self, clock: EventClock) -> Result<()> {
> +        let ret = unsafe {
> +            gpiod::gpiod_line_settings_set_event_clock(self.settings, clock.gpiod_clock() as i32)
> +        };
> +
> +        if ret == -1 {
> +            Err(Error::OperationFailed(
> +                OperationType::LineSettingsSetEventClock,
> +                Errno::last(),
> +            ))
> +        } else {
> +            Ok(())
> +        }
> +    }
> +
> +    /// Get the event clock setting.
> +    fn event_clock(&self) -> Result<EventClock> {
> +        EventClock::new(unsafe { gpiod::gpiod_line_settings_get_event_clock(self.settings) } as u32)
> +    }
> +
> +    /// Set the output value setting.
> +    fn set_output_value(&mut self, value: Value) -> Result<()> {
> +        let ret =
> +            unsafe { gpiod::gpiod_line_settings_set_output_value(self.settings, value.value()) };
> +
> +        if ret == -1 {
> +            Err(Error::OperationFailed(
> +                OperationType::LineSettingsSetOutputValue,
> +                Errno::last(),
> +            ))
> +        } else {
> +            Ok(())
> +        }
> +    }
> +
> +    /// Get the output value, 0 or 1.
> +    fn output_value(&self) -> Result<Value> {
> +        let value = unsafe { gpiod::gpiod_line_settings_get_output_value(self.settings) };
> +
> +        if value != 0 && value != 1 {
> +            Err(Error::OperationFailed(
> +                OperationType::LineSettingsGetOutVal,
> +                Errno::last(),
> +            ))
> +        } else {
> +            Value::new(value)
> +        }
> +    }
> +}
> +
> +impl Drop for Settings {
> +    /// Free the line settings object and release all associated resources.
> +    fn drop(&mut self) {
> +        unsafe { gpiod::gpiod_line_settings_free(self.settings) }
> +    }
> +}
> diff --git a/bindings/rust/libgpiod/src/request_config.rs b/bindings/rust/libgpiod/src/request_config.rs
> new file mode 100644
> index 000000000000..760d9c755c86
> --- /dev/null
> +++ b/bindings/rust/libgpiod/src/request_config.rs
> @@ -0,0 +1,96 @@
> +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
> +//
> +// Copyright 2022 Linaro Ltd. All Rights Reserved.
> +//     Viresh Kumar <viresh.kumar@linaro.org>
> +
> +use libc::strlen;
> +use std::os::raw::{c_char, c_ulong};
> +use std::{slice, str};
> +
> +use vmm_sys_util::errno::Error as Errno;
> +
> +use super::{gpiod, Error, OperationType, 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. The mutators don't return error values. If the
> +/// values are invalid, in general they are silently adjusted to acceptable
> +/// ranges.
> +
> +#[derive(Clone, Debug, Eq, PartialEq)]
> +pub struct Config {
> +    config: *mut gpiod::gpiod_request_config,
> +}
> +

And one last time...

Clone/Drop wrapping a raw pointer again.

> +impl Config {
> +    /// Create a new request config object.
> +    pub fn new() -> Result<Self> {
> +        let config = unsafe { gpiod::gpiod_request_config_new() };
> +        if config.is_null() {
> +            return Err(Error::OperationFailed(
> +                OperationType::RequestConfigNew,
> +                Errno::last(),
> +            ));
> +        }
> +
> +        Ok(Self { config })
> +    }
> +
> +    /// Private helper, Returns gpiod_request_config
> +    pub(crate) fn config(&self) -> *mut gpiod::gpiod_request_config {
> +        self.config
> +    }
> +

Make self.config pub(crate) instead?

> +    /// 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 {
> +            gpiod::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 consumer(&self) -> Result<&str> {
> +        // SAFETY: The string returned by libgpiod is guaranteed to live as long
> +        // as the `struct Config`.
> +        let consumer = unsafe { gpiod::gpiod_request_config_get_consumer(self.config) };
> +        if consumer.is_null() {
> +            return Err(Error::OperationFailed(
> +                OperationType::RequestConfigGetConsumer,
> +                Errno::last(),
> +            ));
> +        }
> +
> +        // SAFETY: The string is guaranteed to be valid here by the C API.
> +        str::from_utf8(unsafe {
> +            slice::from_raw_parts(consumer as *const u8, strlen(consumer) as usize)
> +        })
> +        .map_err(Error::StringNotUtf8)
> +    }
> +
> +    /// Set the size of the kernel event buffer for the request.
> +    pub fn set_event_buffer_size(&self, size: usize) {
> +        unsafe { gpiod::gpiod_request_config_set_event_buffer_size(self.config, size as c_ulong) }
> +    }
> +
> +    /// Get the edge event buffer size setting for the request config.
> +    pub fn event_buffer_size(&self) -> usize {
> +        unsafe { gpiod::gpiod_request_config_get_event_buffer_size(self.config) as usize }
> +    }
> +}
> +
> +impl Drop for Config {
> +    /// Free the request config object and release all associated resources.
> +    fn drop(&mut self) {
> +        unsafe { gpiod::gpiod_request_config_free(self.config) }
> +    }
> +}
> -- 
> 2.31.1.272.g89b43f80a514
> 

Overall I don't see any problems that require major rework to the scale
of previous versions.  My main itches that need to be scratched being
dangling pointers and to a lesser degree thread safey.

I find the event_buffer solution to be less safe than it could be.
I've got an alternative, based on the my earlier PoC/suggestion, that
returns an snaphot of events when you read events from the request into
the buffer.  I've put that in a branch on github [1] if you want to take
a look.

I went though a few variations wrt how to deal with the event cloning.
Not implementing Clone on the events means they can't be stored in a
Vec, and so storing them in the Buffer was problematic.
My initial solution was for the event to only wrap the C pointer and
have distinct event types for buffered and cloned, implemented
using traits - which is bit of a pain as the user then needs to import
the trait themselves to use the events :-(.
Then tried a macro to add impl Event on the two event types, which looks
ugly to me.
Finally went with having Event be a new type of BaseEvent, which does
impl Clone, so the event buffer can store them (and <cough> cast them
to Event as appropriate).  Slightly more evil under the hood than I
would like, but provides the cleanest implementation.

The Events snapshot should impl Iterator to make it easier to use,
though I haven't added that yet.
And the error returned by Events::event() is wrong - just cut and pasted
one in there - probably should be InvalidArguments?

Anyway, the end result is that it is not possible to read an invalid or
uninitialised event from Rust, which was the point.
It also better handles lifetimes, so it isn't necessary to explicitly
drop events before re-using the buffer, and the event_clone() method
can be an instance method rather than a class method (to borrow some
Python terminology).
I've added an events.rs to the examples to demonstrate how the
changes impact usage in each case.

Cheers,
Kent.

[1] https://github.com/warthog618/libgpiod/commits/rust_v7_mods


^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH V7 4/8] libgpiod: Add rust examples
  2022-10-14 10:47 ` [PATCH V7 4/8] libgpiod: Add rust examples Viresh Kumar
@ 2022-10-17 13:00   ` Kent Gibson
  0 siblings, 0 replies; 50+ messages in thread
From: Kent Gibson @ 2022-10-17 13:00 UTC (permalink / raw)
  To: Viresh Kumar
  Cc: Linus Walleij, Bartosz Golaszewski, Vincent Guittot, linux-gpio,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

On Fri, Oct 14, 2022 at 04:17:21PM +0530, Viresh Kumar wrote:
> Add examples for the usage of the rust bindings, quite similar to the
> ones in cxx bindings.
> 
> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
> ---
>  bindings/rust/libgpiod/examples/gpiodetect.rs | 30 ++++++
>  bindings/rust/libgpiod/examples/gpiofind.rs   | 35 +++++++
>  bindings/rust/libgpiod/examples/gpioget.rs    | 42 ++++++++
>  bindings/rust/libgpiod/examples/gpioinfo.rs   | 95 +++++++++++++++++++
>  bindings/rust/libgpiod/examples/gpiomon.rs    | 73 ++++++++++++++
>  bindings/rust/libgpiod/examples/gpioset.rs    | 60 ++++++++++++
>  bindings/rust/libgpiod/examples/gpiowatch.rs  | 54 +++++++++++
>  7 files changed, 389 insertions(+)
>  create mode 100644 bindings/rust/libgpiod/examples/gpiodetect.rs
>  create mode 100644 bindings/rust/libgpiod/examples/gpiofind.rs
>  create mode 100644 bindings/rust/libgpiod/examples/gpioget.rs
>  create mode 100644 bindings/rust/libgpiod/examples/gpioinfo.rs
>  create mode 100644 bindings/rust/libgpiod/examples/gpiomon.rs
>  create mode 100644 bindings/rust/libgpiod/examples/gpioset.rs
>  create mode 100644 bindings/rust/libgpiod/examples/gpiowatch.rs
> 

Add an example that exercises the event buffer, demonstrating that its is
not possible to read new events while the previous set are still in
use.

I added something along those lines, based on gpiomon, in my mods
branch mentioned in patch 3.

If multi-threading is supported, perhaps some examples demonstrating
that as well.  Even if there are tests that already cover it - major
features should have explicit examples, not require rooting around
through the test suite.

> diff --git a/bindings/rust/libgpiod/examples/gpiodetect.rs b/bindings/rust/libgpiod/examples/gpiodetect.rs
> new file mode 100644
> index 000000000000..f24ac72e2d48
> --- /dev/null
> +++ b/bindings/rust/libgpiod/examples/gpiodetect.rs
> @@ -0,0 +1,30 @@
> +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
> +//
> +// Copyright 2022 Linaro Ltd. All Rights Reserved.
> +//     Viresh Kumar <viresh.kumar@linaro.org>
> +//
> +// Simplified Rust implementation of gpiodetect tool.
> +
> +use std::env;
> +use std::path::Path;
> +
> +use libgpiod::{gpiochip_devices, Error, Result};
> +
> +fn main() -> Result<()> {
> +    let args: Vec<String> = env::args().collect();
> +    if args.len() > 1 {
> +        println!("Usage: {}", args[0]);
> +        return Err(Error::InvalidArguments);
> +    }
> +
> +    for chip in gpiochip_devices(&Path::new("/dev"))? {
> +        println!(
> +            "{} [{}] ({})",
> +            chip.name()?,
> +            chip.label()?,
> +            chip.num_lines(),
> +        );
> +    }
> +
> +    Ok(())
> +}
> diff --git a/bindings/rust/libgpiod/examples/gpiofind.rs b/bindings/rust/libgpiod/examples/gpiofind.rs
> new file mode 100644
> index 000000000000..07e886bc3896
> --- /dev/null
> +++ b/bindings/rust/libgpiod/examples/gpiofind.rs
> @@ -0,0 +1,35 @@
> +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
> +//
> +// Copyright 2022 Linaro Ltd. All Rights Reserved.
> +//     Viresh Kumar <viresh.kumar@linaro.org>
> +//
> +// Simplified Rust implementation of gpiofind tool.
> +
> +use std::env;
> +use std::path::Path;
> +
> +use libgpiod::{gpiochip_devices, Error, Result};
> +
> +fn main() -> Result<()> {
> +    let args: Vec<String> = env::args().collect();
> +    if args.len() != 2 {
> +        println!("Usage: {} <line-name>", args[0]);
> +        return Err(Error::InvalidArguments);
> +    }
> +
> +    for chip in gpiochip_devices(&Path::new("/dev"))? {
> +        let offset = chip.line_offset_from_name(&args[1]);
> +        if offset.is_ok() {
> +            println!(
> +                "Line {} found: Chip: {}, offset: {}",
> +                args[1],
> +                chip.name()?,
> +                offset?
> +            );
> +            return Ok(());
> +        }
> +    }
> +
> +    println!("Failed to find line: {}", args[1]);
> +    Ok(())
> +}
> diff --git a/bindings/rust/libgpiod/examples/gpioget.rs b/bindings/rust/libgpiod/examples/gpioget.rs
> new file mode 100644
> index 000000000000..f4c111987d96
> --- /dev/null
> +++ b/bindings/rust/libgpiod/examples/gpioget.rs
> @@ -0,0 +1,42 @@
> +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
> +//
> +// Copyright 2022 Linaro Ltd. All Rights Reserved.
> +//     Viresh Kumar <viresh.kumar@linaro.org>
> +//
> +// Simplified Rust implementation of gpioget tool.
> +
> +use std::env;
> +
> +use libgpiod::{chip::Chip, line, request, Direction, Error, Offset, Result, SettingVal};
> +
> +fn main() -> Result<()> {
> +    let args: Vec<String> = env::args().collect();
> +    if args.len() < 3 {
> +        println!("Usage: {} <chip> <line_offset0> ...", args[0]);
> +        return Err(Error::InvalidArguments);
> +    }
> +
> +    let mut lsettings = line::Settings::new()?;
> +    let lconfig = line::Config::new()?;
> +    let mut offsets = Vec::<Offset>::new();
> +
> +    for arg in &args[2..] {
> +        let offset = arg.parse::<Offset>().map_err(|_| Error::InvalidArguments)?;
> +        offsets.push(offset);
> +    }
> +
> +    lsettings.set_prop(&[SettingVal::Direction(Direction::Input)])?;

This is an example of where 

       lsettings.set_direction(Direction::Input)?;

would be simpler and clearer.

Cheers,
Kent.

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH V7 5/8] libgpiod: Add gpiosim rust crate
  2022-10-14 10:47 ` [PATCH V7 5/8] libgpiod: Add gpiosim rust crate Viresh Kumar
@ 2022-10-17 13:00   ` Kent Gibson
  2022-10-21  9:56     ` Viresh Kumar
  0 siblings, 1 reply; 50+ messages in thread
From: Kent Gibson @ 2022-10-17 13:00 UTC (permalink / raw)
  To: Viresh Kumar
  Cc: Linus Walleij, Bartosz Golaszewski, Vincent Guittot, linux-gpio,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

On Fri, Oct 14, 2022 at 04:17:22PM +0530, Viresh Kumar wrote:
> This adds gpiosim rust crate, which provides helpers to emulate GPIO
> chips.
> 
> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
> ---
>  bindings/rust/Cargo.toml         |   3 +-
>  bindings/rust/gpiosim/Cargo.toml |  18 ++
>  bindings/rust/gpiosim/build.rs   |  43 ++++
>  bindings/rust/gpiosim/src/lib.rs |  25 +++
>  bindings/rust/gpiosim/src/sim.rs | 323 +++++++++++++++++++++++++++++++
>  5 files changed, 411 insertions(+), 1 deletion(-)
>  create mode 100644 bindings/rust/gpiosim/Cargo.toml
>  create mode 100644 bindings/rust/gpiosim/build.rs
>  create mode 100644 bindings/rust/gpiosim/src/lib.rs
>  create mode 100644 bindings/rust/gpiosim/src/sim.rs
> 
> diff --git a/bindings/rust/Cargo.toml b/bindings/rust/Cargo.toml
> index 1e57ef2c0002..8721bc610b86 100644
> --- a/bindings/rust/Cargo.toml
> +++ b/bindings/rust/Cargo.toml
> @@ -2,5 +2,6 @@
>  
>  members = [
>      "libgpiod-sys",
> -    "libgpiod"
> +    "libgpiod",
> +    "gpiosim"
>  ]
> diff --git a/bindings/rust/gpiosim/Cargo.toml b/bindings/rust/gpiosim/Cargo.toml
> new file mode 100644
> index 000000000000..d6b4cc34339b
> --- /dev/null
> +++ b/bindings/rust/gpiosim/Cargo.toml
> @@ -0,0 +1,18 @@
> +[package]
> +name = "gpiosim"
> +version = "0.1.0"
> +edition = "2018"
> +

license and other keys...

> +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
> +
> +[dependencies]
> +libc = ">=0.2.39"
> +libgpiod = { path = "../libgpiod" }
> +vmm-sys-util = "=0.10.0"
> +
> +[features]
> +generate = [ "bindgen" ]
> +
> +[build-dependencies]
> +bindgen = { version = "0.59.1", optional = true }
> +cc = "1.0.46"
> diff --git a/bindings/rust/gpiosim/build.rs b/bindings/rust/gpiosim/build.rs
> new file mode 100644
> index 000000000000..460fb8c092c3
> --- /dev/null
> +++ b/bindings/rust/gpiosim/build.rs
> @@ -0,0 +1,43 @@
> +#[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() {
> +    // Tell cargo to invalidate the built crate whenever following files change
> +    println!("cargo:rerun-if-changed=../../../tests/gpiosim/gpiosim.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()
> +        // The input header we would like to generate
> +        // bindings for.
> +        .header("../../../tests/gpiosim/gpiosim.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 main() {
> +    #[cfg(feature = "generate")]
> +    generate_bindings();
> +
> +    println!("cargo:rustc-link-lib=kmod");
> +    println!("cargo:rustc-link-lib=mount");
> +    println!("cargo:rustc-link-search=./../../tests/gpiosim/.libs/");
> +    println!("cargo:rustc-link-lib=static=gpiosim");
> +}
> diff --git a/bindings/rust/gpiosim/src/lib.rs b/bindings/rust/gpiosim/src/lib.rs
> new file mode 100644
> index 000000000000..94d0ddb38e0f
> --- /dev/null
> +++ b/bindings/rust/gpiosim/src/lib.rs
> @@ -0,0 +1,25 @@
> +// SPDX-License-Identifier: GPL-2.0
> +

GPL-2.0.  Not so critical for the gpiosim, but still...

> +#[allow(non_camel_case_types, non_upper_case_globals)]
> +#[cfg_attr(test, allow(deref_nullptr, non_snake_case))]
> +#[allow(dead_code)]
> +mod bindings_raw {
> +    #[cfg(feature = "generate")]
> +    include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
> +
> +    #[cfg(not(feature = "generate"))]
> +    include!("bindings.rs");
> +}
> +
> +use bindings_raw::*;
> +
> +pub use bindings_raw::{
> +    GPIOSIM_HOG_DIR_INPUT, GPIOSIM_HOG_DIR_OUTPUT_HIGH, GPIOSIM_HOG_DIR_OUTPUT_LOW,
> +    GPIOSIM_PULL_DOWN, GPIOSIM_PULL_UP, GPIOSIM_VALUE_ACTIVE, GPIOSIM_VALUE_INACTIVE,
> +};

Create enums for pull, direction, and value and accept/return those rather
than passing around these consts as i32.

> +
> +#[allow(dead_code)]
> +mod sim;
> +

Handle the warning inside the module rather than the blanket allow.

Rename Sim.ctx to _ctx to clear the warning.

> +#[allow(unused_imports)]
> +pub use sim::*;

Which imports are unused and why?
I don't see any warnings after removing this allow.

> diff --git a/bindings/rust/gpiosim/src/sim.rs b/bindings/rust/gpiosim/src/sim.rs
> new file mode 100644
> index 000000000000..50977ea54ebb
> --- /dev/null
> +++ b/bindings/rust/gpiosim/src/sim.rs
> @@ -0,0 +1,323 @@
> +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
> +//
> +// Copyright 2022 Linaro Ltd. All Rights Reserved.
> +//     Viresh Kumar <viresh.kumar@linaro.org>
> +
> +use libc::strlen;
> +use std::os::raw::c_char;
> +use std::path::PathBuf;
> +use std::{slice, str};
> +
> +use vmm_sys_util::errno::Error as Errno;
> +
> +use libgpiod::{Error, Offset, OperationType, Result};
> +
> +use crate::*;
> +
> +/// Sim Ctx
> +#[derive(Debug)]
> +struct SimCtx {
> +    ctx: *mut gpiosim_ctx,
> +}
> +
> +unsafe impl Send for SimCtx {}
> +unsafe impl Sync for SimCtx {}
> +

Is gpiosim thread safe?

> +impl SimCtx {
> +    fn new() -> Result<Self> {
> +        let ctx = unsafe { gpiosim_ctx_new() };

SAFETY comments for all unsafe.

Cheers,
Kent.

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH V7 7/8] libgpiod: Add rust tests
  2022-10-14 10:47 ` [PATCH V7 7/8] libgpiod: Add rust tests Viresh Kumar
@ 2022-10-17 13:00   ` Kent Gibson
  2022-10-17 14:30     ` Kent Gibson
  2022-10-20 10:37     ` Viresh Kumar
  0 siblings, 2 replies; 50+ messages in thread
From: Kent Gibson @ 2022-10-17 13:00 UTC (permalink / raw)
  To: Viresh Kumar
  Cc: Linus Walleij, Bartosz Golaszewski, Vincent Guittot, linux-gpio,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

On Fri, Oct 14, 2022 at 04:17:24PM +0530, Viresh Kumar wrote:
> Add tests for the rust bindings, quite similar to the ones in cxx
> bindings.
> 
> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
> ---

<snip>
> +
> +        #[test]
> +        fn clock() {

My preference would be event_clock(), but I can live with clock().

> +            let mut config = TestConfig::new(NGPIO).unwrap();
> +            config.lconfig_add_settings(&[0]);
> +            config.request_lines().unwrap();
> +            let info = config.chip().line_info(0).unwrap();
> +            assert_eq!(info.event_clock().unwrap(), EventClock::Monotonic);
> +
> +            let mut config = TestConfig::new(NGPIO).unwrap();
> +            config.lconfig_clock(EventClock::Monotonic);
> +            config.lconfig_add_settings(&[0]);
> +            config.request_lines().unwrap();
> +            let info = config.chip().line_info(0).unwrap();
> +            assert_eq!(info.event_clock().unwrap(), EventClock::Monotonic);
> +
> +            let mut config = TestConfig::new(NGPIO).unwrap();
> +            config.lconfig_clock(EventClock::Realtime);
> +            config.lconfig_add_settings(&[0]);
> +            config.request_lines().unwrap();
> +            let info = config.chip().line_info(0).unwrap();
> +            assert_eq!(info.event_clock().unwrap(), EventClock::Realtime);
> +
> +            let mut config = TestConfig::new(NGPIO).unwrap();
> +            config.lconfig_clock(EventClock::HTE);
> +            config.lconfig_add_settings(&[0]);
> +            config.request_lines().unwrap();
> +            let info = config.chip().line_info(0).unwrap();
> +            assert_eq!(info.event_clock().unwrap(), EventClock::HTE);

I was surprised to find HTE tests passing on a kernel without CONFIG_HTE.
I take that as being a kernel bug (GPIO_V2_LINE_VALID_FLAGS includes the
HTE flag unconditionally - which is wrong IMHO).

You probably shouldn't assume HTE works - unless you have a system that
supports HTE.

Other than that the tests look good to me, though as with the Python
bindings I've only skimmed them.

Cheers,
Kent.

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH V7 7/8] libgpiod: Add rust tests
  2022-10-17 13:00   ` Kent Gibson
@ 2022-10-17 14:30     ` Kent Gibson
  2022-10-20 10:37     ` Viresh Kumar
  1 sibling, 0 replies; 50+ messages in thread
From: Kent Gibson @ 2022-10-17 14:30 UTC (permalink / raw)
  To: Viresh Kumar
  Cc: Linus Walleij, Bartosz Golaszewski, Vincent Guittot, linux-gpio,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

On Mon, Oct 17, 2022 at 09:00:43PM +0800, Kent Gibson wrote:
> On Fri, Oct 14, 2022 at 04:17:24PM +0530, Viresh Kumar wrote:
> > Add tests for the rust bindings, quite similar to the ones in cxx
> > bindings.
> > 
> > Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
> > ---
> 
> <snip>
> > +
> > +        #[test]
> > +        fn clock() {
> 
> My preference would be event_clock(), but I can live with clock().
> 
> > +            let mut config = TestConfig::new(NGPIO).unwrap();
> > +            config.lconfig_add_settings(&[0]);
> > +            config.request_lines().unwrap();
> > +            let info = config.chip().line_info(0).unwrap();
> > +            assert_eq!(info.event_clock().unwrap(), EventClock::Monotonic);
> > +
> > +            let mut config = TestConfig::new(NGPIO).unwrap();
> > +            config.lconfig_clock(EventClock::Monotonic);
> > +            config.lconfig_add_settings(&[0]);
> > +            config.request_lines().unwrap();
> > +            let info = config.chip().line_info(0).unwrap();
> > +            assert_eq!(info.event_clock().unwrap(), EventClock::Monotonic);
> > +
> > +            let mut config = TestConfig::new(NGPIO).unwrap();
> > +            config.lconfig_clock(EventClock::Realtime);
> > +            config.lconfig_add_settings(&[0]);
> > +            config.request_lines().unwrap();
> > +            let info = config.chip().line_info(0).unwrap();
> > +            assert_eq!(info.event_clock().unwrap(), EventClock::Realtime);
> > +
> > +            let mut config = TestConfig::new(NGPIO).unwrap();
> > +            config.lconfig_clock(EventClock::HTE);
> > +            config.lconfig_add_settings(&[0]);
> > +            config.request_lines().unwrap();
> > +            let info = config.chip().line_info(0).unwrap();
> > +            assert_eq!(info.event_clock().unwrap(), EventClock::HTE);
> 
> I was surprised to find HTE tests passing on a kernel without CONFIG_HTE.
> I take that as being a kernel bug (GPIO_V2_LINE_VALID_FLAGS includes the
> HTE flag unconditionally - which is wrong IMHO).
> 

Bah, I was running an old-ish kernel (5.19) in my test setup.

A check for this was added in my HTE tidy up -
commit 272ddba0047 "gpiolib: cdev: compile out HTE unless CONFIG_HTE selected"

So the HTE tests now do fail with a more recent kernel (6.0+) - unless you
have HTE support.

Cheers,
Kent.

> You probably shouldn't assume HTE works - unless you have a system that
> supports HTE.
> 
> Other than that the tests look good to me, though as with the Python
> bindings I've only skimmed them.
> 
> Cheers,
> Kent.

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH V7 3/8] libgpiod: Add rust wrapper crate
  2022-10-14 10:47 ` [PATCH V7 3/8] libgpiod: Add rust wrapper crate Viresh Kumar
  2022-10-17 12:59   ` Kent Gibson
@ 2022-10-18  3:27   ` Kent Gibson
  2022-10-21 11:25     ` Viresh Kumar
  1 sibling, 1 reply; 50+ messages in thread
From: Kent Gibson @ 2022-10-18  3:27 UTC (permalink / raw)
  To: Viresh Kumar
  Cc: Linus Walleij, Bartosz Golaszewski, Vincent Guittot, linux-gpio,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

On Fri, Oct 14, 2022 at 04:17:20PM +0530, Viresh Kumar wrote:
> Add rust wrapper crate, around the libpiod-sys crate added earlier, to
> provide a convenient interface for the users.
> 
> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>

One more thing - I overlooked trying to build on 32-bit while reviewing.
(in this case on a Raspberry Pi 4)

Unfortunately that fails:

   Compiling libgpiod v0.1.0 (/home/pi/libgpiod/bindings/rust/libgpiod)
error[E0308]: mismatched types
    --> libgpiod/src/edge_event.rs:32:88
     |
32   |         let event = unsafe { gpiod::gpiod_edge_event_buffer_get_event(buffer.buffer(), index) };
     |                              ----------------------------------------                  ^^^^^ expected `u32`, found `u64`
     |                              |
     |                              arguments to this function are incorrect
     |
note: function defined here
    --> /home/pi/libgpiod/bindings/rust/libgpiod-sys/src/bindings.rs:1151:12
     |
1151 |     pub fn gpiod_edge_event_buffer_get_event(
     |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: you can convert a `u64` to a `u32` and panic if the converted value doesn't fit
     |
32   |         let event = unsafe { gpiod::gpiod_edge_event_buffer_get_event(buffer.buffer(), index.try_into().unwrap()) };
     |                                                                                             ++++++++++++++++++++

error[E0308]: mismatched types
  --> libgpiod/src/edge_event.rs:81:18
   |
80 |     pub fn global_seqno(&self) -> u64 {
   |                                   --- expected `u64` because of return type
81 |         unsafe { gpiod::gpiod_edge_event_get_global_seqno(self.event) }
   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `u64`, found `u32`
   |
help: you can convert a `u32` to a `u64`
   |
81 |         unsafe { gpiod::gpiod_edge_event_get_global_seqno(self.event).into() }
   |                                                                      +++++++

error[E0308]: mismatched types
  --> libgpiod/src/edge_event.rs:89:18
   |
88 |     pub fn line_seqno(&self) -> u64 {
   |                                 --- expected `u64` because of return type
89 |         unsafe { gpiod::gpiod_edge_event_get_line_seqno(self.event) }
   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `u64`, found `u32`
   |
help: you can convert a `u32` to a `u64`
   |
89 |         unsafe { gpiod::gpiod_edge_event_get_line_seqno(self.event).into() }
   |                                                                    +++++++

error[E0308]: mismatched types
   --> libgpiod/src/line_config.rs:100:72
    |
100 |             unsafe { gpiod::gpiod_line_config_get_offsets(self.config, &mut num, &mut offsets) };
    |                      ------------------------------------              ^^^^^^^^ expected `u32`, found `u64`
    |                      |
    |                      arguments to this function are incorrect
    |
    = note:    expected raw pointer `*mut u32`
            found mutable reference `&mut u64`
note: function defined here
   --> /home/pi/libgpiod/bindings/rust/libgpiod-sys/src/bindings.rs:847:12
    |
847 |     pub fn gpiod_line_config_get_offsets(
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0308]: mismatched types
   --> libgpiod/src/line_info.rs:168:40
    |
168 |         Duration::from_micros(unsafe { gpiod::gpiod_line_info_get_debounce_period_us(self.info) })
    |                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `u64`, found `u32`
    |
help: you can convert a `u32` to a `u64`
    |
168 |         Duration::from_micros(unsafe { gpiod::gpiod_line_info_get_debounce_period_us(self.info).into() })
    |                                                                                                +++++++

error[E0308]: mismatched types
    --> libgpiod/src/line_request.rs:226:17
     |
223  |             gpiod::gpiod_line_request_read_edge_event(
     |             ----------------------------------------- arguments to this function are incorrect
...
226  |                 buffer.capacity() as u64,
     |                 ^^^^^^^^^^^^^^^^^^^^^^^^ expected `u32`, found `u64`
     |
note: function defined here
    --> /home/pi/libgpiod/bindings/rust/libgpiod-sys/src/bindings.rs:1058:12
     |
1058 |     pub fn gpiod_line_request_read_edge_event(
     |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: you can convert a `u64` to a `u32` and panic if the converted value doesn't fit
     |
226  |                 (buffer.capacity() as u64).try_into().unwrap(),
     |                 +                        +++++++++++++++++++++

error[E0308]: mismatched types
   --> libgpiod/src/line_settings.rs:209:17
    |
207 |             gpiod::gpiod_line_settings_set_debounce_period_us(
    |             ------------------------------------------------- arguments to this function are incorrect
208 |                 self.settings,
209 |                 period.as_micros() as u64,
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `u32`, found `u64`
    |
note: function defined here
   --> /home/pi/libgpiod/bindings/rust/libgpiod-sys/src/bindings.rs:746:12
    |
746 |     pub fn gpiod_line_settings_set_debounce_period_us(
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: you can convert a `u64` to a `u32` and panic if the converted value doesn't fit
    |
209 |                 (period.as_micros() as u64).try_into().unwrap(),
    |                 +                         +++++++++++++++++++++

error[E0308]: mismatched types
   --> libgpiod/src/line_settings.rs:217:13
    |
217 |             gpiod::gpiod_line_settings_get_debounce_period_us(self.settings)
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `u64`, found `u32`
    |
help: you can convert a `u32` to a `u64`
    |
217 |             gpiod::gpiod_line_settings_get_debounce_period_us(self.settings).into()
    |                                                                             +++++++

For more information about this error, try `rustc --explain E0308`.
error: could not compile `libgpiod` due to 8 previous errors

---

An example of one of the relevant generated signatures is:

    pub fn gpiod_edge_event_buffer_get_event(
        buffer: *mut gpiod_edge_event_buffer,
        index: ::std::os::raw::c_ulong,
    ) -> *mut gpiod_edge_event;

On 32-bit c_ulong maps to u32, not u64.

Cheers,
Kent.


^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH V7 1/8] libgpiod: Add libgpiod-sys rust crate
  2022-10-17 12:59   ` Kent Gibson
@ 2022-10-18 11:22     ` Viresh Kumar
  2022-10-18 11:42       ` Miguel Ojeda
  2022-10-18 11:49       ` Kent Gibson
  0 siblings, 2 replies; 50+ messages in thread
From: Viresh Kumar @ 2022-10-18 11:22 UTC (permalink / raw)
  To: Kent Gibson
  Cc: Linus Walleij, Bartosz Golaszewski, Vincent Guittot, linux-gpio,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

On 17-10-22, 20:59, Kent Gibson wrote:
> > diff --git a/bindings/rust/libgpiod-sys/Cargo.toml b/bindings/rust/libgpiod-sys/Cargo.toml
> > new file mode 100644
> > index 000000000000..77f82719d269
> > --- /dev/null
> > +++ b/bindings/rust/libgpiod-sys/Cargo.toml
> > @@ -0,0 +1,15 @@
> > +[package]
> > +name = "libgpiod-sys"
> > +version = "0.1.0"
> > +edition = "2018"
> > +
> 
> Add license and other relevant keys as per the link you helpfully include...

Is this enough ?

diff --git a/bindings/rust/libgpiod-sys/Cargo.toml b/bindings/rust/libgpiod-sys/Cargo.toml
index 77f82719d269..af4098cb137d 100644
--- a/bindings/rust/libgpiod-sys/Cargo.toml
+++ b/bindings/rust/libgpiod-sys/Cargo.toml
@@ -1,6 +1,11 @@
 [package]
 name = "libgpiod-sys"
 version = "0.1.0"
+authors = ["Viresh Kumar <viresh.kumar@linaro.org>"]
+description = "A rust library for libgpiod public header bindings"
+repository = "https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/"
+keywords = ["libgpiod", "gpio"]
+license = "LGPL-2.1-or-later"
 edition = "2018"

I picked license details from C code, is that what we should be using
here too ?

> > diff --git a/bindings/rust/libgpiod-sys/src/lib.rs b/bindings/rust/libgpiod-sys/src/lib.rs
> > new file mode 100644
> > index 000000000000..1ca355b5f5ac
> > --- /dev/null
> > +++ b/bindings/rust/libgpiod-sys/src/lib.rs
> > @@ -0,0 +1,13 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +
> 
> GPL-2.0?
> 
> Elsewhere you use Apache 2.0 AND BSD-3-Clause for rust code.

I would use the same information here too, once decided.

-- 
viresh

^ permalink raw reply related	[flat|nested] 50+ messages in thread

* Re: [PATCH V7 1/8] libgpiod: Add libgpiod-sys rust crate
  2022-10-18 11:22     ` Viresh Kumar
@ 2022-10-18 11:42       ` Miguel Ojeda
  2022-10-18 11:49       ` Kent Gibson
  1 sibling, 0 replies; 50+ messages in thread
From: Miguel Ojeda @ 2022-10-18 11:42 UTC (permalink / raw)
  To: Viresh Kumar
  Cc: Kent Gibson, Linus Walleij, Bartosz Golaszewski, Vincent Guittot,
	linux-gpio, Wedson Almeida Filho, Alex Bennée, stratos-dev,
	Gerard Ryan

On Tue, Oct 18, 2022 at 1:22 PM Viresh Kumar <viresh.kumar@linaro.org> wrote:
>
> +description = "A rust library for libgpiod public header bindings"

I think "A rust library for" is redundant (I would write it in
GitHub's description, but not in Cargo's). But double-check with a few
other well-known libraries out there to see what they do for their
Cargo descriptions.

> +repository = "https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/"

No need for the trailing slash, I think.

More importantly, you may want to add a "documentation", "categories"
and "rust-version" keys.

Cheers,
Miguel

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH V7 1/8] libgpiod: Add libgpiod-sys rust crate
  2022-10-18 11:22     ` Viresh Kumar
  2022-10-18 11:42       ` Miguel Ojeda
@ 2022-10-18 11:49       ` Kent Gibson
  2022-10-19  6:46         ` Viresh Kumar
  1 sibling, 1 reply; 50+ messages in thread
From: Kent Gibson @ 2022-10-18 11:49 UTC (permalink / raw)
  To: Viresh Kumar
  Cc: Linus Walleij, Bartosz Golaszewski, Vincent Guittot, linux-gpio,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

On Tue, Oct 18, 2022 at 04:52:04PM +0530, Viresh Kumar wrote:
> On 17-10-22, 20:59, Kent Gibson wrote:
> > > diff --git a/bindings/rust/libgpiod-sys/Cargo.toml b/bindings/rust/libgpiod-sys/Cargo.toml
> > > new file mode 100644
> > > index 000000000000..77f82719d269
> > > --- /dev/null
> > > +++ b/bindings/rust/libgpiod-sys/Cargo.toml
> > > @@ -0,0 +1,15 @@
> > > +[package]
> > > +name = "libgpiod-sys"
> > > +version = "0.1.0"
> > > +edition = "2018"
> > > +
> > 
> > Add license and other relevant keys as per the link you helpfully include...
> 
> Is this enough ?
> 

You should add "categories" and "documentation" if you intend to publish
to crates.io.

> diff --git a/bindings/rust/libgpiod-sys/Cargo.toml b/bindings/rust/libgpiod-sys/Cargo.toml
> index 77f82719d269..af4098cb137d 100644
> --- a/bindings/rust/libgpiod-sys/Cargo.toml
> +++ b/bindings/rust/libgpiod-sys/Cargo.toml
> @@ -1,6 +1,11 @@
>  [package]
>  name = "libgpiod-sys"
>  version = "0.1.0"
> +authors = ["Viresh Kumar <viresh.kumar@linaro.org>"]
> +description = "A rust library for libgpiod public header bindings"
> +repository = "https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/"
> +keywords = ["libgpiod", "gpio"]
> +license = "LGPL-2.1-or-later"
>  edition = "2018"
> 

Why the 2018 edition, rather than 2021?

> I picked license details from C code, is that what we should be using
> here too ?
> 

LGPL is probably not appropriate here, as the binding code ends up
being part of the binary - unless you plan to package it as a dynamic
library?

The licenses of your dependencies are:
$ cargo license
(MIT OR Apache-2.0) AND Unicode-DFS-2016 (1): unicode-ident
Apache-2.0 AND BSD-3-Clause (1): vmm-sys-util
Apache-2.0 OR MIT (8): bitflags, cc, libc, proc-macro2, quote, syn, thiserror, thiserror-impl
MIT (1): intmap
N/A (3): gpiosim, libgpiod, libgpiod-sys

IANAL, but one or more of those would be more appropriate than either
GPL or LGPL.
You, Bart and linaro(?) will have to come to some agreement on which
license or combination to go with - it depends.

Cheers,
Kent.


^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH V7 1/8] libgpiod: Add libgpiod-sys rust crate
  2022-10-18 11:49       ` Kent Gibson
@ 2022-10-19  6:46         ` Viresh Kumar
  2022-10-19  7:21           ` Kent Gibson
  0 siblings, 1 reply; 50+ messages in thread
From: Viresh Kumar @ 2022-10-19  6:46 UTC (permalink / raw)
  To: Kent Gibson
  Cc: Linus Walleij, Bartosz Golaszewski, Vincent Guittot, linux-gpio,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

On 18-10-22, 19:49, Kent Gibson wrote:
> You should add "categories" and "documentation" if you intend to publish
> to crates.io.

In order to add documentations, should I commit documentation (created
with cargo) as well in libgpiod, so I get a link to it ?

> LGPL is probably not appropriate here, as the binding code ends up
> being part of the binary - unless you plan to package it as a dynamic
> library?
> 
> The licenses of your dependencies are:
> $ cargo license
> (MIT OR Apache-2.0) AND Unicode-DFS-2016 (1): unicode-ident
> Apache-2.0 AND BSD-3-Clause (1): vmm-sys-util

This is the license used by the user crate for libgpiod, vhost-device,
I am inclined to use this then, unless someone has an objection to it.

> Apache-2.0 OR MIT (8): bitflags, cc, libc, proc-macro2, quote, syn, thiserror, thiserror-impl
> MIT (1): intmap
> N/A (3): gpiosim, libgpiod, libgpiod-sys
> 
> IANAL, but one or more of those would be more appropriate than either
> GPL or LGPL.
> You, Bart and linaro(?) will have to come to some agreement on which
> license or combination to go with - it depends.

-- 
viresh

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH V7 1/8] libgpiod: Add libgpiod-sys rust crate
  2022-10-19  6:46         ` Viresh Kumar
@ 2022-10-19  7:21           ` Kent Gibson
  2022-10-19 11:22             ` Viresh Kumar
  0 siblings, 1 reply; 50+ messages in thread
From: Kent Gibson @ 2022-10-19  7:21 UTC (permalink / raw)
  To: Viresh Kumar
  Cc: Linus Walleij, Bartosz Golaszewski, Vincent Guittot, linux-gpio,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

On Wed, Oct 19, 2022 at 12:16:12PM +0530, Viresh Kumar wrote:
> On 18-10-22, 19:49, Kent Gibson wrote:
> > You should add "categories" and "documentation" if you intend to publish
> > to crates.io.
> 
> In order to add documentations, should I commit documentation (created
> with cargo) as well in libgpiod, so I get a link to it ?
> 

That doesn't really work - you get a link to the files in github, not
as a web site.  You need somewhere that will host those generated files
as a web site.  You could do that with github-pages if you don't have other
options.  When I've done that I would commit the docs to a separate
branch, just for the docs, and have github-pages host that branch.

If you eventually publish your crate to crates.io you get documentation on
docs.rs for free - and you can skip the documentation key in that case too
- it defaults to the appropriate page on docs.rs.
I assume that would be the case long term - you just need to find
someway to host them in the meantime.

> > LGPL is probably not appropriate here, as the binding code ends up
> > being part of the binary - unless you plan to package it as a dynamic
> > library?
> > 
> > The licenses of your dependencies are:
> > $ cargo license
> > (MIT OR Apache-2.0) AND Unicode-DFS-2016 (1): unicode-ident
> > Apache-2.0 AND BSD-3-Clause (1): vmm-sys-util
> 
> This is the license used by the user crate for libgpiod, vhost-device,
> I am inclined to use this then, unless someone has an objection to it.
> 

I'm wondering if now the python bindings include actual python code there
may be a similar issue there - not sure how Bart intends to package that,
or what licensing implications that may have.

In both cases Bart has the final call.

Cheers,
Kent.

> > Apache-2.0 OR MIT (8): bitflags, cc, libc, proc-macro2, quote, syn, thiserror, thiserror-impl
> > MIT (1): intmap
> > N/A (3): gpiosim, libgpiod, libgpiod-sys
> > 
> > IANAL, but one or more of those would be more appropriate than either
> > GPL or LGPL.
> > You, Bart and linaro(?) will have to come to some agreement on which
> > license or combination to go with - it depends.
> 
> -- 
> viresh

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH V7 1/8] libgpiod: Add libgpiod-sys rust crate
  2022-10-19  7:21           ` Kent Gibson
@ 2022-10-19 11:22             ` Viresh Kumar
  2022-10-19 12:01               ` Kent Gibson
  0 siblings, 1 reply; 50+ messages in thread
From: Viresh Kumar @ 2022-10-19 11:22 UTC (permalink / raw)
  To: Kent Gibson
  Cc: Linus Walleij, Bartosz Golaszewski, Vincent Guittot, linux-gpio,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

On 19-10-22, 15:21, Kent Gibson wrote:
> That doesn't really work - you get a link to the files in github, not
> as a web site.  You need somewhere that will host those generated files
> as a web site.  You could do that with github-pages if you don't have other
> options.

I don't have other options for now, so it is github.

> When I've done that I would commit the docs to a separate
> branch, just for the docs, and have github-pages host that branch.

Okay.

> If you eventually publish your crate to crates.io you get documentation on
> docs.rs for free - and you can skip the documentation key in that case too
> - it defaults to the appropriate page on docs.rs.

Right, that I knew.

> I assume that would be the case long term - you just need to find
> someway to host them in the meantime.

I wonder if we should be doing this at all right now, or if required
only Bartosz should do it instead of me, once this is merged ?

Maybe we need a libgpiod username for that on github ? To make it
independent of personal usernames ? From what I saw, the website name
will be username.github.io eventually.

-- 
viresh

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH V7 1/8] libgpiod: Add libgpiod-sys rust crate
  2022-10-19 11:22             ` Viresh Kumar
@ 2022-10-19 12:01               ` Kent Gibson
  2022-10-20  5:34                 ` Viresh Kumar
  2022-10-27 12:18                 ` Bartosz Golaszewski
  0 siblings, 2 replies; 50+ messages in thread
From: Kent Gibson @ 2022-10-19 12:01 UTC (permalink / raw)
  To: Viresh Kumar
  Cc: Linus Walleij, Bartosz Golaszewski, Vincent Guittot, linux-gpio,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

On Wed, Oct 19, 2022 at 04:52:51PM +0530, Viresh Kumar wrote:
> On 19-10-22, 15:21, Kent Gibson wrote:
> > That doesn't really work - you get a link to the files in github, not
> > as a web site.  You need somewhere that will host those generated files
> > as a web site.  You could do that with github-pages if you don't have other
> > options.
> 
> I don't have other options for now, so it is github.
> 
> > When I've done that I would commit the docs to a separate
> > branch, just for the docs, and have github-pages host that branch.
> 
> Okay.
> 
> > If you eventually publish your crate to crates.io you get documentation on
> > docs.rs for free - and you can skip the documentation key in that case too
> > - it defaults to the appropriate page on docs.rs.
> 
> Right, that I knew.
> 
> > I assume that would be the case long term - you just need to find
> > someway to host them in the meantime.
> 
> I wonder if we should be doing this at all right now, or if required
> only Bartosz should do it instead of me, once this is merged ?
> 

I was assuming it was an interim solution, so it doesn't matter so much.

I'd be happy with docs.rs once libgpiod v2 is released and the bindings
published to crates.io, and I'm personally good with looking at the
generated docs locally in the meantime.

It would be easier for others to take a look if the docs were hosted,
but I don't have any feel as to whether that is worth the effort or not.

> Maybe we need a libgpiod username for that on github ? To make it
> independent of personal usernames ? From what I saw, the website name
> will be username.github.io eventually.
> 

A libgpiod group account might be useful.

And it would be nice to have a libgpiod space somewhere, not just
piggybacking on the gpio mailing list.  Not sure if github is the best
place - but I haven't given it much thought.

Bart, do you have any opinions?

Cheers,
Kent.

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH V7 1/8] libgpiod: Add libgpiod-sys rust crate
  2022-10-19 12:01               ` Kent Gibson
@ 2022-10-20  5:34                 ` Viresh Kumar
  2022-10-27 12:18                 ` Bartosz Golaszewski
  1 sibling, 0 replies; 50+ messages in thread
From: Viresh Kumar @ 2022-10-20  5:34 UTC (permalink / raw)
  To: Kent Gibson
  Cc: Linus Walleij, Bartosz Golaszewski, Vincent Guittot, linux-gpio,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

On 19-10-22, 20:01, Kent Gibson wrote:
> I was assuming it was an interim solution, so it doesn't matter so much.
> 
> I'd be happy with docs.rs once libgpiod v2 is released and the bindings
> published to crates.io, and I'm personally good with looking at the
> generated docs locally in the meantime.

I do plan to publish them to crates.io.

> It would be easier for others to take a look if the docs were hosted,
> but I don't have any feel as to whether that is worth the effort or not.

Lets get the code merge without the docs published, we can always come
back and publish the documentation somewhere once we are sure where to
publish them, if it is really needed since we are going to migrate to
docs.rs anyway..

-- 
viresh

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH V7 7/8] libgpiod: Add rust tests
  2022-10-17 13:00   ` Kent Gibson
  2022-10-17 14:30     ` Kent Gibson
@ 2022-10-20 10:37     ` Viresh Kumar
  2022-10-20 11:02       ` Kent Gibson
  1 sibling, 1 reply; 50+ messages in thread
From: Viresh Kumar @ 2022-10-20 10:37 UTC (permalink / raw)
  To: Kent Gibson
  Cc: Linus Walleij, Bartosz Golaszewski, Vincent Guittot, linux-gpio,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

On 17-10-22, 21:00, Kent Gibson wrote:
> I was surprised to find HTE tests passing on a kernel without CONFIG_HTE.
> I take that as being a kernel bug (GPIO_V2_LINE_VALID_FLAGS includes the
> HTE flag unconditionally - which is wrong IMHO).
> 
> You probably shouldn't assume HTE works - unless you have a system that
> supports HTE.

Should I drop them ? Or run them conditionally ?  How ?

> Other than that the tests look good to me, though as with the Python
> bindings I've only skimmed them.

-- 
viresh

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH V7 7/8] libgpiod: Add rust tests
  2022-10-20 10:37     ` Viresh Kumar
@ 2022-10-20 11:02       ` Kent Gibson
  2022-10-20 12:40         ` Bartosz Golaszewski
  0 siblings, 1 reply; 50+ messages in thread
From: Kent Gibson @ 2022-10-20 11:02 UTC (permalink / raw)
  To: Viresh Kumar
  Cc: Linus Walleij, Bartosz Golaszewski, Vincent Guittot, linux-gpio,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

On Thu, Oct 20, 2022 at 04:07:41PM +0530, Viresh Kumar wrote:
> On 17-10-22, 21:00, Kent Gibson wrote:
> > I was surprised to find HTE tests passing on a kernel without CONFIG_HTE.
> > I take that as being a kernel bug (GPIO_V2_LINE_VALID_FLAGS includes the
> > HTE flag unconditionally - which is wrong IMHO).
> > 
> > You probably shouldn't assume HTE works - unless you have a system that
> > supports HTE.
> 
> Should I drop them ? Or run them conditionally ?  How ?
> 

The Rust test framework is pretty simple, so there is no way to
conditionally skip tests, AFAIAA.  And the only generic way to tell if
your kernel supports HTE without probing it, which makes testing it
pointless.
So I don't see any alternative but to drop them.

Though I would be happy to learn otherwise.

Cheers,
Kent.


^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH V7 7/8] libgpiod: Add rust tests
  2022-10-20 11:02       ` Kent Gibson
@ 2022-10-20 12:40         ` Bartosz Golaszewski
  2022-10-20 15:16           ` Miguel Ojeda
  0 siblings, 1 reply; 50+ messages in thread
From: Bartosz Golaszewski @ 2022-10-20 12:40 UTC (permalink / raw)
  To: Kent Gibson
  Cc: Viresh Kumar, Linus Walleij, Vincent Guittot, linux-gpio,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

On Thu, Oct 20, 2022 at 1:02 PM Kent Gibson <warthog618@gmail.com> wrote:
>
> On Thu, Oct 20, 2022 at 04:07:41PM +0530, Viresh Kumar wrote:
> > On 17-10-22, 21:00, Kent Gibson wrote:
> > > I was surprised to find HTE tests passing on a kernel without CONFIG_HTE.
> > > I take that as being a kernel bug (GPIO_V2_LINE_VALID_FLAGS includes the
> > > HTE flag unconditionally - which is wrong IMHO).
> > >
> > > You probably shouldn't assume HTE works - unless you have a system that
> > > supports HTE.
> >
> > Should I drop them ? Or run them conditionally ?  How ?
> >
>
> The Rust test framework is pretty simple, so there is no way to
> conditionally skip tests, AFAIAA.  And the only generic way to tell if
> your kernel supports HTE without probing it, which makes testing it
> pointless.
> So I don't see any alternative but to drop them.
>
> Though I would be happy to learn otherwise.
>

In C tests I just skip them using g_test_skip() which generates an
info message. I'm surprised to learn one can't skip tests in rust, it
sounds like a very basic functionality. I'd say let's drop them for
now then.

Bart

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH V7 0/8] libgpiod: Add Rust bindings
  2022-10-14 17:03 ` [PATCH V7 0/8] libgpiod: Add Rust bindings Miguel Ojeda
@ 2022-10-20 13:29   ` Björn Roy Baron
  2022-10-21  9:39     ` Viresh Kumar
  0 siblings, 1 reply; 50+ messages in thread
From: Björn Roy Baron @ 2022-10-20 13:29 UTC (permalink / raw)
  To: Miguel Ojeda
  Cc: Viresh Kumar, Linus Walleij, Bartosz Golaszewski,
	Vincent Guittot, linux-gpio, Kent Gibson, Alex Bennée,
	stratos-dev, Gerard Ryan, Wedson Almeida Filho, Alex Gaynor,
	Boqun Feng, Gary Guo, y86-dev

On Friday, October 14th, 2022 at 19:03, Miguel Ojeda <miguel.ojeda.sandonis@gmail.com> wrote:

> On Fri, Oct 14, 2022 at 12:47 PM Viresh Kumar viresh.kumar@linaro.org wrote:
> 
> > 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 v7
> > 
> > [I have pushed v6 and v5 there too, in case someone wants to look at the
> > changes].

It looks pretty good to me. I do have a couple of minor suggestions though.

https://github.com/vireshk/libgpiod/blob/3e7fb99173856a3995360fc3fad51220c4b4e90e/bindings/rust/libgpiod/Cargo.toml#L10 should use 0.2.39 without >= to ensure that publishing a 0.3 version of libc with breaking changes won't break the build. Cargo treats "0.2.39" as ">=0.2.39, <0.3".

At https://github.com/vireshk/libgpiod/blob/3e7fb99173856a3995360fc3fad51220c4b4e90e/bindings/rust/libgpiod/Cargo.toml#L13 the vmm-sys-utils dependency should be unpinned from "=0.10.0" to "0.10.0". Otherwise having any crate depend on a newer semver compatible version will cause a build error. While Cargo allows multiple semver incompatible versions of a crate, it doesn't allow multiple semver compatible versions as it wouldn't know which version to use for a crate that says it works with both versions.

At https://github.com/vireshk/libgpiod/blob/3e7fb99173856a3995360fc3fad51220c4b4e90e/bindings/rust/libgpiod/src/lib.rs#L469 and elsewhere you might want to use `CStr::from_ptr(version)`. This does the `strlen` call for you and you can convert it to an `&str` using `.to_str()`.

At https://github.com/vireshk/libgpiod/blob/3e7fb99173856a3995360fc3fad51220c4b4e90e/bindings/rust/libgpiod/src/chip.rs#L171 you could use `CString` and use the `.as_ptr()` method to get a null-terminated string. Not sure if it would be nicer that what you currently have though.

At https://github.com/vireshk/libgpiod/blob/3e7fb99173856a3995360fc3fad51220c4b4e90e/bindings/rust/libgpiod/src/edge_event.rs#L46 the lifetimes are unclear. Is Event allowed to outlive the buffer? Can you add a lifetime annotation like fn event_clone<'a>(event: &Event<'a>) -> Result<Event<'a>> if it isn't allowed to outlive the buffer or fn event_clone<'a, 'b>(event: &Event<'a>) -> Result<Event<'b>> if it is allowed to outlive the buffer. I'm not sure which of the two the lifetime elision rules cause the current code to be equivalent of, but even if it is correct, explicitly stating the lifetime here is clearer IMHO.

As for the question about test skipping elsewhere in this thread, rust has the #[ignore] attribute to ignore tests at compile time (with a command line flag to run ignored tests anyway), but nothing to ignore tests at runtime unfortunately.

Cheers,
Björn

nb: I can't reply to a mail I didn't receive directly right now due to mail provider limitations. I'm working on sorting this out, but for now I'm going to reply to the mail I did receive directly.

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH V7 7/8] libgpiod: Add rust tests
  2022-10-20 12:40         ` Bartosz Golaszewski
@ 2022-10-20 15:16           ` Miguel Ojeda
  0 siblings, 0 replies; 50+ messages in thread
From: Miguel Ojeda @ 2022-10-20 15:16 UTC (permalink / raw)
  To: Bartosz Golaszewski
  Cc: Kent Gibson, Viresh Kumar, Linus Walleij, Vincent Guittot,
	linux-gpio, Wedson Almeida Filho, Alex Bennée, stratos-dev,
	Gerard Ryan

On Thu, Oct 20, 2022 at 2:40 PM Bartosz Golaszewski <brgl@bgdev.pl> wrote:
>
> In C tests I just skip them using g_test_skip() which generates an
> info message. I'm surprised to learn one can't skip tests in rust, it
> sounds like a very basic functionality. I'd say let's drop them for
> now then.

A `#[test]` can be unconditionally ignored with `#[ignore]`, but they
can still be run if requested via `--ignored` or `--include-ignored`.

They are still always built, though, which sometimes is not what one
wants. But if possible, it is good to keep them that way, since that
guarantees they remain buildable.

Cheers,
Miguel

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH V7 0/8] libgpiod: Add Rust bindings
  2022-10-20 13:29   ` Björn Roy Baron
@ 2022-10-21  9:39     ` Viresh Kumar
  2022-10-21 14:34       ` Björn Roy Baron
  0 siblings, 1 reply; 50+ messages in thread
From: Viresh Kumar @ 2022-10-21  9:39 UTC (permalink / raw)
  To: Björn Roy Baron
  Cc: Miguel Ojeda, Linus Walleij, Bartosz Golaszewski,
	Vincent Guittot, linux-gpio, Kent Gibson, Alex Bennée,
	stratos-dev, Gerard Ryan, Wedson Almeida Filho, Alex Gaynor,
	Boqun Feng, Gary Guo, y86-dev

Hi Björn,

I have bounced (mutt's feature) the initial emails to your and other
email ids that Miguel added. The patches should be in your inbox now.

On 20-10-22, 13:29, Björn Roy Baron wrote:
> At
> https://github.com/vireshk/libgpiod/blob/3e7fb99173856a3995360fc3fad51220c4b4e90e/bindings/rust/libgpiod/src/lib.rs#L469
> and elsewhere you might want to use `CStr::from_ptr(version)`. This
> does the `strlen` call for you and you can convert it to an `&str`
> using `.to_str()`.
 
> At
> https://github.com/vireshk/libgpiod/blob/3e7fb99173856a3995360fc3fad51220c4b4e90e/bindings/rust/libgpiod/src/chip.rs#L171
> you could use `CString` and use the `.as_ptr()` method to get a
> null-terminated string. Not sure if it would be nicer that what you
> currently have though.

These two were nice. Thanks.

> At
> https://github.com/vireshk/libgpiod/blob/3e7fb99173856a3995360fc3fad51220c4b4e90e/bindings/rust/libgpiod/src/edge_event.rs#L46
> the lifetimes are unclear. Is Event allowed to outlive the buffer?
> Can you add a lifetime annotation like fn event_clone<'a>(event:
> &Event<'a>) -> Result<Event<'a>> if it isn't allowed to outlive the
> buffer or fn event_clone<'a, 'b>(event: &Event<'a>) ->
> Result<Event<'b>> if it is allowed to outlive the buffer. I'm not
> sure which of the two the lifetime elision rules cause the current
> code to be equivalent of, but even if it is correct, explicitly
> stating the lifetime here is clearer IMHO.

An Event created using Event::new() can't outlive the buffer, though
an Event created using Event::event_clone() can.

I tried to play around it based on your suggestion and here is the
diff, does it make sense ?

diff --git a/bindings/rust/libgpiod/src/edge_event.rs b/bindings/rust/libgpiod/src/edge_event.rs
index b36c23601bb4..0d328ebb2b03 100644
--- a/bindings/rust/libgpiod/src/edge_event.rs
+++ b/bindings/rust/libgpiod/src/edge_event.rs
@@ -33,7 +33,7 @@ pub struct Event<'b> {

 impl<'b> Event<'b> {
     /// Get an event stored in the buffer.
-    pub(crate) fn new(buffer: &'b Buffer, index: usize) -> Result<Self> {
+    pub(crate) fn new(buffer: &'b Buffer, index: usize) -> Result<Event<'b>> {
         // SAFETY: The `gpiod_edge_event` returned by libgpiod is guaranteed to live as long
         // as the `struct Event`.
         let event = unsafe {
@@ -52,22 +52,6 @@ impl<'b> Event<'b> {
         })
     }

-    pub fn event_clone(event: &Event) -> Result<Self> {
-        // SAFETY: `gpiod_edge_event` is guaranteed to be valid here.
-        let event = unsafe { gpiod::gpiod_edge_event_copy(event.event) };
-        if event.is_null() {
-            return Err(Error::OperationFailed(
-                OperationType::EdgeEventCopy,
-                Errno::last(),
-            ));
-        }
-
-        Ok(Self {
-            buffer: None,
-            event,
-        })
-    }
-
     /// Get the event type.
     pub fn event_type(&self) -> Result<EdgeKind> {
         // SAFETY: `gpiod_edge_event` is guaranteed to be valid here.
@@ -105,6 +89,27 @@ impl<'b> Event<'b> {
     }
 }

+impl<'e, 'b> Event<'e> {
+    pub fn event_clone(event: &Event<'b>) -> Result<Event<'e>>
+    where
+        'e: 'b,
+    {
+        // SAFETY: `gpiod_edge_event` is guaranteed to be valid here.
+        let event = unsafe { gpiod::gpiod_edge_event_copy(event.event) };
+        if event.is_null() {
+            return Err(Error::OperationFailed(
+                OperationType::EdgeEventCopy,
+                Errno::last(),
+            ));
+        }
+
+        Ok(Self {
+            buffer: None,
+            event,
+        })
+    }
+}
+

-- 
viresh

^ permalink raw reply related	[flat|nested] 50+ messages in thread

* Re: [PATCH V7 5/8] libgpiod: Add gpiosim rust crate
  2022-10-17 13:00   ` Kent Gibson
@ 2022-10-21  9:56     ` Viresh Kumar
  0 siblings, 0 replies; 50+ messages in thread
From: Viresh Kumar @ 2022-10-21  9:56 UTC (permalink / raw)
  To: Kent Gibson
  Cc: Linus Walleij, Bartosz Golaszewski, Vincent Guittot, linux-gpio,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

On 17-10-22, 21:00, Kent Gibson wrote:
> > +/// Sim Ctx
> > +#[derive(Debug)]
> > +struct SimCtx {
> > +    ctx: *mut gpiosim_ctx,
> > +}
> > +
> > +unsafe impl Send for SimCtx {}
> > +unsafe impl Sync for SimCtx {}
> > +
> 
> Is gpiosim thread safe?

There is no locking in place at the moment, if two threads try to do
conflicting operations at the same time. Nothing will stop them I
believe.

Though this module is only used for carrying out the tests at the
moment, where we are generating events from parallel threads. I am not
sure if we should make it more complex, for example with locking. Or
is there something else we can do ?

-- 
viresh

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH V7 3/8] libgpiod: Add rust wrapper crate
  2022-10-17 12:59   ` Kent Gibson
@ 2022-10-21 11:22     ` Viresh Kumar
  2022-10-21 12:39       ` Kent Gibson
  0 siblings, 1 reply; 50+ messages in thread
From: Viresh Kumar @ 2022-10-21 11:22 UTC (permalink / raw)
  To: Kent Gibson
  Cc: Linus Walleij, Bartosz Golaszewski, Vincent Guittot, linux-gpio,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

Hi Kent,

Thanks a lot for putting so much effort in reviewing the series, yes
it makes me invest more time to the bindings, but it is totally worth
it. Excellent work.

I have pushed all the changed I have made so far in v8 branch in my
repository in the top few commits, in case you want to have a look.

On 17-10-22, 20:59, Kent Gibson wrote:
> On Fri, Oct 14, 2022 at 04:17:20PM +0530, Viresh Kumar wrote:
> > +use vmm_sys_util::errno::Error as Errno;
> 
> Why vmm_sys_util::errno::Error rather than, say, errno::Errno?

I used it in another project, vhost-device related to virtio, and so
ended up using here. The main advantage of using this over errno is
that we can do Errno::last() to get the error number of the last
operation.

Do you have a better alternative to that ?

> > +/// 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.
> > +#[derive(Clone, Debug, Eq, PartialEq)]
> > +pub struct Chip {
> > +    ichip: Arc<Internal>,
> > +    info: Info,
> > +}
> > +
> 
> Doesn't info need an Arc as well? (see struct Info below).

Why would that be required ? Internal required it as it can be stored
in two structures, Chip and Info, and should get freed after both of
them are out of scope.

Though Info have a single owner/user, i.e. Chip and once Chip is out
of scope, Info can be freed as well.

> Actually, does it make sense to be able to Clone the Chip?

I have removed Clone now, since Info couldn't be cloned anymore.

> Two threads could then watch different lines and
> wait_info_event() from the one chip.
> That isn't going to work, at least not the way you would want.
> 
> > +unsafe impl Send for Chip {}
> > +unsafe impl Sync for Chip {}
> > +
> 
> Send makes sense to me, but not Sync, due to the waiting for info events.
> Similarly for line_request::Request.
> At least not without some additional synchronisation/locking, and I'm not
> sure we want to go there.

Hmm, I added those for tests and now for the multi-threaded example I
have written. And I do get what you are saying, it is tricky to share
the same chip from two threads.

What should I do? Remove those example/tests and remove Sync ?

> > +impl Chip {
> > +    /// Find a chip by path.
> > +    pub fn open<P: AsRef<Path>>(path: &P) -> Result<Self> {
> > +        let ichip = Arc::new(Internal::open(path)?);
> > +        let info = Info::new(ichip.clone())?;
> > +
> 
> info is not always necessary, e.g. if the user just want to request a
> line.  Maybe lazy load it?  Assuming you want to keep a copy cached with
> the Chip at all.

Yeah, but then I will be required to add an additional check to below
routines to see if info is already available or not, and populate it
if required.

Or detach Info from Chip and remove below wrappers ?

> > +        Ok(Self { ichip, info })
> > +    }
> > +
> > +    /// Get the chip name as represented in the kernel.
> > +    pub fn name(&self) -> Result<&str> {
> > +        self.info.name()
> > +    }
> > +
> > +    /// Get the chip label as represented in the kernel.
> > +    pub fn label(&self) -> Result<&str> {
> > +        self.info.label()
> > +    }
> > +
> > +    /// Get the number of GPIO lines exposed by the chip.
> > +    pub fn num_lines(&self) -> usize {
> > +        self.info.num_lines()
> > +    }


> > +impl Buffer {
> > +    /// 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: usize) -> Result<Self> {
> > +        let buffer = unsafe { gpiod::gpiod_edge_event_buffer_new(capacity as c_ulong) };
> > +        if buffer.is_null() {
> > +            return Err(Error::OperationFailed(
> > +                OperationType::EdgeEventBufferNew,
> > +                Errno::last(),
> > +            ));
> > +        }
> > +
> > +        Ok(Self { buffer })
> > +    }
> > +
> > +    /// Private helper, Returns gpiod_edge_event_buffer
> > +    pub(crate) fn buffer(&self) -> *mut gpiod::gpiod_edge_event_buffer {
> > +        self.buffer
> > +    }
> 
> Just make self.buffer pub(crate)?  Why is the idea of passing a raw
> pointer around making my skin crawl?
> Or even better, move the guts of
> line_request::Request::read_edge_events() into a helper function here
> that it can call so it doesn't need access to self.buffer.

But then this piece of code would need to access self.request as the
API is:

            gpiod::gpiod_line_request_read_edge_event(
                self.request,
                buffer.buffer,
                buffer.capacity().try_into().unwrap(),
            )

It needs raw pointers to both buffer and request.

> > +
> > +    /// Get the capacity of the event buffer.
> > +    pub fn capacity(&self) -> usize {
> > +        unsafe { gpiod::gpiod_edge_event_buffer_get_capacity(self.buffer()) as usize }
> > +    }
> > +
> > +    /// Read an event stored in the buffer.
> > +    pub fn event(&self, index: u64) -> Result<edge::Event> {
> > +        edge::Event::new(self, index)
> > +    }
> 
> What is to prevent reading an event from a slot that has not yet been
> populated?  I realise doing that is possible in C, but it should be
> prevented in the Rust bindings.

Good point, I have done this now:

event_buffer.rs:

    /// Set the number of events read into the event buffer.
    pub fn set_event_count(&mut self, count: usize) {
        self.event_count = count
    }

    /// Read an event stored in the buffer.
    pub fn event(&self, index: usize) -> Result<request::Event> {
        if index >= self.event_count {
            Err(Error::InvalidArguments)
        } else {
            request::Event::new(self, index)
        }
    }

line_request.rs:

    /// 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_events(&self, buffer: &mut request::Buffer) -> Result<u32> {
        // SAFETY: `gpiod_line_request` is guaranteed to be valid here.
        let ret = unsafe {
            gpiod::gpiod_line_request_read_edge_event(
                self.request,
                buffer.buffer,
                buffer.capacity().try_into().unwrap(),
            )
        };

        if ret == -1 {
            Err(Error::OperationFailed(
                OperationType::LineRequestReadEdgeEvent,
                Errno::last(),
            ))
        } else {
            // Set count of events read in the buffer
            buffer.set_event_count(ret as usize);
            Ok(ret as u32)
        }
    }


Good enough ?

> Not sure why event_buffer needs to be nested inside an event module.
> event_buffer should be in the request module.
> 
> edge_event is a tougher call, either line or request.  Probably the
> latter.

Is this okay now ? I had to rename chip.rs to chip_internal.rs in
order to include info_event within it.

mod chip_internal;
mod info_event;

pub mod chip {
    /// GPIO chip related definitions.
    pub use crate::chip_internal::*;

    pub mod info {
        /// GPIO chip info event related definitions.
        pub use crate::info_event::*;
    }
}

mod edge_event;
mod event_buffer;
mod line_request;
mod request_config;

/// GPIO chip request related definitions.
pub mod request {
    pub use crate::edge_event::*;
    pub use crate::event_buffer::*;
    pub use crate::line_request::*;
    pub use crate::request_config::*;
}

mod line_config;
mod line_info;
mod line_settings;

/// GPIO chip line related definitions.
pub mod line {
    pub use crate::line_config::*;
    pub use crate::line_info::*;
    pub use crate::line_settings::*;
}

> > +/// Iterator for GPIO devices.
> > +pub fn gpiochip_devices<P: AsRef<Path>>(path: &P) -> Result<Vec<chip::Chip>> {
> > +    let mut chips = Vec::new();
> > +
> > +    for entry in fs::read_dir(path).map_err(|_| Error::IoError)?.flatten() {
> > +        let path = entry.path();
> > +
> > +        if is_gpiochip_device(&path) {
> > +            chips.push(chip::Chip::open(&path)?);
> > +        }
> > +    }
> 
> If there is a symlink to a device both in the path, or multiple symlinks
> to the same device, you will get the same chip several times in the
> returned Vec?

Probably yeah.

> Some mention of the order in which devices are returned?

Documentation of read_dir() says:

The order in which this iterator returns entries is platform and filesystem dependent.

mentioned same in comment now.

> If you are returning a Vec then sorted might be nice, else state they
> are not sorted.

What should we here then ?

> > +/// Get the API version of the library as a human-readable string.
> > +pub fn version_string() -> Result<&'static str> {
> > +    // SAFETY: The string returned by libgpiod is guaranteed to live forever.
> > +    let version = unsafe { gpiod::gpiod_version_string() };
> > +
> > +    if version.is_null() {
> > +        return Err(Error::NullString("GPIO library version"));
> > +    }
> > +
> > +    // SAFETY: The string is guaranteed to be valid here by the C API.
> > +    str::from_utf8(unsafe { slice::from_raw_parts(version as *const u8, strlen(version) as usize) })
> > +        .map_err(Error::StringNotUtf8)
> > +}
> 
> What if the Rust bindings version differ from the libgpiod version?

Hmm, not sure which version should we return here. I think it should
just be libgpiod's version. Though I am fine with whatever you and
Bartosz decide.

> > +    /// 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_events(&self, buffer: &mut edge::event::Buffer) -> Result<u32> {
> 
> What is to stop the user requesting events between the len returned
> here and the end of the buffer?  Why knows what state they are in.

I have shared a change for the same earlier, is that enough ?

> Overall I don't see any problems that require major rework to the scale
> of previous versions.  My main itches that need to be scratched being
> dangling pointers and to a lesser degree thread safey.
> 
> I find the event_buffer solution to be less safe than it could be.
> I've got an alternative, based on the my earlier PoC/suggestion, that
> returns an snaphot of events when you read events from the request into
> the buffer.  I've put that in a branch on github [1] if you want to take
> a look.
> 
> I went though a few variations wrt how to deal with the event cloning.
> Not implementing Clone on the events means they can't be stored in a
> Vec, and so storing them in the Buffer was problematic.
> My initial solution was for the event to only wrap the C pointer and
> have distinct event types for buffered and cloned, implemented
> using traits - which is bit of a pain as the user then needs to import
> the trait themselves to use the events :-(.
> Then tried a macro to add impl Event on the two event types, which looks
> ugly to me.
> Finally went with having Event be a new type of BaseEvent, which does
> impl Clone, so the event buffer can store them (and <cough> cast them
> to Event as appropriate).  Slightly more evil under the hood than I
> would like, but provides the cleanest implementation.
> 
> The Events snapshot should impl Iterator to make it easier to use,
> though I haven't added that yet.
> And the error returned by Events::event() is wrong - just cut and pasted
> one in there - probably should be InvalidArguments?
> 
> Anyway, the end result is that it is not possible to read an invalid or
> uninitialised event from Rust, which was the point.
> It also better handles lifetimes, so it isn't necessary to explicitly
> drop events before re-using the buffer, and the event_clone() method
> can be an instance method rather than a class method (to borrow some
> Python terminology).
> I've added an events.rs to the examples to demonstrate how the
> changes impact usage in each case.

I haven't looked into that in great details yet, just skimmed through
it. I am not sure I understand them completely yet and I am not sure
if it is worth the complexity, may be it is. I will try to get through
the changes again next week though.

Will it be possible to get on with the current implementation and once
this is merged you send patches on top of it which can then get
reviewed properly by others too, who know Rust better than me ?

-- 
viresh

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH V7 3/8] libgpiod: Add rust wrapper crate
  2022-10-18  3:27   ` Kent Gibson
@ 2022-10-21 11:25     ` Viresh Kumar
  0 siblings, 0 replies; 50+ messages in thread
From: Viresh Kumar @ 2022-10-21 11:25 UTC (permalink / raw)
  To: Kent Gibson
  Cc: Linus Walleij, Bartosz Golaszewski, Vincent Guittot, linux-gpio,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

On 18-10-22, 11:27, Kent Gibson wrote:
> One more thing - I overlooked trying to build on 32-bit while reviewing.
> (in this case on a Raspberry Pi 4)

I missed running this :(

cargo build --target arm-unknown-linux-gnueabi

I have these changes on the top and yet I have an error which I am not
sure how to fix here without special if/else code per target.

diff --git a/bindings/rust/gpiosim/src/sim.rs b/bindings/rust/gpiosim/src/sim.rs
index 4dd4adfa0d3f..dfa22fbec94b 100644
--- a/bindings/rust/gpiosim/src/sim.rs
+++ b/bindings/rust/gpiosim/src/sim.rs
@@ -186,7 +186,7 @@ impl SimBank {

     fn set_num_lines(&self, num: u64) -> Result<()> {
         // SAFETY: `gpiosim_bank` is guaranteed to be valid here.
-        let ret = unsafe { gpiosim_bank_set_num_lines(self.bank, num) };
+        let ret = unsafe { gpiosim_bank_set_num_lines(self.bank, num.try_into().unwrap()) };
         if ret == -1 {
             Err(Error::OperationFailed(
                 OperationType::SimBankSetNumLines,
diff --git a/bindings/rust/libgpiod/src/edge_event.rs b/bindings/rust/libgpiod/src/edge_event.rs
index 0d328ebb2b03..87c5af228e2b 100644
--- a/bindings/rust/libgpiod/src/edge_event.rs
+++ b/bindings/rust/libgpiod/src/edge_event.rs
@@ -76,7 +76,7 @@ impl<'b> Event<'b> {
     /// associated line request.
     pub fn global_seqno(&self) -> u64 {
         // SAFETY: `gpiod_edge_event` is guaranteed to be valid here.
-        unsafe { gpiod::gpiod_edge_event_get_global_seqno(self.event) }
+        unsafe { gpiod::gpiod_edge_event_get_global_seqno(self.event) as u64 }
     }

     /// Get the event sequence number specific to concerned line.
@@ -85,7 +85,7 @@ impl<'b> Event<'b> {
     /// lifetime of the associated line request.
     pub fn line_seqno(&self) -> u64 {
         // SAFETY: `gpiod_edge_event` is guaranteed to be valid here.
-        unsafe { gpiod::gpiod_edge_event_get_line_seqno(self.event) }
+        unsafe { gpiod::gpiod_edge_event_get_line_seqno(self.event) as u64 }
     }
 }

diff --git a/bindings/rust/libgpiod/src/line_info.rs b/bindings/rust/libgpiod/src/line_info.rs
index a458e72e0b12..aa1824cf3e80 100644
--- a/bindings/rust/libgpiod/src/line_info.rs
+++ b/bindings/rust/libgpiod/src/line_info.rs
@@ -144,7 +144,9 @@ impl Info {
     /// Get the debounce period of the line.
     pub fn debounce_period(&self) -> Duration {
         // SAFETY: `gpiod_line_info` is guaranteed to be valid here.
-        Duration::from_micros(unsafe { gpiod::gpiod_line_info_get_debounce_period_us(self.info) })
+        Duration::from_micros(unsafe {
+            gpiod::gpiod_line_info_get_debounce_period_us(self.info).into()
+        })
     }
 }

diff --git a/bindings/rust/libgpiod/src/line_settings.rs b/bindings/rust/libgpiod/src/line_settings.rs
index d422ad1ea68f..4df3efb1a93f 100644
--- a/bindings/rust/libgpiod/src/line_settings.rs
+++ b/bindings/rust/libgpiod/src/line_settings.rs
@@ -220,7 +220,7 @@ impl Settings {
         unsafe {
             gpiod::gpiod_line_settings_set_debounce_period_us(
                 self.settings,
-                period.as_micros() as u64,
+                (period.as_micros() as usize).try_into().unwrap(),
             );
         }

@@ -231,7 +231,7 @@ impl Settings {
     pub fn debounce_period(&self) -> Result<Duration> {
         // SAFETY: `gpiod_line_settings` is guaranteed to be valid here.
         Ok(Duration::from_micros(unsafe {
-            gpiod::gpiod_line_settings_get_debounce_period_us(self.settings)
+            gpiod::gpiod_line_settings_get_debounce_period_us(self.settings).into()
         }))
     }

diff --git a/bindings/rust/libgpiod/tests/chip.rs b/bindings/rust/libgpiod/tests/chip.rs
index aaee910e457f..a7fdaadf4da1 100644
--- a/bindings/rust/libgpiod/tests/chip.rs
+++ b/bindings/rust/libgpiod/tests/chip.rs
@@ -90,7 +90,10 @@ mod chip {
             // Failure
             assert_eq!(
                 chip.line_offset_from_name("nonexistent").unwrap_err(),
-                ChipError::OperationFailed(OperationType::ChipGetLineOffsetFromName, Errno::new(ENOENT))
+                ChipError::OperationFailed(
+                    OperationType::ChipGetLineOffsetFromName,
+                    Errno::new(ENOENT),
+                )
             );
         }
     }
$


-------------------------8<-------------------------

Current failures:

error[E0308]: mismatched types
   --> libgpiod/src/line_config.rs:105:78
    |
105 |         let ret = unsafe { gpiod::gpiod_line_config_get_offsets(self.config, &mut num, &mut ptr) };
    |                            ------------------------------------              ^^^^^^^^ expected `u32`, found `u64`
    |                            |
    |                            arguments to this function are incorrect
    |
    = note:    expected raw pointer `*mut u32`
            found mutable reference `&mut u64`
note: function defined here
   --> /mnt/ssd/all/work/repos/virtio/rust/libgpiod/bindings/rust/libgpiod-sys/src/bindings.rs:847:12
    |
847 |     pub fn gpiod_line_config_get_offsets(
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

For more information about this error, try `rustc --explain E0308`.
error: could not compile `libgpiod` due to previous error


-- 
viresh

^ permalink raw reply related	[flat|nested] 50+ messages in thread

* Re: [PATCH V7 3/8] libgpiod: Add rust wrapper crate
  2022-10-21 11:22     ` Viresh Kumar
@ 2022-10-21 12:39       ` Kent Gibson
  2022-10-27 12:09         ` Bartosz Golaszewski
  0 siblings, 1 reply; 50+ messages in thread
From: Kent Gibson @ 2022-10-21 12:39 UTC (permalink / raw)
  To: Viresh Kumar
  Cc: Linus Walleij, Bartosz Golaszewski, Vincent Guittot, linux-gpio,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

On Fri, Oct 21, 2022 at 04:52:38PM +0530, Viresh Kumar wrote:
> Hi Kent,
> 
> Thanks a lot for putting so much effort in reviewing the series, yes
> it makes me invest more time to the bindings, but it is totally worth
> it. Excellent work.
> 
> I have pushed all the changed I have made so far in v8 branch in my
> repository in the top few commits, in case you want to have a look.
> 
> On 17-10-22, 20:59, Kent Gibson wrote:
> > On Fri, Oct 14, 2022 at 04:17:20PM +0530, Viresh Kumar wrote:
> > > +use vmm_sys_util::errno::Error as Errno;
> > 
> > Why vmm_sys_util::errno::Error rather than, say, errno::Errno?
> 
> I used it in another project, vhost-device related to virtio, and so
> ended up using here. The main advantage of using this over errno is
> that we can do Errno::last() to get the error number of the last
> operation.
> 
> Do you have a better alternative to that ?
>

The one I mentioned is the top match for errno on crates.io[1].
That provides the same functionality, and that is all it does.
All other things being equal I would go for the simplest dependency.

[1] https://crates.io/crates/errno


> > > +/// 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.
> > > +#[derive(Clone, Debug, Eq, PartialEq)]
> > > +pub struct Chip {
> > > +    ichip: Arc<Internal>,
> > > +    info: Info,
> > > +}
> > > +
> > 
> > Doesn't info need an Arc as well? (see struct Info below).
> 
> Why would that be required ? Internal required it as it can be stored
> in two structures, Chip and Info, and should get freed after both of
> them are out of scope.
> 
> Though Info have a single owner/user, i.e. Chip and once Chip is out
> of scope, Info can be freed as well.
> 
> > Actually, does it make sense to be able to Clone the Chip?
> 
> I have removed Clone now, since Info couldn't be cloned anymore.
> 

Exactly - if Info can't clone then you could Arc it.
But if the Chip can't clone then problem solved.

> > Two threads could then watch different lines and
> > wait_info_event() from the one chip.
> > That isn't going to work, at least not the way you would want.
> > 
> > > +unsafe impl Send for Chip {}
> > > +unsafe impl Sync for Chip {}
> > > +
> > 
> > Send makes sense to me, but not Sync, due to the waiting for info events.
> > Similarly for line_request::Request.
> > At least not without some additional synchronisation/locking, and I'm not
> > sure we want to go there.
> 
> Hmm, I added those for tests and now for the multi-threaded example I
> have written. And I do get what you are saying, it is tricky to share
> the same chip from two threads.
> 
> What should I do? Remove those example/tests and remove Sync ?
> 

I would remove Sync support until you are satisfied you have a working
solution for it.

> > > +impl Chip {
> > > +    /// Find a chip by path.
> > > +    pub fn open<P: AsRef<Path>>(path: &P) -> Result<Self> {
> > > +        let ichip = Arc::new(Internal::open(path)?);
> > > +        let info = Info::new(ichip.clone())?;
> > > +
> > 
> > info is not always necessary, e.g. if the user just want to request a
> > line.  Maybe lazy load it?  Assuming you want to keep a copy cached with
> > the Chip at all.
> 
> Yeah, but then I will be required to add an additional check to below
> routines to see if info is already available or not, and populate it
> if required.
> 
> Or detach Info from Chip and remove below wrappers ?
> 

The usual way is to add a helper function that returns the lazy loaded
object.  If it hasn't been loaded yet then it does that first.
And your wrapper functions use that to access the info.

Or, as you suggest, just let the user get the Info and use its
accessors.

Either way works for me.

> > > +        Ok(Self { ichip, info })
> > > +    }
> > > +
> > > +    /// Get the chip name as represented in the kernel.
> > > +    pub fn name(&self) -> Result<&str> {
> > > +        self.info.name()
> > > +    }
> > > +
> > > +    /// Get the chip label as represented in the kernel.
> > > +    pub fn label(&self) -> Result<&str> {
> > > +        self.info.label()
> > > +    }
> > > +
> > > +    /// Get the number of GPIO lines exposed by the chip.
> > > +    pub fn num_lines(&self) -> usize {
> > > +        self.info.num_lines()
> > > +    }
> 
> 
> > > +impl Buffer {
> > > +    /// 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: usize) -> Result<Self> {
> > > +        let buffer = unsafe { gpiod::gpiod_edge_event_buffer_new(capacity as c_ulong) };
> > > +        if buffer.is_null() {
> > > +            return Err(Error::OperationFailed(
> > > +                OperationType::EdgeEventBufferNew,
> > > +                Errno::last(),
> > > +            ));
> > > +        }
> > > +
> > > +        Ok(Self { buffer })
> > > +    }
> > > +
> > > +    /// Private helper, Returns gpiod_edge_event_buffer
> > > +    pub(crate) fn buffer(&self) -> *mut gpiod::gpiod_edge_event_buffer {
> > > +        self.buffer
> > > +    }
> > 
> > Just make self.buffer pub(crate)?  Why is the idea of passing a raw
> > pointer around making my skin crawl?
> > Or even better, move the guts of
> > line_request::Request::read_edge_events() into a helper function here
> > that it can call so it doesn't need access to self.buffer.
> 
> But then this piece of code would need to access self.request as the
> API is:
> 
>             gpiod::gpiod_line_request_read_edge_event(
>                 self.request,
>                 buffer.buffer,
>                 buffer.capacity().try_into().unwrap(),
>             )
> 

Yep - you pass the Request to the Buffer, rather than have the Request
doing the work on the buffer.

IIRC my branch includes my version of this.

> It needs raw pointers to both buffer and request.
> 
> > > +
> > > +    /// Get the capacity of the event buffer.
> > > +    pub fn capacity(&self) -> usize {
> > > +        unsafe { gpiod::gpiod_edge_event_buffer_get_capacity(self.buffer()) as usize }
> > > +    }
> > > +
> > > +    /// Read an event stored in the buffer.
> > > +    pub fn event(&self, index: u64) -> Result<edge::Event> {
> > > +        edge::Event::new(self, index)
> > > +    }
> > 
> > What is to prevent reading an event from a slot that has not yet been
> > populated?  I realise doing that is possible in C, but it should be
> > prevented in the Rust bindings.
> 
> Good point, I have done this now:
> 
> event_buffer.rs:
> 
>     /// Set the number of events read into the event buffer.
>     pub fn set_event_count(&mut self, count: usize) {
>         self.event_count = count
>     }
> 

pub(crate) else the user can mess with the threshold.

>     /// Read an event stored in the buffer.
>     pub fn event(&self, index: usize) -> Result<request::Event> {
>         if index >= self.event_count {
>             Err(Error::InvalidArguments)
>         } else {
>             request::Event::new(self, index)
>         }
>     }
> 
> line_request.rs:
> 
>     /// 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_events(&self, buffer: &mut request::Buffer) -> Result<u32> {
>         // SAFETY: `gpiod_line_request` is guaranteed to be valid here.
>         let ret = unsafe {
>             gpiod::gpiod_line_request_read_edge_event(
>                 self.request,
>                 buffer.buffer,
>                 buffer.capacity().try_into().unwrap(),
>             )
>         };
> 
>         if ret == -1 {
>             Err(Error::OperationFailed(
>                 OperationType::LineRequestReadEdgeEvent,
>                 Errno::last(),
>             ))
>         } else {
>             // Set count of events read in the buffer
>             buffer.set_event_count(ret as usize);
>             Ok(ret as u32)
>         }
>     }
> 
> 
> Good enough ?
> 

Better.

> > Not sure why event_buffer needs to be nested inside an event module.
> > event_buffer should be in the request module.
> > 
> > edge_event is a tougher call, either line or request.  Probably the
> > latter.
> 
> Is this okay now ? I had to rename chip.rs to chip_internal.rs in
> order to include info_event within it.
> 
> mod chip_internal;
> mod info_event;
> 
> pub mod chip {
>     /// GPIO chip related definitions.
>     pub use crate::chip_internal::*;
> 
>     pub mod info {
>         /// GPIO chip info event related definitions.
>         pub use crate::info_event::*;
>     }
> }
> 

Couldn't you just add those pubs to the old chip file?
So you don't need to rename it to chip_internal.

> mod edge_event;
> mod event_buffer;
> mod line_request;
> mod request_config;
> 
> /// GPIO chip request related definitions.
> pub mod request {
>     pub use crate::edge_event::*;
>     pub use crate::event_buffer::*;
>     pub use crate::line_request::*;
>     pub use crate::request_config::*;
> }
> 
> mod line_config;
> mod line_info;
> mod line_settings;
> 
> /// GPIO chip line related definitions.
> pub mod line {
>     pub use crate::line_config::*;
>     pub use crate::line_info::*;
>     pub use crate::line_settings::*;
> }
> 
> > > +/// Iterator for GPIO devices.
> > > +pub fn gpiochip_devices<P: AsRef<Path>>(path: &P) -> Result<Vec<chip::Chip>> {
> > > +    let mut chips = Vec::new();
> > > +
> > > +    for entry in fs::read_dir(path).map_err(|_| Error::IoError)?.flatten() {
> > > +        let path = entry.path();
> > > +
> > > +        if is_gpiochip_device(&path) {
> > > +            chips.push(chip::Chip::open(&path)?);
> > > +        }
> > > +    }
> > 
> > If there is a symlink to a device both in the path, or multiple symlinks
> > to the same device, you will get the same chip several times in the
> > returned Vec?
> 
> Probably yeah.
> 

Lets say I want to implement a search for line by name, and check that
the line name is unique.  That will fail if there is a symlink to the
relevant device, cos it will see a spurious duplicate.
How would I do that then?

> > Some mention of the order in which devices are returned?
> 
> Documentation of read_dir() says:
> 
> The order in which this iterator returns entries is platform and filesystem dependent.
> 
> mentioned same in comment now.
> 
> > If you are returning a Vec then sorted might be nice, else state they
> > are not sorted.
> 
> What should we here then ?
> 

From my PoV, the two options are either
    a)wrap read_dir and return an actual iterator and live with the disorder
 or b) return a Vec and sort it before you return it

Returning an unsorted Vec is the worst of both worlds.

> > > +/// Get the API version of the library as a human-readable string.
> > > +pub fn version_string() -> Result<&'static str> {
> > > +    // SAFETY: The string returned by libgpiod is guaranteed to live forever.
> > > +    let version = unsafe { gpiod::gpiod_version_string() };
> > > +
> > > +    if version.is_null() {
> > > +        return Err(Error::NullString("GPIO library version"));
> > > +    }
> > > +
> > > +    // SAFETY: The string is guaranteed to be valid here by the C API.
> > > +    str::from_utf8(unsafe { slice::from_raw_parts(version as *const u8, strlen(version) as usize) })
> > > +        .map_err(Error::StringNotUtf8)
> > > +}
> > 
> > What if the Rust bindings version differ from the libgpiod version?
> 
> Hmm, not sure which version should we return here. I think it should
> just be libgpiod's version. Though I am fine with whatever you and
> Bartosz decide.
> 

You need both - separately.

> > > +    /// 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_events(&self, buffer: &mut edge::event::Buffer) -> Result<u32> {
> > 
> > What is to stop the user requesting events between the len returned
> > here and the end of the buffer?  Why knows what state they are in.
> 
> I have shared a change for the same earlier, is that enough ?
> 
> > Overall I don't see any problems that require major rework to the scale
> > of previous versions.  My main itches that need to be scratched being
> > dangling pointers and to a lesser degree thread safey.
> > 
> > I find the event_buffer solution to be less safe than it could be.
> > I've got an alternative, based on the my earlier PoC/suggestion, that
> > returns an snaphot of events when you read events from the request into
> > the buffer.  I've put that in a branch on github [1] if you want to take
> > a look.
> > 
> > I went though a few variations wrt how to deal with the event cloning.
> > Not implementing Clone on the events means they can't be stored in a
> > Vec, and so storing them in the Buffer was problematic.
> > My initial solution was for the event to only wrap the C pointer and
> > have distinct event types for buffered and cloned, implemented
> > using traits - which is bit of a pain as the user then needs to import
> > the trait themselves to use the events :-(.
> > Then tried a macro to add impl Event on the two event types, which looks
> > ugly to me.
> > Finally went with having Event be a new type of BaseEvent, which does
> > impl Clone, so the event buffer can store them (and <cough> cast them
> > to Event as appropriate).  Slightly more evil under the hood than I
> > would like, but provides the cleanest implementation.
> > 
> > The Events snapshot should impl Iterator to make it easier to use,
> > though I haven't added that yet.
> > And the error returned by Events::event() is wrong - just cut and pasted
> > one in there - probably should be InvalidArguments?
> > 
> > Anyway, the end result is that it is not possible to read an invalid or
> > uninitialised event from Rust, which was the point.
> > It also better handles lifetimes, so it isn't necessary to explicitly
> > drop events before re-using the buffer, and the event_clone() method
> > can be an instance method rather than a class method (to borrow some
> > Python terminology).
> > I've added an events.rs to the examples to demonstrate how the
> > changes impact usage in each case.
> 
> I haven't looked into that in great details yet, just skimmed through
> it. I am not sure I understand them completely yet and I am not sure
> if it is worth the complexity, may be it is. I will try to get through
> the changes again next week though.
> 

Clearly I think it is or I wouldn't've bothered?
So not sure what you would like me to add, and I'm not going to push for
it any more than I already have.  If you have the time and inclination
then give it a look.

> Will it be possible to get on with the current implementation and once
> this is merged you send patches on top of it which can then get
> reviewed properly by others too, who know Rust better than me ?
> 

The Rust bindings are your baby, and I don't have any plans to be
patching it.  And Bart is the arbiter of what gets merged, so that
decision is his.

Cheers,
Kent.

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH V7 0/8] libgpiod: Add Rust bindings
  2022-10-21  9:39     ` Viresh Kumar
@ 2022-10-21 14:34       ` Björn Roy Baron
  2022-10-25  6:42         ` Viresh Kumar
  0 siblings, 1 reply; 50+ messages in thread
From: Björn Roy Baron @ 2022-10-21 14:34 UTC (permalink / raw)
  To: Viresh Kumar
  Cc: Miguel Ojeda, Linus Walleij, Bartosz Golaszewski,
	Vincent Guittot, linux-gpio, Kent Gibson, Alex Bennée,
	stratos-dev, Gerard Ryan, Wedson Almeida Filho, Alex Gaynor,
	Boqun Feng, Gary Guo, y86-dev

On Friday, October 21st, 2022 at 11:39, Viresh Kumar <viresh.kumar@linaro.org> wrote:

> Hi Björn,
> 
> I have bounced (mutt's feature) the initial emails to your and other
> email ids that Miguel added. The patches should be in your inbox now.

Thanks! I receive the patches.

> 
> On 20-10-22, 13:29, Björn Roy Baron wrote:
> 
> > At
> > https://github.com/vireshk/libgpiod/blob/3e7fb99173856a3995360fc3fad51220c4b4e90e/bindings/rust/libgpiod/src/lib.rs#L469
> > and elsewhere you might want to use `CStr::from_ptr(version)`. This
> > does the `strlen` call for you and you can convert it to an `&str`
> > using `.to_str()`.
> 
> > At
> > https://github.com/vireshk/libgpiod/blob/3e7fb99173856a3995360fc3fad51220c4b4e90e/bindings/rust/libgpiod/src/chip.rs#L171
> > you could use `CString` and use the `.as_ptr()` method to get a
> > null-terminated string. Not sure if it would be nicer that what you
> > currently have though.
> 
> 
> These two were nice. Thanks.
> 
> > At
> > https://github.com/vireshk/libgpiod/blob/3e7fb99173856a3995360fc3fad51220c4b4e90e/bindings/rust/libgpiod/src/edge_event.rs#L46
> > the lifetimes are unclear. Is Event allowed to outlive the buffer?
> > Can you add a lifetime annotation like fn event_clone<'a>(event:
> > &Event<'a>) -> Result<Event<'a>> if it isn't allowed to outlive the
> > buffer or fn event_clone<'a, 'b>(event: &Event<'a>) ->
> > Result<Event<'b>> if it is allowed to outlive the buffer. I'm not
> > sure which of the two the lifetime elision rules cause the current
> > code to be equivalent of, but even if it is correct, explicitly
> > stating the lifetime here is clearer IMHO.
> 
> 
> An Event created using Event::new() can't outlive the buffer, though
> an Event created using Event::event_clone() can.
> 
> I tried to play around it based on your suggestion and here is the
> diff, does it make sense ?
> 
> diff --git a/bindings/rust/libgpiod/src/edge_event.rs b/bindings/rust/libgpiod/src/edge_event.rs
> index b36c23601bb4..0d328ebb2b03 100644
> --- a/bindings/rust/libgpiod/src/edge_event.rs
> +++ b/bindings/rust/libgpiod/src/edge_event.rs
> @@ -33,7 +33,7 @@ pub struct Event<'b> {
> 
> 
> impl<'b> Event<'b> {
> 
> /// Get an event stored in the buffer.
> - pub(crate) fn new(buffer: &'b Buffer, index: usize) -> Result<Self> {
> 
> + pub(crate) fn new(buffer: &'b Buffer, index: usize) -> Result<Event<'b>> {

This looks good to me.

> 
> // SAFETY: The `gpiod_edge_event` returned by libgpiod is guaranteed to live as long
> // as the `struct Event`.
> let event = unsafe {
> @@ -52,22 +52,6 @@ impl<'b> Event<'b> {
> 
> })
> }
> 
> - pub fn event_clone(event: &Event) -> Result<Self> {
> 
> - // SAFETY: `gpiod_edge_event` is guaranteed to be valid here.
> - let event = unsafe { gpiod::gpiod_edge_event_copy(event.event) };
> - if event.is_null() {
> - return Err(Error::OperationFailed(
> - OperationType::EdgeEventCopy,
> - Errno::last(),
> - ));
> - }
> -
> - Ok(Self {
> - buffer: None,
> - event,
> - })
> - }
> -
> /// Get the event type.
> pub fn event_type(&self) -> Result<EdgeKind> {
> 
> // SAFETY: `gpiod_edge_event` is guaranteed to be valid here.
> @@ -105,6 +89,27 @@ impl<'b> Event<'b> {
> 
> }
> }
> 
> +impl<'e, 'b> Event<'e> {
> 
> + pub fn event_clone(event: &Event<'b>) -> Result<Event<'e>>
> 
> + where
> + 'e: 'b,

Using `Event<'b>` on both sides should work fine. `Event` is covariant in it's lifetime parameter, so `Event<'b>` can be turned into `Event<'e>` with `'e` being a shorter lifetime than `'b`. What you wrote here is not incorrect, so if you prefer keeping it this way that is fine with me.

> + {
> + // SAFETY: `gpiod_edge_event` is guaranteed to be valid here.
> + let event = unsafe { gpiod::gpiod_edge_event_copy(event.event) };
> + if event.is_null() {
> + return Err(Error::OperationFailed(
> + OperationType::EdgeEventCopy,
> + Errno::last(),
> + ));
> + }
> +
> + Ok(Self {
> + buffer: None,
> + event,
> + })
> + }
> +}
> +
> 
> --
> viresh

Cheers,
Björn

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH V7 0/8] libgpiod: Add Rust bindings
  2022-10-21 14:34       ` Björn Roy Baron
@ 2022-10-25  6:42         ` Viresh Kumar
  2022-10-29 17:46           ` Björn Roy Baron
  0 siblings, 1 reply; 50+ messages in thread
From: Viresh Kumar @ 2022-10-25  6:42 UTC (permalink / raw)
  To: Björn Roy Baron
  Cc: Miguel Ojeda, Linus Walleij, Bartosz Golaszewski,
	Vincent Guittot, linux-gpio, Kent Gibson, Alex Bennée,
	stratos-dev, Gerard Ryan, Wedson Almeida Filho, Alex Gaynor,
	Boqun Feng, Gary Guo, y86-dev

On 21-10-22, 14:34, Björn Roy Baron wrote:
> > impl<'b> Event<'b> {
> > 
> > /// Get an event stored in the buffer.
> > - pub(crate) fn new(buffer: &'b Buffer, index: usize) -> Result<Self> {
> > 
> > + pub(crate) fn new(buffer: &'b Buffer, index: usize) -> Result<Event<'b>> {
> 
> This looks good to me.
 
> > +impl<'e, 'b> Event<'e> {
> > 
> > + pub fn event_clone(event: &Event<'b>) -> Result<Event<'e>>
> > 
> > + where
> > + 'e: 'b,
> 
> Using `Event<'b>` on both sides should work fine. `Event` is
> covariant in it's lifetime parameter, so `Event<'b>` can be turned
> into `Event<'e>` with `'e` being a shorter lifetime than `'b`. What
> you wrote here is not incorrect, so if you prefer keeping it this
> way that is fine with me.

That doesn't let the cloned event to live past read_edge_events().

error[E0502]: cannot borrow `buffer` as mutable because it is also borrowed as immutable
  --> libgpiod/examples/gpio_events.rs:70:50
   |
64 |             let event = buffer.event(0)?;
   |                         --------------- immutable borrow occurs here
...
70 |             let count = request.read_edge_events(&mut buffer)?;
   |                                                  ^^^^^^^^^^^ mutable borrow occurs here
...
86 |         }
   |         - immutable borrow might be used here, when `cloned_event` is dropped and runs the `Drop` code for type `libgpiod::request::Event`

-- 
viresh

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH V7 3/8] libgpiod: Add rust wrapper crate
  2022-10-21 12:39       ` Kent Gibson
@ 2022-10-27 12:09         ` Bartosz Golaszewski
  2022-10-31 12:33           ` Kent Gibson
  0 siblings, 1 reply; 50+ messages in thread
From: Bartosz Golaszewski @ 2022-10-27 12:09 UTC (permalink / raw)
  To: Kent Gibson
  Cc: Viresh Kumar, Linus Walleij, Vincent Guittot, linux-gpio,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

On Fri, Oct 21, 2022 at 2:39 PM Kent Gibson <warthog618@gmail.com> wrote:
>
> On Fri, Oct 21, 2022 at 04:52:38PM +0530, Viresh Kumar wrote:
> > Hi Kent,
> >
> > Thanks a lot for putting so much effort in reviewing the series, yes
> > it makes me invest more time to the bindings, but it is totally worth
> > it. Excellent work.
> >
> > I have pushed all the changed I have made so far in v8 branch in my
> > repository in the top few commits, in case you want to have a look.
> >
> > On 17-10-22, 20:59, Kent Gibson wrote:
> > > On Fri, Oct 14, 2022 at 04:17:20PM +0530, Viresh Kumar wrote:
> > > > +use vmm_sys_util::errno::Error as Errno;
> > >
> > > Why vmm_sys_util::errno::Error rather than, say, errno::Errno?
> >
> > I used it in another project, vhost-device related to virtio, and so
> > ended up using here. The main advantage of using this over errno is
> > that we can do Errno::last() to get the error number of the last
> > operation.
> >
> > Do you have a better alternative to that ?
> >
>
> The one I mentioned is the top match for errno on crates.io[1].
> That provides the same functionality, and that is all it does.
> All other things being equal I would go for the simplest dependency.
>
> [1] https://crates.io/crates/errno
>
>
> > > > +/// 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.
> > > > +#[derive(Clone, Debug, Eq, PartialEq)]
> > > > +pub struct Chip {
> > > > +    ichip: Arc<Internal>,
> > > > +    info: Info,
> > > > +}
> > > > +
> > >
> > > Doesn't info need an Arc as well? (see struct Info below).
> >
> > Why would that be required ? Internal required it as it can be stored
> > in two structures, Chip and Info, and should get freed after both of
> > them are out of scope.
> >
> > Though Info have a single owner/user, i.e. Chip and once Chip is out
> > of scope, Info can be freed as well.
> >
> > > Actually, does it make sense to be able to Clone the Chip?
> >
> > I have removed Clone now, since Info couldn't be cloned anymore.
> >
>
> Exactly - if Info can't clone then you could Arc it.
> But if the Chip can't clone then problem solved.
>
> > > Two threads could then watch different lines and
> > > wait_info_event() from the one chip.
> > > That isn't going to work, at least not the way you would want.
> > >
> > > > +unsafe impl Send for Chip {}
> > > > +unsafe impl Sync for Chip {}
> > > > +
> > >
> > > Send makes sense to me, but not Sync, due to the waiting for info events.
> > > Similarly for line_request::Request.
> > > At least not without some additional synchronisation/locking, and I'm not
> > > sure we want to go there.
> >
> > Hmm, I added those for tests and now for the multi-threaded example I
> > have written. And I do get what you are saying, it is tricky to share
> > the same chip from two threads.
> >
> > What should I do? Remove those example/tests and remove Sync ?
> >
>
> I would remove Sync support until you are satisfied you have a working
> solution for it.
>
> > > > +impl Chip {
> > > > +    /// Find a chip by path.
> > > > +    pub fn open<P: AsRef<Path>>(path: &P) -> Result<Self> {
> > > > +        let ichip = Arc::new(Internal::open(path)?);
> > > > +        let info = Info::new(ichip.clone())?;
> > > > +
> > >
> > > info is not always necessary, e.g. if the user just want to request a
> > > line.  Maybe lazy load it?  Assuming you want to keep a copy cached with
> > > the Chip at all.
> >
> > Yeah, but then I will be required to add an additional check to below
> > routines to see if info is already available or not, and populate it
> > if required.
> >
> > Or detach Info from Chip and remove below wrappers ?
> >
>
> The usual way is to add a helper function that returns the lazy loaded
> object.  If it hasn't been loaded yet then it does that first.
> And your wrapper functions use that to access the info.
>
> Or, as you suggest, just let the user get the Info and use its
> accessors.
>
> Either way works for me.
>
> > > > +        Ok(Self { ichip, info })
> > > > +    }
> > > > +
> > > > +    /// Get the chip name as represented in the kernel.
> > > > +    pub fn name(&self) -> Result<&str> {
> > > > +        self.info.name()
> > > > +    }
> > > > +
> > > > +    /// Get the chip label as represented in the kernel.
> > > > +    pub fn label(&self) -> Result<&str> {
> > > > +        self.info.label()
> > > > +    }
> > > > +
> > > > +    /// Get the number of GPIO lines exposed by the chip.
> > > > +    pub fn num_lines(&self) -> usize {
> > > > +        self.info.num_lines()
> > > > +    }
> >
> >
> > > > +impl Buffer {
> > > > +    /// 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: usize) -> Result<Self> {
> > > > +        let buffer = unsafe { gpiod::gpiod_edge_event_buffer_new(capacity as c_ulong) };
> > > > +        if buffer.is_null() {
> > > > +            return Err(Error::OperationFailed(
> > > > +                OperationType::EdgeEventBufferNew,
> > > > +                Errno::last(),
> > > > +            ));
> > > > +        }
> > > > +
> > > > +        Ok(Self { buffer })
> > > > +    }
> > > > +
> > > > +    /// Private helper, Returns gpiod_edge_event_buffer
> > > > +    pub(crate) fn buffer(&self) -> *mut gpiod::gpiod_edge_event_buffer {
> > > > +        self.buffer
> > > > +    }
> > >
> > > Just make self.buffer pub(crate)?  Why is the idea of passing a raw
> > > pointer around making my skin crawl?
> > > Or even better, move the guts of
> > > line_request::Request::read_edge_events() into a helper function here
> > > that it can call so it doesn't need access to self.buffer.
> >
> > But then this piece of code would need to access self.request as the
> > API is:
> >
> >             gpiod::gpiod_line_request_read_edge_event(
> >                 self.request,
> >                 buffer.buffer,
> >                 buffer.capacity().try_into().unwrap(),
> >             )
> >
>
> Yep - you pass the Request to the Buffer, rather than have the Request
> doing the work on the buffer.
>
> IIRC my branch includes my version of this.
>
> > It needs raw pointers to both buffer and request.
> >
> > > > +
> > > > +    /// Get the capacity of the event buffer.
> > > > +    pub fn capacity(&self) -> usize {
> > > > +        unsafe { gpiod::gpiod_edge_event_buffer_get_capacity(self.buffer()) as usize }
> > > > +    }
> > > > +
> > > > +    /// Read an event stored in the buffer.
> > > > +    pub fn event(&self, index: u64) -> Result<edge::Event> {
> > > > +        edge::Event::new(self, index)
> > > > +    }
> > >
> > > What is to prevent reading an event from a slot that has not yet been
> > > populated?  I realise doing that is possible in C, but it should be
> > > prevented in the Rust bindings.
> >
> > Good point, I have done this now:
> >
> > event_buffer.rs:
> >
> >     /// Set the number of events read into the event buffer.
> >     pub fn set_event_count(&mut self, count: usize) {
> >         self.event_count = count
> >     }
> >
>
> pub(crate) else the user can mess with the threshold.
>
> >     /// Read an event stored in the buffer.
> >     pub fn event(&self, index: usize) -> Result<request::Event> {
> >         if index >= self.event_count {
> >             Err(Error::InvalidArguments)
> >         } else {
> >             request::Event::new(self, index)
> >         }
> >     }
> >
> > line_request.rs:
> >
> >     /// 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_events(&self, buffer: &mut request::Buffer) -> Result<u32> {
> >         // SAFETY: `gpiod_line_request` is guaranteed to be valid here.
> >         let ret = unsafe {
> >             gpiod::gpiod_line_request_read_edge_event(
> >                 self.request,
> >                 buffer.buffer,
> >                 buffer.capacity().try_into().unwrap(),
> >             )
> >         };
> >
> >         if ret == -1 {
> >             Err(Error::OperationFailed(
> >                 OperationType::LineRequestReadEdgeEvent,
> >                 Errno::last(),
> >             ))
> >         } else {
> >             // Set count of events read in the buffer
> >             buffer.set_event_count(ret as usize);
> >             Ok(ret as u32)
> >         }
> >     }
> >
> >
> > Good enough ?
> >
>
> Better.
>
> > > Not sure why event_buffer needs to be nested inside an event module.
> > > event_buffer should be in the request module.
> > >
> > > edge_event is a tougher call, either line or request.  Probably the
> > > latter.
> >
> > Is this okay now ? I had to rename chip.rs to chip_internal.rs in
> > order to include info_event within it.
> >
> > mod chip_internal;
> > mod info_event;
> >
> > pub mod chip {
> >     /// GPIO chip related definitions.
> >     pub use crate::chip_internal::*;
> >
> >     pub mod info {
> >         /// GPIO chip info event related definitions.
> >         pub use crate::info_event::*;
> >     }
> > }
> >
>
> Couldn't you just add those pubs to the old chip file?
> So you don't need to rename it to chip_internal.
>
> > mod edge_event;
> > mod event_buffer;
> > mod line_request;
> > mod request_config;
> >
> > /// GPIO chip request related definitions.
> > pub mod request {
> >     pub use crate::edge_event::*;
> >     pub use crate::event_buffer::*;
> >     pub use crate::line_request::*;
> >     pub use crate::request_config::*;
> > }
> >
> > mod line_config;
> > mod line_info;
> > mod line_settings;
> >
> > /// GPIO chip line related definitions.
> > pub mod line {
> >     pub use crate::line_config::*;
> >     pub use crate::line_info::*;
> >     pub use crate::line_settings::*;
> > }
> >
> > > > +/// Iterator for GPIO devices.
> > > > +pub fn gpiochip_devices<P: AsRef<Path>>(path: &P) -> Result<Vec<chip::Chip>> {
> > > > +    let mut chips = Vec::new();
> > > > +
> > > > +    for entry in fs::read_dir(path).map_err(|_| Error::IoError)?.flatten() {
> > > > +        let path = entry.path();
> > > > +
> > > > +        if is_gpiochip_device(&path) {
> > > > +            chips.push(chip::Chip::open(&path)?);
> > > > +        }
> > > > +    }
> > >
> > > If there is a symlink to a device both in the path, or multiple symlinks
> > > to the same device, you will get the same chip several times in the
> > > returned Vec?
> >
> > Probably yeah.
> >
>
> Lets say I want to implement a search for line by name, and check that
> the line name is unique.  That will fail if there is a symlink to the
> relevant device, cos it will see a spurious duplicate.
> How would I do that then?
>
> > > Some mention of the order in which devices are returned?
> >
> > Documentation of read_dir() says:
> >
> > The order in which this iterator returns entries is platform and filesystem dependent.
> >
> > mentioned same in comment now.
> >
> > > If you are returning a Vec then sorted might be nice, else state they
> > > are not sorted.
> >
> > What should we here then ?
> >
>
> From my PoV, the two options are either
>     a)wrap read_dir and return an actual iterator and live with the disorder
>  or b) return a Vec and sort it before you return it
>
> Returning an unsorted Vec is the worst of both worlds.
>
> > > > +/// Get the API version of the library as a human-readable string.
> > > > +pub fn version_string() -> Result<&'static str> {
> > > > +    // SAFETY: The string returned by libgpiod is guaranteed to live forever.
> > > > +    let version = unsafe { gpiod::gpiod_version_string() };
> > > > +
> > > > +    if version.is_null() {
> > > > +        return Err(Error::NullString("GPIO library version"));
> > > > +    }
> > > > +
> > > > +    // SAFETY: The string is guaranteed to be valid here by the C API.
> > > > +    str::from_utf8(unsafe { slice::from_raw_parts(version as *const u8, strlen(version) as usize) })
> > > > +        .map_err(Error::StringNotUtf8)
> > > > +}
> > >
> > > What if the Rust bindings version differ from the libgpiod version?
> >
> > Hmm, not sure which version should we return here. I think it should
> > just be libgpiod's version. Though I am fine with whatever you and
> > Bartosz decide.
> >
>
> You need both - separately.
>

For C++ and Python, the API version stays consistent across all three
trees for simplicity. There are separate ABI numbers for libgpiod,
libgpiodcxx and libgpiosim. Rust doesn't seem to have this kind of
differentiation between ABI and API so I'd say we should have a single
API version but see below about the idea for the contrib/ directory.

> > > > +    /// 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_events(&self, buffer: &mut edge::event::Buffer) -> Result<u32> {
> > >
> > > What is to stop the user requesting events between the len returned
> > > here and the end of the buffer?  Why knows what state they are in.
> >
> > I have shared a change for the same earlier, is that enough ?
> >
> > > Overall I don't see any problems that require major rework to the scale
> > > of previous versions.  My main itches that need to be scratched being
> > > dangling pointers and to a lesser degree thread safey.
> > >
> > > I find the event_buffer solution to be less safe than it could be.
> > > I've got an alternative, based on the my earlier PoC/suggestion, that
> > > returns an snaphot of events when you read events from the request into
> > > the buffer.  I've put that in a branch on github [1] if you want to take
> > > a look.
> > >
> > > I went though a few variations wrt how to deal with the event cloning.
> > > Not implementing Clone on the events means they can't be stored in a
> > > Vec, and so storing them in the Buffer was problematic.
> > > My initial solution was for the event to only wrap the C pointer and
> > > have distinct event types for buffered and cloned, implemented
> > > using traits - which is bit of a pain as the user then needs to import
> > > the trait themselves to use the events :-(.
> > > Then tried a macro to add impl Event on the two event types, which looks
> > > ugly to me.
> > > Finally went with having Event be a new type of BaseEvent, which does
> > > impl Clone, so the event buffer can store them (and <cough> cast them
> > > to Event as appropriate).  Slightly more evil under the hood than I
> > > would like, but provides the cleanest implementation.
> > >
> > > The Events snapshot should impl Iterator to make it easier to use,
> > > though I haven't added that yet.
> > > And the error returned by Events::event() is wrong - just cut and pasted
> > > one in there - probably should be InvalidArguments?
> > >
> > > Anyway, the end result is that it is not possible to read an invalid or
> > > uninitialised event from Rust, which was the point.
> > > It also better handles lifetimes, so it isn't necessary to explicitly
> > > drop events before re-using the buffer, and the event_clone() method
> > > can be an instance method rather than a class method (to borrow some
> > > Python terminology).
> > > I've added an events.rs to the examples to demonstrate how the
> > > changes impact usage in each case.
> >
> > I haven't looked into that in great details yet, just skimmed through
> > it. I am not sure I understand them completely yet and I am not sure
> > if it is worth the complexity, may be it is. I will try to get through
> > the changes again next week though.
> >
>
> Clearly I think it is or I wouldn't've bothered?
> So not sure what you would like me to add, and I'm not going to push for
> it any more than I already have.  If you have the time and inclination
> then give it a look.
>
> > Will it be possible to get on with the current implementation and once
> > this is merged you send patches on top of it which can then get
> > reviewed properly by others too, who know Rust better than me ?
> >
>
> The Rust bindings are your baby, and I don't have any plans to be
> patching it.  And Bart is the arbiter of what gets merged, so that
> decision is his.
>

I was thinking about it lately and figured that - since I don't know
Rust very well and cannot reliably maintain that part myself - how
about we create a 'contrib' directory in the libgpiod tree for this
kind of "third-party" bindings? The requirements for stability and
correctness in there would be more relaxed compared to the main tree?

And yes, we can think about pulling it into master soon - as soon as
the python bindings land and we ditch the next/libgpiod-v2.0 branch.

Bart

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH V7 1/8] libgpiod: Add libgpiod-sys rust crate
  2022-10-19 12:01               ` Kent Gibson
  2022-10-20  5:34                 ` Viresh Kumar
@ 2022-10-27 12:18                 ` Bartosz Golaszewski
  1 sibling, 0 replies; 50+ messages in thread
From: Bartosz Golaszewski @ 2022-10-27 12:18 UTC (permalink / raw)
  To: Kent Gibson
  Cc: Viresh Kumar, Linus Walleij, Vincent Guittot, linux-gpio,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

On Wed, Oct 19, 2022 at 2:02 PM Kent Gibson <warthog618@gmail.com> wrote:
>
> On Wed, Oct 19, 2022 at 04:52:51PM +0530, Viresh Kumar wrote:
> > On 19-10-22, 15:21, Kent Gibson wrote:
> > > That doesn't really work - you get a link to the files in github, not
> > > as a web site.  You need somewhere that will host those generated files
> > > as a web site.  You could do that with github-pages if you don't have other
> > > options.
> >
> > I don't have other options for now, so it is github.
> >
> > > When I've done that I would commit the docs to a separate
> > > branch, just for the docs, and have github-pages host that branch.
> >
> > Okay.
> >
> > > If you eventually publish your crate to crates.io you get documentation on
> > > docs.rs for free - and you can skip the documentation key in that case too
> > > - it defaults to the appropriate page on docs.rs.
> >
> > Right, that I knew.
> >
> > > I assume that would be the case long term - you just need to find
> > > someway to host them in the meantime.
> >
> > I wonder if we should be doing this at all right now, or if required
> > only Bartosz should do it instead of me, once this is merged ?
> >
>
> I was assuming it was an interim solution, so it doesn't matter so much.
>
> I'd be happy with docs.rs once libgpiod v2 is released and the bindings
> published to crates.io, and I'm personally good with looking at the
> generated docs locally in the meantime.
>
> It would be easier for others to take a look if the docs were hosted,
> but I don't have any feel as to whether that is worth the effort or not.
>
> > Maybe we need a libgpiod username for that on github ? To make it
> > independent of personal usernames ? From what I saw, the website name
> > will be username.github.io eventually.
> >
>
> A libgpiod group account might be useful.
>
> And it would be nice to have a libgpiod space somewhere, not just
> piggybacking on the gpio mailing list.  Not sure if github is the best
> place - but I haven't given it much thought.
>
> Bart, do you have any opinions?
>

For a long time I tried avoiding github etc. but I think I'll finally
have to give in and at least set up a git mirror and a discussion
place. Not sure which one is better, github, gitlab, something else?

BTW about crates.io - the python bindings in libgpiod v2 use setup.py
and I also plan to publish a python package on pypi so
decentralization is happening this time.

I also plan to split the yocto recipe into libgpiod and python3-libgpiod.

Bart

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH V7 0/8] libgpiod: Add Rust bindings
  2022-10-25  6:42         ` Viresh Kumar
@ 2022-10-29 17:46           ` Björn Roy Baron
  0 siblings, 0 replies; 50+ messages in thread
From: Björn Roy Baron @ 2022-10-29 17:46 UTC (permalink / raw)
  To: Viresh Kumar
  Cc: Miguel Ojeda, Linus Walleij, Bartosz Golaszewski,
	Vincent Guittot, linux-gpio, Kent Gibson, Alex Bennée,
	stratos-dev, Gerard Ryan, Wedson Almeida Filho, Alex Gaynor,
	Boqun Feng, Gary Guo, y86-dev

On Tuesday, October 25th, 2022 at 08:42, Viresh Kumar <viresh.kumar@linaro.org> wrote:


> On 21-10-22, 14:34, Björn Roy Baron wrote:
> 
> > > impl<'b> Event<'b> {
> > > 
> > > /// Get an event stored in the buffer.
> > > - pub(crate) fn new(buffer: &'b Buffer, index: usize) -> Result<Self> {
> > > 
> > > + pub(crate) fn new(buffer: &'b Buffer, index: usize) -> Result<Event<'b>> {
> > 
> > This looks good to me.
> 
> > > +impl<'e, 'b> Event<'e> {
> > > 
> > > + pub fn event_clone(event: &Event<'b>) -> Result<Event<'e>>
> > > 
> > > + where
> > > + 'e: 'b,
> > 
> > Using `Event<'b>` on both sides should work fine. `Event` is
> > covariant in it's lifetime parameter, so `Event<'b>` can be turned
> > into `Event<'e>` with `'e` being a shorter lifetime than `'b`. What
> > you wrote here is not incorrect, so if you prefer keeping it this
> > way that is fine with me.
> 
> 
> That doesn't let the cloned event to live past read_edge_events().
> 
> error[E0502]: cannot borrow `buffer` as mutable because it is also borrowed as immutable
> --> libgpiod/examples/gpio_events.rs:70:50
> 
> |
> 64 | let event = buffer.event(0)?;
> | --------------- immutable borrow occurs here
> ...
> 70 | let count = request.read_edge_events(&mut buffer)?;
> | ^^^^^^^^^^^ mutable borrow occurs here
> ...
> 86 | }
> | - immutable borrow might be used here, when `cloned_event` is dropped and runs the `Drop` code for type `libgpiod::request::Event`
> 
> --
> viresh

I would have expected that to work fine, but as it doesn't work keeping your code with two separate lifetimes is fine.

Cheers,
Bjorn

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH V7 3/8] libgpiod: Add rust wrapper crate
  2022-10-27 12:09         ` Bartosz Golaszewski
@ 2022-10-31 12:33           ` Kent Gibson
  2022-10-31 12:45             ` Bartosz Golaszewski
  2022-11-02  4:00             ` Viresh Kumar
  0 siblings, 2 replies; 50+ messages in thread
From: Kent Gibson @ 2022-10-31 12:33 UTC (permalink / raw)
  To: Bartosz Golaszewski
  Cc: Viresh Kumar, Linus Walleij, Vincent Guittot, linux-gpio,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

On Thu, Oct 27, 2022 at 02:09:31PM +0200, Bartosz Golaszewski wrote:
> On Fri, Oct 21, 2022 at 2:39 PM Kent Gibson <warthog618@gmail.com> wrote:
> >
> > On Fri, Oct 21, 2022 at 04:52:38PM +0530, Viresh Kumar wrote:
> > > Hi Kent,
> > >
> > > >
> > > > What if the Rust bindings version differ from the libgpiod version?
> > >
> > > Hmm, not sure which version should we return here. I think it should
> > > just be libgpiod's version. Though I am fine with whatever you and
> > > Bartosz decide.
> > >
> >
> > You need both - separately.
> >
> 
> For C++ and Python, the API version stays consistent across all three
> trees for simplicity. There are separate ABI numbers for libgpiod,
> libgpiodcxx and libgpiosim. Rust doesn't seem to have this kind of
> differentiation between ABI and API so I'd say we should have a single
> API version but see below about the idea for the contrib/ directory.
> 

My thinking was that if the Rust bindings end up being compiled into the
app then it may be difficult to identify the version it was built with,
as opposed to the version of libgpiod it is running against.
Not sure you can always determine that from the Cargo.toml or
Cargo.lock, or that those will always be available after the fact.

Not a biggy if the two always remain compatible, but if a libgpiod change
inadvertently triggers some fault in the Rust bindings then it could be a
PITA just to determine the versions involved.

> > > Will it be possible to get on with the current implementation and once
> > > this is merged you send patches on top of it which can then get
> > > reviewed properly by others too, who know Rust better than me ?
> > >
> >
> > The Rust bindings are your baby, and I don't have any plans to be
> > patching it.  And Bart is the arbiter of what gets merged, so that
> > decision is his.
> >
> 
> I was thinking about it lately and figured that - since I don't know
> Rust very well and cannot reliably maintain that part myself - how
> about we create a 'contrib' directory in the libgpiod tree for this
> kind of "third-party" bindings? The requirements for stability and
> correctness in there would be more relaxed compared to the main tree?
> 

The problem there is you thinking that you need to be able to personally
support every line of libgpiod, and that it is not a team effort.

Wrt the Rust bindings, I was assuming that either Viresh would provide
support, or as his work appears to be on behalf of Linaro that they
would have an interest in maintaining it.

Similarly, I was assuming that I would be providing support for the
proposed tool changes, should they ever get merged.  Though, based on the
little feedback to date, I'm dubious about that ever happening.
I could always split them off into a separate project, but maintaining
an autoconf based setup doesn't interest me, particularly when the Go and
Rust alternatives are one-liners that just work, so probably not.

I was thinking that some forum for libgpiod, or more broadly GPIO
user space development, would help increase community involvement,
in both directions, and that might help address the Bartosz doesn't
scale problem. But I don't have anything useful to add on that.
The only relevant experience I have is with github, and while it would
probably suffice I would explore other options first, with gitlab
being the most obvious alternative.  No help there, I know.

Sorry for not replying sooner.  I was hoping to do a respin in a more
positive light, but that has eluded me and time marches on.

Cheers,
Kent.

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH V7 3/8] libgpiod: Add rust wrapper crate
  2022-10-31 12:33           ` Kent Gibson
@ 2022-10-31 12:45             ` Bartosz Golaszewski
  2022-11-02  4:00             ` Viresh Kumar
  1 sibling, 0 replies; 50+ messages in thread
From: Bartosz Golaszewski @ 2022-10-31 12:45 UTC (permalink / raw)
  To: Kent Gibson
  Cc: Viresh Kumar, Linus Walleij, Vincent Guittot, linux-gpio,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

On Mon, Oct 31, 2022 at 1:34 PM Kent Gibson <warthog618@gmail.com> wrote:
>
> On Thu, Oct 27, 2022 at 02:09:31PM +0200, Bartosz Golaszewski wrote:
> > On Fri, Oct 21, 2022 at 2:39 PM Kent Gibson <warthog618@gmail.com> wrote:
> > >
> > > On Fri, Oct 21, 2022 at 04:52:38PM +0530, Viresh Kumar wrote:
> > > > Hi Kent,
> > > >
> > > > >
> > > > > What if the Rust bindings version differ from the libgpiod version?
> > > >
> > > > Hmm, not sure which version should we return here. I think it should
> > > > just be libgpiod's version. Though I am fine with whatever you and
> > > > Bartosz decide.
> > > >
> > >
> > > You need both - separately.
> > >
> >
> > For C++ and Python, the API version stays consistent across all three
> > trees for simplicity. There are separate ABI numbers for libgpiod,
> > libgpiodcxx and libgpiosim. Rust doesn't seem to have this kind of
> > differentiation between ABI and API so I'd say we should have a single
> > API version but see below about the idea for the contrib/ directory.
> >
>
> My thinking was that if the Rust bindings end up being compiled into the
> app then it may be difficult to identify the version it was built with,
> as opposed to the version of libgpiod it is running against.
> Not sure you can always determine that from the Cargo.toml or
> Cargo.lock, or that those will always be available after the fact.
>
> Not a biggy if the two always remain compatible, but if a libgpiod change
> inadvertently triggers some fault in the Rust bindings then it could be a
> PITA just to determine the versions involved.
>
> > > > Will it be possible to get on with the current implementation and once
> > > > this is merged you send patches on top of it which can then get
> > > > reviewed properly by others too, who know Rust better than me ?
> > > >
> > >
> > > The Rust bindings are your baby, and I don't have any plans to be
> > > patching it.  And Bart is the arbiter of what gets merged, so that
> > > decision is his.
> > >
> >
> > I was thinking about it lately and figured that - since I don't know
> > Rust very well and cannot reliably maintain that part myself - how
> > about we create a 'contrib' directory in the libgpiod tree for this
> > kind of "third-party" bindings? The requirements for stability and
> > correctness in there would be more relaxed compared to the main tree?
> >
>
> The problem there is you thinking that you need to be able to personally
> support every line of libgpiod, and that it is not a team effort.
>
> Wrt the Rust bindings, I was assuming that either Viresh would provide
> support, or as his work appears to be on behalf of Linaro that they
> would have an interest in maintaining it.
>
> Similarly, I was assuming that I would be providing support for the
> proposed tool changes, should they ever get merged.  Though, based on the
> little feedback to date, I'm dubious about that ever happening.

Hey, this is literally the next item on my TODO list. I've just merged
the v2 API into master and am getting ready to dive head-first into
your patches.

Bart

> I could always split them off into a separate project, but maintaining
> an autoconf based setup doesn't interest me, particularly when the Go and
> Rust alternatives are one-liners that just work, so probably not.
>
> I was thinking that some forum for libgpiod, or more broadly GPIO
> user space development, would help increase community involvement,
> in both directions, and that might help address the Bartosz doesn't
> scale problem. But I don't have anything useful to add on that.
> The only relevant experience I have is with github, and while it would
> probably suffice I would explore other options first, with gitlab
> being the most obvious alternative.  No help there, I know.
>
> Sorry for not replying sooner.  I was hoping to do a respin in a more
> positive light, but that has eluded me and time marches on.
>
> Cheers,
> Kent.

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH V7 3/8] libgpiod: Add rust wrapper crate
  2022-10-31 12:33           ` Kent Gibson
  2022-10-31 12:45             ` Bartosz Golaszewski
@ 2022-11-02  4:00             ` Viresh Kumar
  2022-11-02 12:47               ` Bartosz Golaszewski
  1 sibling, 1 reply; 50+ messages in thread
From: Viresh Kumar @ 2022-11-02  4:00 UTC (permalink / raw)
  To: Kent Gibson
  Cc: Bartosz Golaszewski, Linus Walleij, Vincent Guittot, linux-gpio,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

On 31-10-22, 20:33, Kent Gibson wrote:
> Wrt the Rust bindings, I was assuming that either Viresh would provide
> support, or as his work appears to be on behalf of Linaro that they
> would have an interest in maintaining it.

I will surely help in maintaining the Rust part. Not an issue.

-- 
viresh

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH V7 3/8] libgpiod: Add rust wrapper crate
  2022-11-02  4:00             ` Viresh Kumar
@ 2022-11-02 12:47               ` Bartosz Golaszewski
  2022-11-02 13:08                 ` Kent Gibson
  0 siblings, 1 reply; 50+ messages in thread
From: Bartosz Golaszewski @ 2022-11-02 12:47 UTC (permalink / raw)
  To: Viresh Kumar
  Cc: Kent Gibson, Linus Walleij, Vincent Guittot, linux-gpio,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

On Wed, Nov 2, 2022 at 5:00 AM Viresh Kumar <viresh.kumar@linaro.org> wrote:
>
> On 31-10-22, 20:33, Kent Gibson wrote:
> > Wrt the Rust bindings, I was assuming that either Viresh would provide
> > support, or as his work appears to be on behalf of Linaro that they
> > would have an interest in maintaining it.
>
> I will surely help in maintaining the Rust part. Not an issue.
>

Sounds like a plan. I'm going through Kent's gpio-tools patches and
rust bindings are next in line.

Bart

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH V7 3/8] libgpiod: Add rust wrapper crate
  2022-11-02 12:47               ` Bartosz Golaszewski
@ 2022-11-02 13:08                 ` Kent Gibson
  2022-11-02 16:34                   ` Bartosz Golaszewski
  0 siblings, 1 reply; 50+ messages in thread
From: Kent Gibson @ 2022-11-02 13:08 UTC (permalink / raw)
  To: Bartosz Golaszewski
  Cc: Viresh Kumar, Linus Walleij, Vincent Guittot, linux-gpio,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

On Wed, Nov 02, 2022 at 01:47:44PM +0100, Bartosz Golaszewski wrote:
> On Wed, Nov 2, 2022 at 5:00 AM Viresh Kumar <viresh.kumar@linaro.org> wrote:
> >
> > On 31-10-22, 20:33, Kent Gibson wrote:
> > > Wrt the Rust bindings, I was assuming that either Viresh would provide
> > > support, or as his work appears to be on behalf of Linaro that they
> > > would have an interest in maintaining it.
> >
> > I will surely help in maintaining the Rust part. Not an issue.
> >
> 
> Sounds like a plan. I'm going through Kent's gpio-tools patches and
> rust bindings are next in line.
> 

The rust bindings might make it in before they do - you may find that
they are a bit more different from the old tools than you were expecting.

For my part I'm just wrapping up a quick pass over rust v8, before
looking for where the python landed.

Cheers,
Kent.


^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH V7 3/8] libgpiod: Add rust wrapper crate
  2022-11-02 13:08                 ` Kent Gibson
@ 2022-11-02 16:34                   ` Bartosz Golaszewski
  2022-11-03  0:38                     ` Kent Gibson
  0 siblings, 1 reply; 50+ messages in thread
From: Bartosz Golaszewski @ 2022-11-02 16:34 UTC (permalink / raw)
  To: Kent Gibson
  Cc: Viresh Kumar, Linus Walleij, Vincent Guittot, linux-gpio,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

On Wed, Nov 2, 2022 at 2:08 PM Kent Gibson <warthog618@gmail.com> wrote:
>
> On Wed, Nov 02, 2022 at 01:47:44PM +0100, Bartosz Golaszewski wrote:
> > On Wed, Nov 2, 2022 at 5:00 AM Viresh Kumar <viresh.kumar@linaro.org> wrote:
> > >
> > > On 31-10-22, 20:33, Kent Gibson wrote:
> > > > Wrt the Rust bindings, I was assuming that either Viresh would provide
> > > > support, or as his work appears to be on behalf of Linaro that they
> > > > would have an interest in maintaining it.
> > >
> > > I will surely help in maintaining the Rust part. Not an issue.
> > >
> >
> > Sounds like a plan. I'm going through Kent's gpio-tools patches and
> > rust bindings are next in line.
> >
>
> The rust bindings might make it in before they do - you may find that
> they are a bit more different from the old tools than you were expecting.
>

Yeah, I can tell that. :)

I have lots of non-functional minor things to point out - mostly
coding style etc. - that I will probably just fix locally when
applying.

One thing that stands out though is the dependency on libedit - do you
think we could make the gpioset interactive mode a configurable
feature with its own configure switch that could be left out if we
don't want to depend on any external libraries?

Bart

> For my part I'm just wrapping up a quick pass over rust v8, before
> looking for where the python landed.
>
> Cheers,
> Kent.
>

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH V7 3/8] libgpiod: Add rust wrapper crate
  2022-11-02 16:34                   ` Bartosz Golaszewski
@ 2022-11-03  0:38                     ` Kent Gibson
  2022-11-03  8:35                       ` Bartosz Golaszewski
  0 siblings, 1 reply; 50+ messages in thread
From: Kent Gibson @ 2022-11-03  0:38 UTC (permalink / raw)
  To: Bartosz Golaszewski
  Cc: Viresh Kumar, Linus Walleij, Vincent Guittot, linux-gpio,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

On Wed, Nov 02, 2022 at 05:34:20PM +0100, Bartosz Golaszewski wrote:
> On Wed, Nov 2, 2022 at 2:08 PM Kent Gibson <warthog618@gmail.com> wrote:
> >
> > On Wed, Nov 02, 2022 at 01:47:44PM +0100, Bartosz Golaszewski wrote:
> > > On Wed, Nov 2, 2022 at 5:00 AM Viresh Kumar <viresh.kumar@linaro.org> wrote:
> > > >
> > > > On 31-10-22, 20:33, Kent Gibson wrote:
> > > > > Wrt the Rust bindings, I was assuming that either Viresh would provide
> > > > > support, or as his work appears to be on behalf of Linaro that they
> > > > > would have an interest in maintaining it.
> > > >
> > > > I will surely help in maintaining the Rust part. Not an issue.
> > > >
> > >
> > > Sounds like a plan. I'm going through Kent's gpio-tools patches and
> > > rust bindings are next in line.
> > >
> >
> > The rust bindings might make it in before they do - you may find that
> > they are a bit more different from the old tools than you were expecting.
> >
> 
> Yeah, I can tell that. :)
> 
> I have lots of non-functional minor things to point out - mostly
> coding style etc. - that I will probably just fix locally when
> applying.
> 

Looks like there will be a v5 for the optional interactive set, if
nothing else, so let me know and I can fix them as well.

> One thing that stands out though is the dependency on libedit - do you
> think we could make the gpioset interactive mode a configurable
> feature with its own configure switch that could be left out if we
> don't want to depend on any external libraries?

Well, libedit was your preferred choice.
But, yeah, making the interactive mode optional is a good idea.

Any preference on the name for the config flag?
--enable-gpioset-interactive ?

One other major thing I had been considering, and implemented in my Rust
version[1], is renaming the tools, since it isn't intuitively obvious,
to me anyway, what gpiodetect, gpioinfo, gpiomon, and gpiowatch do.

I went with:
 gpiodetect -> gpiochip
 gpioinfo   -> gpioline
 gpiomon    -> gpioedges
 gpiowatch  -> gpionotify

where the names try to indicate the information returned, rather than
how it is collected.
And yeah, I got stuck with gpiowatch/gpionotify as I couldn't come up
with a short meaningful name for line info changed events.
Suggestions welcome.  And lice is not an option.

gpioget and gpioset remain unchanged as they are already as clear as can
be.

Would that work for you, or would you prefer to stick with the existing
names?

Cheers,
Kent.

[1] https://crates.io/crates/gpiocdev-cli

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH V7 3/8] libgpiod: Add rust wrapper crate
  2022-11-03  0:38                     ` Kent Gibson
@ 2022-11-03  8:35                       ` Bartosz Golaszewski
  2022-11-03 12:29                         ` Kent Gibson
  0 siblings, 1 reply; 50+ messages in thread
From: Bartosz Golaszewski @ 2022-11-03  8:35 UTC (permalink / raw)
  To: Kent Gibson
  Cc: Viresh Kumar, Linus Walleij, Vincent Guittot, linux-gpio,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

On Thu, Nov 3, 2022 at 1:38 AM Kent Gibson <warthog618@gmail.com> wrote:
>
> On Wed, Nov 02, 2022 at 05:34:20PM +0100, Bartosz Golaszewski wrote:
> > On Wed, Nov 2, 2022 at 2:08 PM Kent Gibson <warthog618@gmail.com> wrote:
> > >
> > > On Wed, Nov 02, 2022 at 01:47:44PM +0100, Bartosz Golaszewski wrote:
> > > > On Wed, Nov 2, 2022 at 5:00 AM Viresh Kumar <viresh.kumar@linaro.org> wrote:
> > > > >
> > > > > On 31-10-22, 20:33, Kent Gibson wrote:
> > > > > > Wrt the Rust bindings, I was assuming that either Viresh would provide
> > > > > > support, or as his work appears to be on behalf of Linaro that they
> > > > > > would have an interest in maintaining it.
> > > > >
> > > > > I will surely help in maintaining the Rust part. Not an issue.
> > > > >
> > > >
> > > > Sounds like a plan. I'm going through Kent's gpio-tools patches and
> > > > rust bindings are next in line.
> > > >
> > >
> > > The rust bindings might make it in before they do - you may find that
> > > they are a bit more different from the old tools than you were expecting.
> > >
> >
> > Yeah, I can tell that. :)
> >
> > I have lots of non-functional minor things to point out - mostly
> > coding style etc. - that I will probably just fix locally when
> > applying.
> >
>
> Looks like there will be a v5 for the optional interactive set, if
> nothing else, so let me know and I can fix them as well.
>
> > One thing that stands out though is the dependency on libedit - do you
> > think we could make the gpioset interactive mode a configurable
> > feature with its own configure switch that could be left out if we
> > don't want to depend on any external libraries?
>
> Well, libedit was your preferred choice.

It's still is - it's not about the choice of the library but about
keeping it possible to build a set of command-line tools with no
dependencies other than libc.

> But, yeah, making the interactive mode optional is a good idea.
>
> Any preference on the name for the config flag?
> --enable-gpioset-interactive ?
>

Sounds great.

> One other major thing I had been considering, and implemented in my Rust
> version[1], is renaming the tools, since it isn't intuitively obvious,
> to me anyway, what gpiodetect, gpioinfo, gpiomon, and gpiowatch do.
>
> I went with:
>  gpiodetect -> gpiochip
>  gpioinfo   -> gpioline
>  gpiomon    -> gpioedges
>  gpiowatch  -> gpionotify
>
> where the names try to indicate the information returned, rather than
> how it is collected.
> And yeah, I got stuck with gpiowatch/gpionotify as I couldn't come up
> with a short meaningful name for line info changed events.
> Suggestions welcome.  And lice is not an option.
>
> gpioget and gpioset remain unchanged as they are already as clear as can
> be.
>
> Would that work for you, or would you prefer to stick with the existing
> names?

I don't know if it is because I'm used to the previous names but none
of the first three make much sense to me. I think it's better for the
names to indicate what the program is doing, not what it's returning.

On the other hand - gpionotify is great, go for it!

Bart

>
> Cheers,
> Kent.
>
> [1] https://crates.io/crates/gpiocdev-cli

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH V7 3/8] libgpiod: Add rust wrapper crate
  2022-11-03  8:35                       ` Bartosz Golaszewski
@ 2022-11-03 12:29                         ` Kent Gibson
  2022-11-04 15:51                           ` Bartosz Golaszewski
  0 siblings, 1 reply; 50+ messages in thread
From: Kent Gibson @ 2022-11-03 12:29 UTC (permalink / raw)
  To: Bartosz Golaszewski
  Cc: Viresh Kumar, Linus Walleij, Vincent Guittot, linux-gpio,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

On Thu, Nov 03, 2022 at 09:35:33AM +0100, Bartosz Golaszewski wrote:
> On Thu, Nov 3, 2022 at 1:38 AM Kent Gibson <warthog618@gmail.com> wrote:
> >
> > On Wed, Nov 02, 2022 at 05:34:20PM +0100, Bartosz Golaszewski wrote:
> > > On Wed, Nov 2, 2022 at 2:08 PM Kent Gibson <warthog618@gmail.com> wrote:
> > > >
> > > > On Wed, Nov 02, 2022 at 01:47:44PM +0100, Bartosz Golaszewski wrote:
> > > > > On Wed, Nov 2, 2022 at 5:00 AM Viresh Kumar <viresh.kumar@linaro.org> wrote:
> > > > > >
> > > > > > On 31-10-22, 20:33, Kent Gibson wrote:
> > > > > > > Wrt the Rust bindings, I was assuming that either Viresh would provide
> > > > > > > support, or as his work appears to be on behalf of Linaro that they
> > > > > > > would have an interest in maintaining it.
> > > > > >
> > > > > > I will surely help in maintaining the Rust part. Not an issue.
> > > > > >
> > > > >
> > > > > Sounds like a plan. I'm going through Kent's gpio-tools patches and
> > > > > rust bindings are next in line.
> > > > >
> > > >
> > > > The rust bindings might make it in before they do - you may find that
> > > > they are a bit more different from the old tools than you were expecting.
> > > >
> > >
> > > Yeah, I can tell that. :)
> > >
> > > I have lots of non-functional minor things to point out - mostly
> > > coding style etc. - that I will probably just fix locally when
> > > applying.
> > >
> >
> > Looks like there will be a v5 for the optional interactive set, if
> > nothing else, so let me know and I can fix them as well.
> >
> > > One thing that stands out though is the dependency on libedit - do you
> > > think we could make the gpioset interactive mode a configurable
> > > feature with its own configure switch that could be left out if we
> > > don't want to depend on any external libraries?
> >
> > Well, libedit was your preferred choice.
> 
> It's still is - it's not about the choice of the library but about
> keeping it possible to build a set of command-line tools with no
> dependencies other than libc.
> 
> > But, yeah, making the interactive mode optional is a good idea.
> >
> > Any preference on the name for the config flag?
> > --enable-gpioset-interactive ?
> >
> 
> Sounds great.
> 
> > One other major thing I had been considering, and implemented in my Rust
> > version[1], is renaming the tools, since it isn't intuitively obvious,
> > to me anyway, what gpiodetect, gpioinfo, gpiomon, and gpiowatch do.
> >
> > I went with:
> >  gpiodetect -> gpiochip
> >  gpioinfo   -> gpioline
> >  gpiomon    -> gpioedges
> >  gpiowatch  -> gpionotify
> >
> > where the names try to indicate the information returned, rather than
> > how it is collected.
> > And yeah, I got stuck with gpiowatch/gpionotify as I couldn't come up
> > with a short meaningful name for line info changed events.
> > Suggestions welcome.  And lice is not an option.
> >
> > gpioget and gpioset remain unchanged as they are already as clear as can
> > be.
> >
> > Would that work for you, or would you prefer to stick with the existing
> > names?
> 
> I don't know if it is because I'm used to the previous names but none
> of the first three make much sense to me. I think it's better for the
> names to indicate what the program is doing, not what it's returning.
> 
> On the other hand - gpionotify is great, go for it!
> 

Ok, now I'm confused - only rename gpiowatch?

Anyway, we probably should pick this conversation up as part of the
tools review, not the Rust bindings, so let me know there.

Cheers,
Kent.

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH V7 3/8] libgpiod: Add rust wrapper crate
  2022-11-03 12:29                         ` Kent Gibson
@ 2022-11-04 15:51                           ` Bartosz Golaszewski
  0 siblings, 0 replies; 50+ messages in thread
From: Bartosz Golaszewski @ 2022-11-04 15:51 UTC (permalink / raw)
  To: Kent Gibson
  Cc: Viresh Kumar, Linus Walleij, Vincent Guittot, linux-gpio,
	Miguel Ojeda, Wedson Almeida Filho, Alex Bennée,
	stratos-dev, Gerard Ryan

On Thu, Nov 3, 2022 at 1:29 PM Kent Gibson <warthog618@gmail.com> wrote:
>
> On Thu, Nov 03, 2022 at 09:35:33AM +0100, Bartosz Golaszewski wrote:
> > On Thu, Nov 3, 2022 at 1:38 AM Kent Gibson <warthog618@gmail.com> wrote:
> > >
> > > On Wed, Nov 02, 2022 at 05:34:20PM +0100, Bartosz Golaszewski wrote:
> > > > On Wed, Nov 2, 2022 at 2:08 PM Kent Gibson <warthog618@gmail.com> wrote:
> > > > >
> > > > > On Wed, Nov 02, 2022 at 01:47:44PM +0100, Bartosz Golaszewski wrote:
> > > > > > On Wed, Nov 2, 2022 at 5:00 AM Viresh Kumar <viresh.kumar@linaro.org> wrote:
> > > > > > >
> > > > > > > On 31-10-22, 20:33, Kent Gibson wrote:
> > > > > > > > Wrt the Rust bindings, I was assuming that either Viresh would provide
> > > > > > > > support, or as his work appears to be on behalf of Linaro that they
> > > > > > > > would have an interest in maintaining it.
> > > > > > >
> > > > > > > I will surely help in maintaining the Rust part. Not an issue.
> > > > > > >
> > > > > >
> > > > > > Sounds like a plan. I'm going through Kent's gpio-tools patches and
> > > > > > rust bindings are next in line.
> > > > > >
> > > > >
> > > > > The rust bindings might make it in before they do - you may find that
> > > > > they are a bit more different from the old tools than you were expecting.
> > > > >
> > > >
> > > > Yeah, I can tell that. :)
> > > >
> > > > I have lots of non-functional minor things to point out - mostly
> > > > coding style etc. - that I will probably just fix locally when
> > > > applying.
> > > >
> > >
> > > Looks like there will be a v5 for the optional interactive set, if
> > > nothing else, so let me know and I can fix them as well.
> > >
> > > > One thing that stands out though is the dependency on libedit - do you
> > > > think we could make the gpioset interactive mode a configurable
> > > > feature with its own configure switch that could be left out if we
> > > > don't want to depend on any external libraries?
> > >
> > > Well, libedit was your preferred choice.
> >
> > It's still is - it's not about the choice of the library but about
> > keeping it possible to build a set of command-line tools with no
> > dependencies other than libc.
> >
> > > But, yeah, making the interactive mode optional is a good idea.
> > >
> > > Any preference on the name for the config flag?
> > > --enable-gpioset-interactive ?
> > >
> >
> > Sounds great.
> >
> > > One other major thing I had been considering, and implemented in my Rust
> > > version[1], is renaming the tools, since it isn't intuitively obvious,
> > > to me anyway, what gpiodetect, gpioinfo, gpiomon, and gpiowatch do.
> > >
> > > I went with:
> > >  gpiodetect -> gpiochip
> > >  gpioinfo   -> gpioline
> > >  gpiomon    -> gpioedges
> > >  gpiowatch  -> gpionotify
> > >
> > > where the names try to indicate the information returned, rather than
> > > how it is collected.
> > > And yeah, I got stuck with gpiowatch/gpionotify as I couldn't come up
> > > with a short meaningful name for line info changed events.
> > > Suggestions welcome.  And lice is not an option.
> > >
> > > gpioget and gpioset remain unchanged as they are already as clear as can
> > > be.
> > >
> > > Would that work for you, or would you prefer to stick with the existing
> > > names?
> >
> > I don't know if it is because I'm used to the previous names but none
> > of the first three make much sense to me. I think it's better for the
> > names to indicate what the program is doing, not what it's returning.
> >
> > On the other hand - gpionotify is great, go for it!
> >
>
> Ok, now I'm confused - only rename gpiowatch?

Yes, gpiowatch -> gpionotify.

>
> Anyway, we probably should pick this conversation up as part of the
> tools review, not the Rust bindings, so let me know there.
>

Makes sense. I'm off most of this week, but I will get there, I promise. :)

Bartosz

^ permalink raw reply	[flat|nested] 50+ messages in thread

end of thread, other threads:[~2022-11-04 15:51 UTC | newest]

Thread overview: 50+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-10-14 10:47 [PATCH V7 0/8] libgpiod: Add Rust bindings Viresh Kumar
2022-10-14 10:47 ` [PATCH V7 1/8] libgpiod: Add libgpiod-sys rust crate Viresh Kumar
2022-10-17 12:59   ` Kent Gibson
2022-10-18 11:22     ` Viresh Kumar
2022-10-18 11:42       ` Miguel Ojeda
2022-10-18 11:49       ` Kent Gibson
2022-10-19  6:46         ` Viresh Kumar
2022-10-19  7:21           ` Kent Gibson
2022-10-19 11:22             ` Viresh Kumar
2022-10-19 12:01               ` Kent Gibson
2022-10-20  5:34                 ` Viresh Kumar
2022-10-27 12:18                 ` Bartosz Golaszewski
2022-10-14 10:47 ` [PATCH V7 2/8] libgpiod-sys: Add pre generated rust bindings Viresh Kumar
2022-10-14 10:47 ` [PATCH V7 3/8] libgpiod: Add rust wrapper crate Viresh Kumar
2022-10-17 12:59   ` Kent Gibson
2022-10-21 11:22     ` Viresh Kumar
2022-10-21 12:39       ` Kent Gibson
2022-10-27 12:09         ` Bartosz Golaszewski
2022-10-31 12:33           ` Kent Gibson
2022-10-31 12:45             ` Bartosz Golaszewski
2022-11-02  4:00             ` Viresh Kumar
2022-11-02 12:47               ` Bartosz Golaszewski
2022-11-02 13:08                 ` Kent Gibson
2022-11-02 16:34                   ` Bartosz Golaszewski
2022-11-03  0:38                     ` Kent Gibson
2022-11-03  8:35                       ` Bartosz Golaszewski
2022-11-03 12:29                         ` Kent Gibson
2022-11-04 15:51                           ` Bartosz Golaszewski
2022-10-18  3:27   ` Kent Gibson
2022-10-21 11:25     ` Viresh Kumar
2022-10-14 10:47 ` [PATCH V7 4/8] libgpiod: Add rust examples Viresh Kumar
2022-10-17 13:00   ` Kent Gibson
2022-10-14 10:47 ` [PATCH V7 5/8] libgpiod: Add gpiosim rust crate Viresh Kumar
2022-10-17 13:00   ` Kent Gibson
2022-10-21  9:56     ` Viresh Kumar
2022-10-14 10:47 ` [PATCH V7 6/8] gpiosim: Add pre generated rust bindings Viresh Kumar
2022-10-14 10:47 ` [PATCH V7 7/8] libgpiod: Add rust tests Viresh Kumar
2022-10-17 13:00   ` Kent Gibson
2022-10-17 14:30     ` Kent Gibson
2022-10-20 10:37     ` Viresh Kumar
2022-10-20 11:02       ` Kent Gibson
2022-10-20 12:40         ` Bartosz Golaszewski
2022-10-20 15:16           ` Miguel Ojeda
2022-10-14 10:47 ` [PATCH V7 8/8] libgpiod: Integrate building of rust bindings with make Viresh Kumar
2022-10-14 17:03 ` [PATCH V7 0/8] libgpiod: Add Rust bindings Miguel Ojeda
2022-10-20 13:29   ` Björn Roy Baron
2022-10-21  9:39     ` Viresh Kumar
2022-10-21 14:34       ` Björn Roy Baron
2022-10-25  6:42         ` Viresh Kumar
2022-10-29 17:46           ` Björn Roy Baron

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.