From patchwork Tue Aug 2 12:18:05 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 595003 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9D8C5C19F2C for ; Tue, 2 Aug 2022 12:18:27 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236872AbiHBMS0 (ORCPT ); Tue, 2 Aug 2022 08:18:26 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38738 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234024AbiHBMS0 (ORCPT ); Tue, 2 Aug 2022 08:18:26 -0400 Received: from mail-pg1-x535.google.com (mail-pg1-x535.google.com [IPv6:2607:f8b0:4864:20::535]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E91594F18E for ; Tue, 2 Aug 2022 05:18:21 -0700 (PDT) Received: by mail-pg1-x535.google.com with SMTP id h132so12187539pgc.10 for ; Tue, 02 Aug 2022 05:18:21 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=TwhuAp3W7cu4b7rXJywkLPtcj3qj9jo5db4QI6rcIPg=; b=Vi+G8Z1LulR85fUuoO9Tov7kUe2wT+RhlbBhFf4JvlbDDzNyKfKZDIIHwwMrVRxFpN r18UDfBnB6quabp7Jykcd92Mm7jcKl49ZZjLm+p32cXDsW1iboDNDo93zWyAD8U9mNpY +d0jtmE9dGcYcc8WKhejQBRt8Xri3AQa9w8DYvSm4DT5ocN+Z6qe+31aMyQE3fbnvUVy KzqB4mqlXEhfQXXORvue4HuAG5qx2neckHzYLs/G3LwlEmZnTiakb9Y3wud8XRmu+QlP sqW/GdEunfwCBorWPetDrs1PfIa9rayHEnUoiJSZU41fk6TNHWXp8qN2LY98ZenrinYb +EMw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=TwhuAp3W7cu4b7rXJywkLPtcj3qj9jo5db4QI6rcIPg=; b=470SACoBzlO4rm3rqMPS2i6cKTou/76NctXKH0bqA93VkVKHqHgXIFmKasLbPwP0QG Ru1wDmx4tpiaHicjBqTbg/dAoVJyEhyx9WGmEE8L0kgBor2vrHx6RwL36u+KdUIK9bMM +0dUqqczCYsw2kxMRjWweDDfqvPMHnRrdDdbyioI5LJS1ojlJpzGqs0PA8AURa+ltdU3 O+o8IDvAP3p7YNw1Z5fctAN+msFP8b7Cd+RIg55KoGhRIURQiT86vTk6Gp6kNEdQQIKY 8wVG7WazlvFO2eh/44x1em1sEPnw2QtvH6BsRvmTMtsZgN86WBwKDsgT+G03EBsFd1f4 Rh0w== X-Gm-Message-State: ACgBeo2SNGIcUjBHjl75OfaEK1qQI67Yva1RJCX/LKT8CFjiyqM+CtAd 2Bpsa9JSz9SdDR3m57HSzZzzkg== X-Google-Smtp-Source: AA6agR7CL4fHgojVnwug471JQITk02JMGCviWCRFVHdRbWFs3JHmeQ6LSV5v0ysoNd6iSyDHrraE4A== X-Received: by 2002:a05:6a00:2288:b0:52d:dbdf:318e with SMTP id f8-20020a056a00228800b0052ddbdf318emr2853737pfe.62.1659442701354; Tue, 02 Aug 2022 05:18:21 -0700 (PDT) Received: from localhost ([122.171.18.80]) by smtp.gmail.com with ESMTPSA id h6-20020a17090a130600b001f4d711e165sm7084667pja.11.2022.08.02.05.18.20 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 02 Aug 2022 05:18:21 -0700 (PDT) From: Viresh Kumar To: Linus Walleij , Bartosz Golaszewski , Kent Gibson Cc: Viresh Kumar , Vincent Guittot , linux-gpio@vger.kernel.org, Miguel Ojeda , Wedson Almeida Filho , =?utf-8?q?Alex_Benn=C3=A9e?= , stratos-dev@op-lists.linaro.org, Gerard Ryan Subject: [PATCH V5 1/8] libgpiod: Add libgpiod-sys rust crate Date: Tue, 2 Aug 2022 17:48:05 +0530 Message-Id: <5319a3efc633e15993ba339972d139d6ec376cc6.1659442066.git.viresh.kumar@linaro.org> X-Mailer: git-send-email 2.31.1.272.g89b43f80a514 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org This adds libgpiod-sys rust crate, which provides FFI (foreign function interface) bindings for libgpiod APIs. Signed-off-by: Viresh Kumar --- .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 58e1c5fc7e00..9541482d5efb 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,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::*; From patchwork Tue Aug 2 12:18:06 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 595002 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 764FAC3F6B0 for ; Tue, 2 Aug 2022 12:18:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236899AbiHBMSf (ORCPT ); Tue, 2 Aug 2022 08:18:35 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38872 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236916AbiHBMSc (ORCPT ); Tue, 2 Aug 2022 08:18:32 -0400 Received: from mail-pj1-x1029.google.com (mail-pj1-x1029.google.com [IPv6:2607:f8b0:4864:20::1029]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 84F3C4F66B for ; Tue, 2 Aug 2022 05:18:26 -0700 (PDT) Received: by mail-pj1-x1029.google.com with SMTP id h21-20020a17090aa89500b001f31a61b91dso15439047pjq.4 for ; Tue, 02 Aug 2022 05:18:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=u2MXL6fTKl6lsWvww+yDPi7Wfn6B2NptXQt633za4zA=; b=uHPIjFLLfnj9gsPBep6Wxw1o8zuohBhv1Kh2VYWpQJ+GEufcI8Z+vT7OdDG7Q4/du9 IpZ3UK3JDwI9L59hnhcnX05PZsI/xcBIXGyxar0CDvHDmqJ7s69tk7raT0/fhKnQCRGE 08BSNltmppYeFh/nlUtHElB3K7G2fZyzWwlGcwdwJgmNbJ4xIbozWrTpzjdpQ90kWsQ/ w3b5otrW2ZaP+qKct7vjAcKTccsTWiExqNeRyoBUJm75AzVrYxobb40O34LtEj/uOhDr HWKw/XDnBbmKdPwuOb7QfiDswo/QCVpjRvOdu/NcQtYRQ3F8f2Xi3FH0fxpmn9ayRbId /Vag== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=u2MXL6fTKl6lsWvww+yDPi7Wfn6B2NptXQt633za4zA=; b=3tXTRHvKtfGTvf68cCceSwc56Y7EF6iZWU10/YZgAh55NqcJUebV49VRPAt3gDh11C 2HncEjHAW4qPoGqeYXn1Ff1Hs5rBfsOaJd3mLZln/HVEIIU++iAwPLKT6REdT2kyUF0Z gSUnKjygIlUaq588U49u1AqHmqP1hVP/kOzzVH0ASoPQ+u0WsoxK67ZmjBa/8xJWwL46 bB9Ldq3fnSxyNSoHkI2mUcM1OJWZugJ6Lrc1xkL4jpvcS47RGSnWggE+ziPGZ2jFgbq9 LXVXjsZi056plf1+eTxYjaQB63tu2fLhAYsHyjSyNewfNES6Wm2XsHbwkZzhnDT3OW19 ew+g== X-Gm-Message-State: ACgBeo0VPcZy7jYjOd5aj5flvWI4tGvbQf5pkZhnsXHCkRPuj3vAgo4P vFdmlVRqO7GgVO0Y76sEZXbxTA== X-Google-Smtp-Source: AA6agR6yOGu0xyCEvZFP/xmnynNmLbpTmsVX88bCPBdrpzMsIXbYO1xW8jLrmE+OrjsyGyWeQTCypw== X-Received: by 2002:a17:90b:1e4d:b0:1f0:462b:b573 with SMTP id pi13-20020a17090b1e4d00b001f0462bb573mr24688658pjb.164.1659442704596; Tue, 02 Aug 2022 05:18:24 -0700 (PDT) Received: from localhost ([122.171.18.80]) by smtp.gmail.com with ESMTPSA id l13-20020a170903244d00b0016ed8af2ec0sm6508874pls.29.2022.08.02.05.18.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 02 Aug 2022 05:18:24 -0700 (PDT) From: Viresh Kumar To: Linus Walleij , Bartosz Golaszewski , Kent Gibson Cc: Viresh Kumar , Vincent Guittot , linux-gpio@vger.kernel.org, Miguel Ojeda , Wedson Almeida Filho , =?utf-8?q?Alex_Benn=C3=A9e?= , stratos-dev@op-lists.linaro.org, Gerard Ryan Subject: [PATCH V5 2/8] libgpiod-sys: Add pre generated rust bindings Date: Tue, 2 Aug 2022 17:48:06 +0530 Message-Id: X-Mailer: git-send-email 2.31.1.272.g89b43f80a514 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org This adds a copy of pre generated bindings and adds the suggested way of updating those in README. Signed-off-by: Viresh Kumar --- bindings/rust/libgpiod-sys/README.md | 11 + bindings/rust/libgpiod-sys/src/bindings.rs | 1535 ++++++++++++++++++++ 2 files changed, 1546 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 + +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..243f57712018 --- /dev/null +++ b/bindings/rust/libgpiod-sys/src/bindings.rs @@ -0,0 +1,1535 @@ +/* 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::(), + 32usize, + concat!("Size of: ", stringify!(max_align_t)) + ); + assert_eq!( + ::std::mem::align_of::(), + 16usize, + concat!("Alignment of ", stringify!(max_align_t)) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).__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::())).__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 = "

The API is logically split into several parts such as: GPIO chip & line"] +#[doc = " operators, GPIO events handling etc."] +#[doc = ""] +#[doc = "

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 = "

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_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."] + #[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."] + #[doc = " @note Line configuration overrides for lines that are not requested are"] + #[doc = "\t silently ignored."] + 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; +#[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 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 Set the default line direction."] + #[doc = " @param config Line config object."] + #[doc = " @param direction New direction."] + pub fn gpiod_line_config_set_direction_default( + config: *mut gpiod_line_config, + direction: ::std::os::raw::c_int, + ); +} +extern "C" { + #[doc = " @brief Set the direction override for a line."] + #[doc = " @param config Line config object."] + #[doc = " @param direction New direction."] + #[doc = " @param offset The offset of the line for which to set the override."] + pub fn gpiod_line_config_set_direction_override( + config: *mut gpiod_line_config, + direction: ::std::os::raw::c_int, + offset: ::std::os::raw::c_uint, + ); +} +extern "C" { + #[doc = " @brief Clear the direction override for a line."] + #[doc = " @param config Line config object."] + #[doc = " @param offset The offset of the line for which to clear the override."] + #[doc = " @note Does nothing if no override is set for the line."] + pub fn gpiod_line_config_clear_direction_override( + config: *mut gpiod_line_config, + offset: ::std::os::raw::c_uint, + ); +} +extern "C" { + #[doc = " @brief Check if the direction is overridden for a line."] + #[doc = " @param config Line config object."] + #[doc = " @param offset The offset of the line to check for the override."] + #[doc = " @return True if direction is overridden on the line, false otherwise."] + pub fn gpiod_line_config_direction_is_overridden( + config: *mut gpiod_line_config, + offset: ::std::os::raw::c_uint, + ) -> bool; +} +extern "C" { + #[doc = " @brief Get the default direction setting."] + #[doc = " @param config Line config object."] + #[doc = " @return Direction setting used for any non-overridden line."] + pub fn gpiod_line_config_get_direction_default( + config: *mut gpiod_line_config, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Get the direction setting for a line."] + #[doc = " @param config Line config object."] + #[doc = " @param offset The offset of the line for which to read the direction."] + #[doc = " @return Direction setting for the line if the config object were used"] + #[doc = "\t in a request."] + pub fn gpiod_line_config_get_direction_offset( + config: *mut gpiod_line_config, + offset: ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Set the default edge event detection."] + #[doc = " @param config Line config object."] + #[doc = " @param edge Type of edge events to detect."] + pub fn gpiod_line_config_set_edge_detection_default( + config: *mut gpiod_line_config, + edge: ::std::os::raw::c_int, + ); +} +extern "C" { + #[doc = " @brief Set the edge detection override for a line."] + #[doc = " @param config Line config object."] + #[doc = " @param edge Type of edge events to detect."] + #[doc = " @param offset The offset of the line for which to set the override."] + pub fn gpiod_line_config_set_edge_detection_override( + config: *mut gpiod_line_config, + edge: ::std::os::raw::c_int, + offset: ::std::os::raw::c_uint, + ); +} +extern "C" { + #[doc = " @brief Clear the edge detection override for a line."] + #[doc = " @param config Line config object."] + #[doc = " @param offset The offset of the line for which to clear the override."] + #[doc = " @note Does nothing if no override is set for the line."] + pub fn gpiod_line_config_clear_edge_detection_override( + config: *mut gpiod_line_config, + offset: ::std::os::raw::c_uint, + ); +} +extern "C" { + #[doc = " @brief Check if the edge detection setting is overridden for a line."] + #[doc = " @param config Line config object."] + #[doc = " @param offset The offset of the line to check for the override."] + #[doc = " @return True if edge detection is overridden for the line, false otherwise."] + pub fn gpiod_line_config_edge_detection_is_overridden( + config: *mut gpiod_line_config, + offset: ::std::os::raw::c_uint, + ) -> bool; +} +extern "C" { + #[doc = " @brief Get the default edge detection setting."] + #[doc = " @param config Line config object."] + #[doc = " @return Edge detection setting used for any non-overridden line."] + pub fn gpiod_line_config_get_edge_detection_default( + config: *mut gpiod_line_config, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Get the edge event detection setting for a line."] + #[doc = " @param config Line config object."] + #[doc = " @param offset The offset of the line for which to read the edge event detection"] + #[doc = "\t\t setting."] + #[doc = " @return Edge event detection setting for the line if the config object"] + #[doc = "\t were used in a request."] + pub fn gpiod_line_config_get_edge_detection_offset( + config: *mut gpiod_line_config, + offset: ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Set the default bias setting."] + #[doc = " @param config Line config object."] + #[doc = " @param bias New bias."] + pub fn gpiod_line_config_set_bias_default( + config: *mut gpiod_line_config, + bias: ::std::os::raw::c_int, + ); +} +extern "C" { + #[doc = " @brief Set the bias override for a line."] + #[doc = " @param config Line config object."] + #[doc = " @param bias New bias setting."] + #[doc = " @param offset The offset of the line for which to set the override."] + pub fn gpiod_line_config_set_bias_override( + config: *mut gpiod_line_config, + bias: ::std::os::raw::c_int, + offset: ::std::os::raw::c_uint, + ); +} +extern "C" { + #[doc = " @brief Clear the bias override for a line."] + #[doc = " @param config Line config object."] + #[doc = " @param offset The offset of the line for which to clear the override."] + #[doc = " @note Does nothing if no override is set for the line."] + pub fn gpiod_line_config_clear_bias_override( + config: *mut gpiod_line_config, + offset: ::std::os::raw::c_uint, + ); +} +extern "C" { + #[doc = " @brief Check if the bias setting is overridden for a line."] + #[doc = " @param config Line config object."] + #[doc = " @param offset The offset of the line to check for the override."] + #[doc = " @return True if bias is overridden for the line, false otherwise."] + pub fn gpiod_line_config_bias_is_overridden( + config: *mut gpiod_line_config, + offset: ::std::os::raw::c_uint, + ) -> bool; +} +extern "C" { + #[doc = " @brief Get the default bias setting."] + #[doc = " @param config Line config object."] + #[doc = " @return Bias setting used for any non-overridden line."] + pub fn gpiod_line_config_get_bias_default( + config: *mut gpiod_line_config, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Get the bias setting for a line."] + #[doc = " @param config Line config object."] + #[doc = " @param offset The offset of the line for which to read the bias setting."] + #[doc = " @return Bias setting used for the line if the config object were used"] + #[doc = "\t in a request."] + pub fn gpiod_line_config_get_bias_offset( + config: *mut gpiod_line_config, + offset: ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Set the default drive setting."] + #[doc = " @param config Line config object."] + #[doc = " @param drive New drive."] + pub fn gpiod_line_config_set_drive_default( + config: *mut gpiod_line_config, + drive: ::std::os::raw::c_int, + ); +} +extern "C" { + #[doc = " @brief Set the drive override for a line."] + #[doc = " @param config Line config object."] + #[doc = " @param drive New drive setting."] + #[doc = " @param offset The offset of the line for which to set the override."] + pub fn gpiod_line_config_set_drive_override( + config: *mut gpiod_line_config, + drive: ::std::os::raw::c_int, + offset: ::std::os::raw::c_uint, + ); +} +extern "C" { + #[doc = " @brief Clear the drive override for a line."] + #[doc = " @param config Line config object."] + #[doc = " @param offset The offset of the line for which to clear the override."] + #[doc = " @note Does nothing if no override is set for the line."] + pub fn gpiod_line_config_clear_drive_override( + config: *mut gpiod_line_config, + offset: ::std::os::raw::c_uint, + ); +} +extern "C" { + #[doc = " @brief Check if the drive setting is overridden for a line."] + #[doc = " @param config Line config object."] + #[doc = " @param offset The offset of the line to check for the override."] + #[doc = " @return True if drive is overridden for the line, false otherwise."] + pub fn gpiod_line_config_drive_is_overridden( + config: *mut gpiod_line_config, + offset: ::std::os::raw::c_uint, + ) -> bool; +} +extern "C" { + #[doc = " @brief Get the default drive setting."] + #[doc = " @param config Line config object."] + #[doc = " @return Drive setting for any non-overridden line."] + pub fn gpiod_line_config_get_drive_default( + config: *mut gpiod_line_config, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Get the drive setting for a line."] + #[doc = " @param config Line config object."] + #[doc = " @param offset The offset of the line for which to read the drive setting."] + #[doc = " @return Drive setting for the line if the config object were used in a"] + #[doc = "\t request."] + pub fn gpiod_line_config_get_drive_offset( + config: *mut gpiod_line_config, + offset: ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Set the default active-low setting."] + #[doc = " @param config Line config object."] + #[doc = " @param active_low New active-low setting."] + pub fn gpiod_line_config_set_active_low_default( + config: *mut gpiod_line_config, + active_low: bool, + ); +} +extern "C" { + #[doc = " @brief Override the active-low setting for a line."] + #[doc = " @param config Line config object."] + #[doc = " @param active_low New active-low setting."] + #[doc = " @param offset The offset of the line for which to set the override."] + pub fn gpiod_line_config_set_active_low_override( + config: *mut gpiod_line_config, + active_low: bool, + offset: ::std::os::raw::c_uint, + ); +} +extern "C" { + #[doc = " @brief Clear the active-low override for a line."] + #[doc = " @param config Line config object."] + #[doc = " @param offset The offset of the line for which to clear the override."] + #[doc = " @note Does nothing if no override is set for the line."] + pub fn gpiod_line_config_clear_active_low_override( + config: *mut gpiod_line_config, + offset: ::std::os::raw::c_uint, + ); +} +extern "C" { + #[doc = " @brief Check if the active-low setting is overridden for a line."] + #[doc = " @param config Line config object."] + #[doc = " @param offset The offset of the line to check for the override."] + #[doc = " @return True if active-low is overridden for the line, false otherwise."] + pub fn gpiod_line_config_active_low_is_overridden( + config: *mut gpiod_line_config, + offset: ::std::os::raw::c_uint, + ) -> bool; +} +extern "C" { + #[doc = " @brief Check if active-low is the default setting."] + #[doc = " @param config Line config object."] + #[doc = " @return Active-low setting for any non-overridden line."] + pub fn gpiod_line_config_get_active_low_default(config: *mut gpiod_line_config) -> bool; +} +extern "C" { + #[doc = " @brief Check if a line is configured as active-low."] + #[doc = " @param config Line config object."] + #[doc = " @param offset The offset of the line for which to read the active-low setting."] + #[doc = " @return Active-low setting for the line if the config object were used in"] + #[doc = "\t a request."] + pub fn gpiod_line_config_get_active_low_offset( + config: *mut gpiod_line_config, + offset: ::std::os::raw::c_uint, + ) -> bool; +} +extern "C" { + #[doc = " @brief Set the default debounce period."] + #[doc = " @param config Line config object."] + #[doc = " @param period New debounce period in microseconds. Disables debouncing if 0."] + #[doc = " @note Debouncing is only useful on input lines with edge detection."] + #[doc = "\t Its purpose is to filter spurious events due to noise during the"] + #[doc = "\t edge transition. It has no effect on normal get or set operations."] + pub fn gpiod_line_config_set_debounce_period_us_default( + config: *mut gpiod_line_config, + period: ::std::os::raw::c_ulong, + ); +} +extern "C" { + #[doc = " @brief Override the debounce period setting for a line."] + #[doc = " @param config Line config object."] + #[doc = " @param period New debounce period in microseconds."] + #[doc = " @param offset The offset of the line for which to set the override."] + pub fn gpiod_line_config_set_debounce_period_us_override( + config: *mut gpiod_line_config, + period: ::std::os::raw::c_ulong, + offset: ::std::os::raw::c_uint, + ); +} +extern "C" { + #[doc = " @brief Clear the debounce period override for a line."] + #[doc = " @param config Line config object."] + #[doc = " @param offset The offset of the line for which to clear the override."] + #[doc = " @note Does nothing if no override is set for the line."] + pub fn gpiod_line_config_clear_debounce_period_us_override( + config: *mut gpiod_line_config, + offset: ::std::os::raw::c_uint, + ); +} +extern "C" { + #[doc = " @brief Check if the debounce period setting is overridden for a line."] + #[doc = " @param config Line config object."] + #[doc = " @param offset The offset of the line to check for the override."] + #[doc = " @return True if debounce period is overridden for the line, false"] + #[doc = "\t otherwise."] + pub fn gpiod_line_config_debounce_period_us_is_overridden( + config: *mut gpiod_line_config, + offset: ::std::os::raw::c_uint, + ) -> bool; +} +extern "C" { + #[doc = " @brief Get the default debounce period."] + #[doc = " @param config Line config object."] + #[doc = " @return Debounce period for any non-overridden line."] + #[doc = "\t Measured in microseconds."] + #[doc = "\t 0 if debouncing is disabled."] + pub fn gpiod_line_config_get_debounce_period_us_default( + config: *mut gpiod_line_config, + ) -> ::std::os::raw::c_ulong; +} +extern "C" { + #[doc = " @brief Get the debounce period for a line."] + #[doc = " @param config Line config object."] + #[doc = " @param offset The offset of the line for which to read the debounce period."] + #[doc = " @return Debounce period for the line if the config object were used in a"] + #[doc = "\t request."] + #[doc = "\t Measured in microseconds."] + #[doc = "\t 0 if debouncing is disabled."] + pub fn gpiod_line_config_get_debounce_period_us_offset( + config: *mut gpiod_line_config, + offset: ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_ulong; +} +extern "C" { + #[doc = " @brief Set the default event timestamp clock."] + #[doc = " @param config Line config object."] + #[doc = " @param clock New clock to use."] + pub fn gpiod_line_config_set_event_clock_default( + config: *mut gpiod_line_config, + clock: ::std::os::raw::c_int, + ); +} +extern "C" { + #[doc = " @brief Override the event clock setting for a line."] + #[doc = " @param config Line config object."] + #[doc = " @param clock New event clock to use."] + #[doc = " @param offset The offset of the line for which to set the override."] + pub fn gpiod_line_config_set_event_clock_override( + config: *mut gpiod_line_config, + clock: ::std::os::raw::c_int, + offset: ::std::os::raw::c_uint, + ); +} +extern "C" { + #[doc = " @brief Clear the event clock override for a line."] + #[doc = " @param config Line config object."] + #[doc = " @param offset The offset of the line for which to clear the override."] + #[doc = " @note Does nothing if no override is set for the line."] + pub fn gpiod_line_config_clear_event_clock_override( + config: *mut gpiod_line_config, + offset: ::std::os::raw::c_uint, + ); +} +extern "C" { + #[doc = " @brief Check if the event clock setting is overridden for a line."] + #[doc = " @param config Line config object."] + #[doc = " @param offset The offset of the line to check for the override."] + #[doc = " @return True if event clock period is overridden for the line, false"] + #[doc = "\t otherwise."] + pub fn gpiod_line_config_event_clock_is_overridden( + config: *mut gpiod_line_config, + offset: ::std::os::raw::c_uint, + ) -> bool; +} +extern "C" { + #[doc = " @brief Get the default event clock setting."] + #[doc = " @param config Line config object."] + #[doc = " @return Event clock setting for any non-overridden line."] + pub fn gpiod_line_config_get_event_clock_default( + config: *mut gpiod_line_config, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Get the event clock setting for a line."] + #[doc = " @param config Line config object."] + #[doc = " @param offset The offset of the line for which to read the event clock setting."] + #[doc = " @return Event clock setting for the line if the config object were used in a"] + #[doc = "\t request."] + pub fn gpiod_line_config_get_event_clock_offset( + config: *mut gpiod_line_config, + offset: ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Set the default output value."] + #[doc = " @param config Line config object."] + #[doc = " @param value New value."] + #[doc = ""] + #[doc = " The default output value applies to all non-overridden output lines."] + #[doc = " It does not apply to input lines or overridden lines."] + pub fn gpiod_line_config_set_output_value_default( + config: *mut gpiod_line_config, + value: ::std::os::raw::c_int, + ); +} +extern "C" { + #[doc = " @brief Override the output value for a line."] + #[doc = " @param config Line config object."] + #[doc = " @param offset The offset of the line for which to override the output value."] + #[doc = " @param value Output value to set."] + pub fn gpiod_line_config_set_output_value_override( + config: *mut gpiod_line_config, + value: ::std::os::raw::c_int, + offset: ::std::os::raw::c_uint, + ); +} +extern "C" { + #[doc = " @brief Override the output values for multiple lines."] + #[doc = " @param config Line config object."] + #[doc = " @param num_values Number of lines for which to override values."] + #[doc = " @param offsets Array of offsets identifying the lines for which to override"] + #[doc = "\t\t values, containing \\p num_values entries."] + #[doc = " @param values Array of output values corresponding to the lines identified in"] + #[doc = "\t\t \\p offsets, also containing \\p num_values entries."] + pub fn gpiod_line_config_set_output_values( + config: *mut gpiod_line_config, + num_values: size_t, + offsets: *const ::std::os::raw::c_uint, + values: *const ::std::os::raw::c_int, + ); +} +extern "C" { + #[doc = " @brief Clear the output value override for a line."] + #[doc = " @param config Line config object."] + #[doc = " @param offset The offset of the line for which to clear the override."] + #[doc = " @note Does nothing if no override is set for the line."] + pub fn gpiod_line_config_clear_output_value_override( + config: *mut gpiod_line_config, + offset: ::std::os::raw::c_uint, + ); +} +extern "C" { + #[doc = " @brief Check if the output value is overridden for a line."] + #[doc = " @param config Line config object."] + #[doc = " @param offset The offset of the line to check for the override."] + #[doc = " @return True if output value is overridden for the line, false otherwise."] + pub fn gpiod_line_config_output_value_is_overridden( + config: *mut gpiod_line_config, + offset: ::std::os::raw::c_uint, + ) -> bool; +} +extern "C" { + #[doc = " @brief Get the default output value."] + #[doc = " @param config Line config object."] + #[doc = " @return Output value for any non-overridden line."] + pub fn gpiod_line_config_get_output_value_default( + config: *mut gpiod_line_config, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Get the configured output value for a line."] + #[doc = " @param config Line config object."] + #[doc = " @param offset Line offset for which to read the value."] + #[doc = " @return Output value for the line if the config object were used in a"] + #[doc = "\t request."] + pub fn gpiod_line_config_get_output_value_offset( + config: *mut gpiod_line_config, + offset: ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int; +} +pub const GPIOD_LINE_CONFIG_PROP_DIRECTION: ::std::os::raw::c_uint = 1; +pub const GPIOD_LINE_CONFIG_PROP_EDGE_DETECTION: ::std::os::raw::c_uint = 2; +pub const GPIOD_LINE_CONFIG_PROP_BIAS: ::std::os::raw::c_uint = 3; +pub const GPIOD_LINE_CONFIG_PROP_DRIVE: ::std::os::raw::c_uint = 4; +pub const GPIOD_LINE_CONFIG_PROP_ACTIVE_LOW: ::std::os::raw::c_uint = 5; +pub const GPIOD_LINE_CONFIG_PROP_DEBOUNCE_PERIOD_US: ::std::os::raw::c_uint = 6; +#[doc = " Debounce period."] +pub const GPIOD_LINE_CONFIG_PROP_EVENT_CLOCK: ::std::os::raw::c_uint = 7; +pub const GPIOD_LINE_CONFIG_PROP_OUTPUT_VALUE: ::std::os::raw::c_uint = 8; +#[doc = " @brief List of properties that can be stored in a line_config object."] +#[doc = ""] +#[doc = " Used when retrieving the overrides."] +pub type _bindgen_ty_8 = ::std::os::raw::c_uint; +extern "C" { + #[doc = " @brief Get the total number of overridden settings stored in the line config"] + #[doc = "\t object."] + #[doc = " @param config Line config object."] + #[doc = " @return Number of individual overridden settings."] + pub fn gpiod_line_config_get_num_overrides(config: *mut gpiod_line_config) -> size_t; +} +extern "C" { + #[doc = " @brief Get the list of overridden offsets and the corresponding types of"] + #[doc = "\t overridden settings."] + #[doc = " @param config Line config object."] + #[doc = " @param offsets Array to store the overidden offsets. Must be sized to hold"] + #[doc = "\t\t the number of unsigned integers returned by"] + #[doc = "\t\t ::gpiod_line_config_get_num_overrides."] + #[doc = " @param props Array to store the types of overridden settings. Must be sized"] + #[doc = "\t\tto hold the number of integers returned by"] + #[doc = "\t\t::gpiod_line_config_get_num_overrides."] + #[doc = ""] + #[doc = " The overridden (offset, prop) pairs are stored in the \\p offsets and"] + #[doc = " \\p props arrays, with the pairs having the same index."] + pub fn gpiod_line_config_get_overrides( + config: *mut gpiod_line_config, + offsets: *mut ::std::os::raw::c_uint, + props: *mut ::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 offsets of the lines to be requested."] + #[doc = " @param config Request config object."] + #[doc = " @param num_offsets Number of offsets to set."] + #[doc = " @param offsets Array of offsets, containing \\p num_offsets entries."] + #[doc = " @note If too many offsets were specified, the offsets above the limit"] + #[doc = " accepted by the kernel (64 lines) are silently dropped."] + pub fn gpiod_request_config_set_offsets( + config: *mut gpiod_request_config, + num_offsets: size_t, + offsets: *const ::std::os::raw::c_uint, + ); +} +extern "C" { + #[doc = " @brief Get the number of offsets configured in this request config."] + #[doc = " @param config Request config object."] + #[doc = " @return Number of line offsets in this request config."] + pub fn gpiod_request_config_get_num_offsets(config: *mut gpiod_request_config) -> size_t; +} +extern "C" { + #[doc = " @brief Get the offsets of lines in the request config."] + #[doc = " @param config Request config object."] + #[doc = " @param offsets Array to store offsets. Must be sized to hold the number of"] + #[doc = "\t\t lines returned by ::gpiod_request_config_get_num_offsets."] + pub fn gpiod_request_config_get_offsets( + config: *mut gpiod_request_config, + offsets: *mut ::std::os::raw::c_uint, + ); +} +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_9 = ::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; +} From patchwork Tue Aug 2 12:18:07 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 595001 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id D4B34C19F2D for ; Tue, 2 Aug 2022 12:18:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236916AbiHBMSg (ORCPT ); Tue, 2 Aug 2022 08:18:36 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38862 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236338AbiHBMSe (ORCPT ); Tue, 2 Aug 2022 08:18:34 -0400 Received: from mail-pg1-x52e.google.com (mail-pg1-x52e.google.com [IPv6:2607:f8b0:4864:20::52e]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E53154F689 for ; Tue, 2 Aug 2022 05:18:29 -0700 (PDT) Received: by mail-pg1-x52e.google.com with SMTP id 12so12220992pga.1 for ; Tue, 02 Aug 2022 05:18:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=jT1QL/TYC6skFF8+F2Bp2JWBcAE5kwJOkiRecwRtsPI=; b=So1d2ZHqiikUUyVL2NvOq6yQBcXtEsjAxxt7mWTdLjBq3bMY4ggpprezqz7K7lud24 WE1P68d5BZskSI+HGgNX6AsY2/A4qmN3gYxBUO0aNHcEEAN1Wr1azqw6FwLIr/iFSP+S ZgwIkB48xIdT0mnJ3J2xUECU6oDsOAvLV4wGgLKXvzbJgkwA30cAszQjOfFM7c6FRZE+ ocJOd2G+vsCbSN48s4MNJrkmw/QYI8u4iamhAvseoyodqBQe3EKnVu7iB5bjylDrLA9n BGZgKcoOQgS+0BoJ67+UyjP9mNmerNzmIbybYnS8mNMHcsTvAx0TmI3a/aM5RY6AKPJz 1cbg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=jT1QL/TYC6skFF8+F2Bp2JWBcAE5kwJOkiRecwRtsPI=; b=6Gdtz1RvZ+5utQli0CXPVJQc0ni15y1kGl8cKA9xTfgheMJcHCvP2c/zwBNIhIx6VN Pz0hAHUo89ldAUYlfcjQY49OifOESpP5WtS+CIZNIY0q/xUrP7KLWMLRoBOV9K7BkwZL 3kVYUAfQ1M2q5StmVl4lOTT0W3ttIAV+/xVv/ZLspexFXUDe4ZEVc25jCS+LQ8YI6tp7 fo0U1YEq2mxPULQlX+bs5/i3AiuaMlxOzx5epu45QdPqQEa3C6Yhfw0ggPDuQOqRFtIz mWoAOeSthSOrpR8/RKOQsdoSwR52j3HZCNnmQ6k6mGVN7kjerZv2Xjn3KJHFK0LmsvdE SNxA== X-Gm-Message-State: ACgBeo1WoI65X9PL7THYJHnxu0fjXti7E6++RpzPXZSZq0iow4EWQHJT bvN8/IeJB1NqpV0yBU3gE2ICzg== X-Google-Smtp-Source: AA6agR4wFhu3BF06lqG3GBFNCS0THOvSFIHugJCB6icLC0tPg1OKN3cn7lczEylnLUWZU2mqDExBYg== X-Received: by 2002:a05:6a00:2d1:b0:52d:36bb:14d7 with SMTP id b17-20020a056a0002d100b0052d36bb14d7mr12924368pft.22.1659442707792; Tue, 02 Aug 2022 05:18:27 -0700 (PDT) Received: from localhost ([122.171.18.80]) by smtp.gmail.com with ESMTPSA id g31-20020a63201f000000b0041ae78c3493sm9065214pgg.52.2022.08.02.05.18.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 02 Aug 2022 05:18:27 -0700 (PDT) From: Viresh Kumar To: Linus Walleij , Bartosz Golaszewski , Kent Gibson Cc: Viresh Kumar , Vincent Guittot , linux-gpio@vger.kernel.org, Miguel Ojeda , Wedson Almeida Filho , =?utf-8?q?Alex_Benn=C3=A9e?= , stratos-dev@op-lists.linaro.org, Gerard Ryan Subject: [PATCH V5 3/8] libgpiod: Add rust wrapper crate Date: Tue, 2 Aug 2022 17:48:07 +0530 Message-Id: <802b554ebdae1a389f56bfc21e6ec7ad8677f9ff.1659442066.git.viresh.kumar@linaro.org> X-Mailer: git-send-email 2.31.1.272.g89b43f80a514 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org Add rust wrapper crate, around the libpiod-sys crate added earlier, to provide a convenient interface for the users. Signed-off-by: Viresh Kumar --- 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 | 102 ++++ bindings/rust/libgpiod/src/event_buffer.rs | 90 +++ bindings/rust/libgpiod/src/info_event.rs | 68 +++ bindings/rust/libgpiod/src/lib.rs | 477 +++++++++++++++ bindings/rust/libgpiod/src/line_config.rs | 586 +++++++++++++++++++ bindings/rust/libgpiod/src/line_info.rs | 180 ++++++ bindings/rust/libgpiod/src/line_request.rs | 246 ++++++++ bindings/rust/libgpiod/src/request_config.rs | 119 ++++ 11 files changed, 2135 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/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 + +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>(path: &P) -> Result { + // 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, + info: Info, +} + +unsafe impl Send for Chip {} +unsafe impl Sync for Chip {} + +impl Chip { + /// Find a chip by path. + pub fn open>(path: &P) -> Result { + 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::new(self.ichip.clone()) + } + + /// Get a snapshot of information about the line. + pub fn line_info(&self, offset: Offset) -> Result { + 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::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 { + 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) -> Result { + 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::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 { + // 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::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) -> Result { + 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..ce583916a2e3 --- /dev/null +++ b/bindings/rust/libgpiod/src/edge_event.rs @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use std::sync::Arc; +use std::time::Duration; + +use vmm_sys_util::errno::Error as Errno; + +use super::{edge::event::BufferInternal, 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 { + ibuffer: Option>, + event: *mut gpiod::gpiod_edge_event, +} + +impl Event { + /// Get an event stored in the buffer. + pub(crate) fn new(ibuffer: &Arc, index: u64) -> Result { + let event = unsafe { gpiod::gpiod_edge_event_buffer_get_event(ibuffer.buffer(), index) }; + if event.is_null() { + return Err(Error::OperationFailed( + OperationType::EdgeEventBufferGetEvent, + Errno::last(), + )); + } + + Ok(Self { + ibuffer: Some(ibuffer.clone()), + event, + }) + } + + pub fn event_clone(&self) -> Result { + let event = unsafe { gpiod::gpiod_edge_event_copy(self.event) }; + if event.is_null() { + return Err(Error::OperationFailed( + OperationType::EdgeEventCopy, + Errno::last(), + )); + } + + Ok(Self { + ibuffer: None, + event, + }) + } + + /// Get the event type. + pub fn event_type(&self) -> Result { + 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 Drop for Event { + /// Free the edge event. + fn drop(&mut self) { + // Free the event only if a copy is made + if self.ibuffer.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..e272e7aa9e9d --- /dev/null +++ b/bindings/rust/libgpiod/src/event_buffer.rs @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use std::os::raw::c_ulong; +use std::sync::Arc; + +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(crate) struct BufferInternal { + buffer: *mut gpiod::gpiod_edge_event_buffer, +} + +impl BufferInternal { + /// 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 { + 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 + } +} + +impl Drop for BufferInternal { + /// Free the edge event buffer and release all associated resources. + fn drop(&mut self) { + unsafe { gpiod::gpiod_edge_event_buffer_free(self.buffer) }; + } +} + +/// Line edge events buffer +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Buffer { + ibuffer: Arc, +} + +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 { + Ok(Self { + ibuffer: Arc::new(BufferInternal::new(capacity)?), + }) + } + + /// Private helper, Returns gpiod_edge_event_buffer + pub(crate) fn buffer(&self) -> *mut gpiod::gpiod_edge_event_buffer { + self.ibuffer.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::new(&self.ibuffer, 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 + } +} 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 + +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) -> Result { + 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::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::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..4d49384c60f1 --- /dev/null +++ b/bindings/rust/libgpiod/src/lib.rs @@ -0,0 +1,477 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Rust wrappers for GPIOD APIs +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +//! 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; + +/// GPIO chip line related definitions. +pub mod line { + pub use crate::line_config::*; + pub use crate::line_info::*; + pub use crate::line_request::*; +} + +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, PartialEq)] +pub enum OperationType { + ChipOpen, + ChipGetFd, + ChipWaitInfoEvent, + ChipGetLine, + ChipGetLineInfo, + ChipInfoGet, + ChipReadInfoEvent, + ChipWatchLineInfo, + EdgeEventBufferGetEvent, + EdgeEventCopy, + EdgeEventBufferNew, + InfoEventGetLineInfo, + LineConfigNew, + LineConfigGetOutValDefault, + LineConfigGetOutValOffset, + LineRequest, + LineRequestReconfigLines, + LineRequestGetVal, + LineRequestGetValSubset, + LineRequestSetVal, + LineRequestSetValSubset, + LineRequestReadEdgeEvent, + LineRequestWaitEdgeEvent, + 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 = std::result::Result; + +/// 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("Setting mismatch, expected {0} found: {1}")] + SettingMismatch(Setting, Setting), + #[error("Invalid Arguments")] + InvalidArguments, + #[error("Std Io Error")] + IoError, +} + +/// Value settings. +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum Value { + /// Active + Active, + /// Inactive + InActive, +} + +/// Maps offset to Value. +pub type ValueMap = IntMap; + +impl Value { + pub fn new(val: i32) -> Result { + 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, 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 { + 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, 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> { + 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) -> 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, 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 { + 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, 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> { + 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) -> 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, 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, +} + +/// Maps offset to SettingKind. +pub type SettingKindMap = IntMap; + +impl SettingKind { + fn new(kind: u32) -> Result { + match kind { + gpiod::GPIOD_LINE_CONFIG_PROP_DIRECTION => Ok(SettingKind::Direction), + gpiod::GPIOD_LINE_CONFIG_PROP_EDGE_DETECTION => Ok(SettingKind::EdgeDetection), + gpiod::GPIOD_LINE_CONFIG_PROP_BIAS => Ok(SettingKind::Bias), + gpiod::GPIOD_LINE_CONFIG_PROP_DRIVE => Ok(SettingKind::Drive), + gpiod::GPIOD_LINE_CONFIG_PROP_ACTIVE_LOW => Ok(SettingKind::ActiveLow), + gpiod::GPIOD_LINE_CONFIG_PROP_DEBOUNCE_PERIOD_US => Ok(SettingKind::DebouncePeriod), + gpiod::GPIOD_LINE_CONFIG_PROP_EVENT_CLOCK => Ok(SettingKind::EventClock), + gpiod::GPIOD_LINE_CONFIG_PROP_OUTPUT_VALUE => Ok(SettingKind::OutputValue), + _ => Err(Error::InvalidEnumValue("SettingKind", kind)), + } + } +} + +/// Line settings. +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum Setting { + /// Line direction. + Direction(Direction), + /// Bias. + Bias(Option), + /// Drive. + Drive(Drive), + /// Edge detection. + EdgeDetection(Option), + /// Active-low setting. + ActiveLow(bool), + /// Debounce period. + DebouncePeriod(Duration), + /// Event clock type. + EventClock(EventClock), + /// Output value. + OutputValue(Value), +} + +/// Maps offset to Setting. +pub type SettingMap = IntMap; + +impl fmt::Display for Setting { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +/// Event clock settings. +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum EventClock { + /// Line uses the monotonic clock for edge event timestamps. + Monotonic, + /// Line uses the realtime clock for edge event timestamps. + Realtime, +} + +impl EventClock { + fn new(clock: u32) -> Result { + match clock { + gpiod::GPIOD_LINE_EVENT_CLOCK_MONOTONIC => Ok(EventClock::Monotonic), + gpiod::GPIOD_LINE_EVENT_CLOCK_REALTIME => Ok(EventClock::Realtime), + _ => 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, + } + } +} + +/// Line status change event types. +#[derive(Copy, Clone, Debug, 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 { + 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, PartialEq)] +pub enum EdgeKind { + /// Rising edge event. + Rising, + /// Falling edge event. + Falling, +} + +impl EdgeKind { + fn new(kind: u32) -> Result { + 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>(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>(path: &P) -> Result> { + 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..148039cafa33 --- /dev/null +++ b/bindings/rust/libgpiod/src/line_config.rs @@ -0,0 +1,586 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use std::os::raw::c_ulong; +use std::time::Duration; + +use vmm_sys_util::errno::Error as Errno; + +use super::{ + gpiod, Bias, Direction, Drive, Edge, Error, EventClock, Offset, OperationType, Result, Setting, + SettingKind, SettingKindMap, SettingMap, Value, ValueMap, +}; + +/// Line configuration objects. +/// +/// The line-config object contains the configuration for lines that can be +/// used in two cases: +/// - when making a line request +/// - when reconfiguring a set of already requested lines. +/// +/// A new line-config object is instantiated with a set of sane defaults +/// for all supported configuration settings. Those defaults can be modified by +/// the caller. Default values can be overridden by applying different values +/// for specific lines. When making a request or reconfiguring an existing one, +/// the overridden settings for specific lines take precedence. For lines +/// without an override the requested default settings are used. +/// +/// For every setting there are two mutators (one setting the default and one +/// for the per-line override), two getters (one for reading the global +/// default and one for retrieving the effective value for the line), +/// a function for testing if a setting is overridden for the line +/// and finally a function for clearing the overrides (per line). +/// +/// The mutators don't return errors. If the set of options is too complex to +/// be translated into kernel uAPI structures then an error will be returned at +/// the time of the request or reconfiguration. If an invalid value was passed +/// to any of the mutators then the default value will be silently used instead. +/// +/// Operating on lines in struct Config has no immediate effect on real +/// GPIOs, it only manipulates the config object in memory. Those changes are +/// only applied to the hardware at the time of the request or reconfiguration. +/// +/// Overrides for lines that don't end up being requested are silently ignored +/// both in line::Request::new() as well as in line::Request::reconfigure_lines(). +/// +/// In cases where all requested lines are using the one configuration, the +/// line overrides can be entirely ignored when preparing the configuration. + +#[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 { + 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) } + } + + /// Set the default for line property. + pub fn set_prop_default(&mut self, values: &[Setting]) { + for value in values { + match value { + Setting::Direction(val) => self.set_direction_default(*val), + Setting::EdgeDetection(val) => self.set_edge_detection_default(*val), + Setting::Bias(val) => self.set_bias_default(*val), + Setting::Drive(val) => self.set_drive_default(*val), + Setting::ActiveLow(val) => self.set_active_low_default(*val), + Setting::DebouncePeriod(val) => self.set_debounce_period_default(*val), + Setting::EventClock(val) => self.set_event_clock_default(*val), + Setting::OutputValue(val) => self.set_output_value_default(*val), + } + } + } + + /// Set the override for line property. + pub fn set_prop_override(&mut self, map: SettingMap) { + for (offset, value) in map { + match value { + Setting::Direction(val) => self.set_direction_override(val, offset as u32), + Setting::EdgeDetection(val) => self.set_edge_detection_override(val, offset as u32), + Setting::Bias(val) => self.set_bias_override(val, offset as u32), + Setting::Drive(val) => self.set_drive_override(val, offset as u32), + Setting::ActiveLow(val) => self.set_active_low_override(val, offset as u32), + Setting::DebouncePeriod(val) => { + self.set_debounce_period_override(val, offset as u32) + } + Setting::EventClock(val) => self.set_event_clock_override(val, offset as u32), + Setting::OutputValue(val) => self.set_output_value_override(val, offset as u32), + } + } + } + + /// Clear the override for line property. + pub fn clear_prop_override(&mut self, map: SettingKindMap) { + for (offset, prop) in map { + match prop { + SettingKind::Direction => self.clear_direction_override(offset as u32), + SettingKind::EdgeDetection => self.clear_edge_detection_override(offset as u32), + SettingKind::Bias => self.clear_bias_override(offset as u32), + SettingKind::Drive => self.clear_drive_override(offset as u32), + SettingKind::ActiveLow => self.clear_active_low_override(offset as u32), + SettingKind::DebouncePeriod => self.clear_debounce_period_override(offset as u32), + SettingKind::EventClock => self.clear_event_clock_override(offset as u32), + SettingKind::OutputValue => self.clear_output_value_override(offset as u32), + } + } + } + + /// Check if the prop is overridden for a line. + pub fn prop_is_overridden(&self, property: SettingKind, offset: Offset) -> bool { + match property { + SettingKind::Direction => self.direction_is_overridden(offset), + SettingKind::EdgeDetection => self.edge_detection_is_overridden(offset), + SettingKind::Bias => self.bias_is_overridden(offset), + SettingKind::Drive => self.drive_is_overridden(offset), + SettingKind::ActiveLow => self.active_low_is_overridden(offset), + SettingKind::DebouncePeriod => self.debounce_period_is_overridden(offset), + SettingKind::EventClock => self.event_clock_is_overridden(offset), + SettingKind::OutputValue => self.output_value_is_overridden(offset), + } + } + + /// Get the default prop setting. + pub fn prop_default(&self, property: SettingKind) -> Result { + Ok(match property { + SettingKind::Direction => Setting::Direction(self.direction_default()?), + SettingKind::EdgeDetection => Setting::EdgeDetection(self.edge_detection_default()?), + SettingKind::Bias => Setting::Bias(self.bias_default()?), + SettingKind::Drive => Setting::Drive(self.drive_default()?), + SettingKind::ActiveLow => Setting::ActiveLow(self.active_low_default()), + SettingKind::DebouncePeriod => Setting::DebouncePeriod(self.debounce_period_default()?), + SettingKind::EventClock => Setting::EventClock(self.event_clock_default()?), + SettingKind::OutputValue => Setting::OutputValue(self.output_value_default()?), + }) + } + + /// Get the prop setting for an offset. + pub fn prop_offset(&self, property: SettingKind, offset: Offset) -> Result { + Ok(match property { + SettingKind::Direction => Setting::Direction(self.direction_offset(offset)?), + SettingKind::EdgeDetection => { + Setting::EdgeDetection(self.edge_detection_offset(offset)?) + } + SettingKind::Bias => Setting::Bias(self.bias_offset(offset)?), + SettingKind::Drive => Setting::Drive(self.drive_offset(offset)?), + SettingKind::ActiveLow => Setting::ActiveLow(self.active_low_offset(offset)), + SettingKind::DebouncePeriod => { + Setting::DebouncePeriod(self.debounce_period_offset(offset)?) + } + SettingKind::EventClock => Setting::EventClock(self.event_clock_offset(offset)?), + SettingKind::OutputValue => Setting::OutputValue(self.output_value_offset(offset)?), + }) + } + + /// Set the default line direction. + fn set_direction_default(&mut self, direction: Direction) { + unsafe { + gpiod::gpiod_line_config_set_direction_default( + self.config, + direction.gpiod_direction() as i32, + ) + } + } + + /// Set the direction for a line. + fn set_direction_override(&mut self, direction: Direction, offset: Offset) { + unsafe { + gpiod::gpiod_line_config_set_direction_override( + self.config, + direction.gpiod_direction() as i32, + offset, + ) + } + } + + /// Clear the direction for a line. + fn clear_direction_override(&mut self, offset: Offset) { + unsafe { gpiod::gpiod_line_config_clear_direction_override(self.config, offset) } + } + + /// Check if the direction is overridden for a line. + fn direction_is_overridden(&self, offset: Offset) -> bool { + unsafe { gpiod::gpiod_line_config_direction_is_overridden(self.config, offset) } + } + + /// Get the default direction setting. + fn direction_default(&self) -> Result { + Direction::new( + unsafe { gpiod::gpiod_line_config_get_direction_default(self.config) } as u32, + ) + } + + /// Get the direction of a given line. + /// + /// Direction setting for the line if the config object were used in a request. + fn direction_offset(&self, offset: Offset) -> Result { + Direction::new( + unsafe { gpiod::gpiod_line_config_get_direction_offset(self.config, offset) } as u32, + ) + } + + /// Set the default edge event detection setting. + fn set_edge_detection_default(&mut self, edge: Option) { + unsafe { + gpiod::gpiod_line_config_set_edge_detection_default( + self.config, + Edge::gpiod_edge(edge) as i32, + ) + } + } + + /// Set the edge event detection for a single line. + fn set_edge_detection_override(&mut self, edge: Option, offset: Offset) { + unsafe { + gpiod::gpiod_line_config_set_edge_detection_override( + self.config, + Edge::gpiod_edge(edge) as i32, + offset, + ) + } + } + + /// Clear the edge event detection for a single line. + fn clear_edge_detection_override(&mut self, offset: Offset) { + unsafe { gpiod::gpiod_line_config_clear_edge_detection_override(self.config, offset) } + } + + /// Check if the edge event detection is overridden for a line. + fn edge_detection_is_overridden(&self, offset: Offset) -> bool { + unsafe { gpiod::gpiod_line_config_edge_detection_is_overridden(self.config, offset) } + } + + /// Get the default edge event detection setting. + fn edge_detection_default(&self) -> Result> { + Edge::new( + unsafe { gpiod::gpiod_line_config_get_edge_detection_default(self.config) } as u32, + ) + } + + /// Get the edge event detection setting for a given line. + /// + /// Edge event detection setting for the line if the config object were used in a request. + fn edge_detection_offset(&self, offset: Offset) -> Result> { + Edge::new( + unsafe { gpiod::gpiod_line_config_get_edge_detection_offset(self.config, offset) } + as u32, + ) + } + + /// Set the default bias setting. + fn set_bias_default(&mut self, bias: Option) { + unsafe { + gpiod::gpiod_line_config_set_bias_default(self.config, Bias::gpiod_bias(bias) as i32) + } + } + + /// Set the bias for a single line. + fn set_bias_override(&mut self, bias: Option, offset: Offset) { + unsafe { + gpiod::gpiod_line_config_set_bias_override( + self.config, + Bias::gpiod_bias(bias) as i32, + offset, + ) + } + } + + /// Clear the bias for a single line. + fn clear_bias_override(&mut self, offset: Offset) { + unsafe { gpiod::gpiod_line_config_clear_bias_override(self.config, offset) } + } + + /// Check if the bias is overridden for a line. + fn bias_is_overridden(&self, offset: Offset) -> bool { + unsafe { gpiod::gpiod_line_config_bias_is_overridden(self.config, offset) } + } + + /// Get the default bias setting. + fn bias_default(&self) -> Result> { + Bias::new(unsafe { gpiod::gpiod_line_config_get_bias_default(self.config) } as u32) + } + + /// Get the bias setting for a given line. + /// + /// Bias setting used for the line if the config object were used in a request. + fn bias_offset(&self, offset: Offset) -> Result> { + Bias::new(unsafe { gpiod::gpiod_line_config_get_bias_offset(self.config, offset) } as u32) + } + + /// Set the default drive setting. + fn set_drive_default(&mut self, drive: Drive) { + unsafe { + gpiod::gpiod_line_config_set_drive_default(self.config, drive.gpiod_drive() as i32) + } + } + + /// Set the drive for a single line. + fn set_drive_override(&mut self, drive: Drive, offset: Offset) { + unsafe { + gpiod::gpiod_line_config_set_drive_override( + self.config, + drive.gpiod_drive() as i32, + offset, + ) + } + } + + /// clear the drive for a single line. + fn clear_drive_override(&mut self, offset: Offset) { + unsafe { gpiod::gpiod_line_config_clear_drive_override(self.config, offset) } + } + + /// Check if the drive is overridden for a line. + fn drive_is_overridden(&self, offset: Offset) -> bool { + unsafe { gpiod::gpiod_line_config_drive_is_overridden(self.config, offset) } + } + + /// Get the default drive setting. + fn drive_default(&self) -> Result { + Drive::new(unsafe { gpiod::gpiod_line_config_get_drive_default(self.config) } as u32) + } + + /// Get the drive setting for a given line. + /// + /// The offset of the line for which to read the drive setting. Drive setting for the line if + /// the config object were used in a request. + fn drive_offset(&self, offset: Offset) -> Result { + Drive::new(unsafe { gpiod::gpiod_line_config_get_drive_offset(self.config, offset) } as u32) + } + + /// Set default active-low setting. + fn set_active_low_default(&mut self, active_low: bool) { + unsafe { gpiod::gpiod_line_config_set_active_low_default(self.config, active_low) } + } + + /// Set active-low setting for a single line. + fn set_active_low_override(&mut self, active_low: bool, offset: Offset) { + unsafe { gpiod::gpiod_line_config_set_active_low_override(self.config, active_low, offset) } + } + + /// Clear a single line's active-low setting. + fn clear_active_low_override(&mut self, offset: Offset) { + unsafe { gpiod::gpiod_line_config_clear_active_low_override(self.config, offset) } + } + + /// Check if the active-low is overridden for a line. + fn active_low_is_overridden(&self, offset: Offset) -> bool { + unsafe { gpiod::gpiod_line_config_active_low_is_overridden(self.config, offset) } + } + + /// Check the default active-low setting. + fn active_low_default(&self) -> bool { + unsafe { gpiod::gpiod_line_config_get_active_low_default(self.config) } + } + + /// Check the active-low setting of a line. + /// + /// Active-low setting for the line if the config object were used in a request. + fn active_low_offset(&self, offset: Offset) -> bool { + unsafe { gpiod::gpiod_line_config_get_active_low_offset(self.config, offset) } + } + + /// Set the default debounce period setting. + fn set_debounce_period_default(&mut self, period: Duration) { + unsafe { + gpiod::gpiod_line_config_set_debounce_period_us_default( + self.config, + period.as_micros() as u64, + ) + } + } + + /// Set the debounce period for a single line. + fn set_debounce_period_override(&mut self, period: Duration, offset: Offset) { + unsafe { + gpiod::gpiod_line_config_set_debounce_period_us_override( + self.config, + period.as_micros() as u64, + offset, + ) + } + } + + /// Clear the debounce period for a single line. + fn clear_debounce_period_override(&mut self, offset: Offset) { + unsafe { gpiod::gpiod_line_config_clear_debounce_period_us_override(self.config, offset) } + } + + /// Check if the debounce period setting is overridden. + fn debounce_period_is_overridden(&self, offset: Offset) -> bool { + unsafe { gpiod::gpiod_line_config_debounce_period_us_is_overridden(self.config, offset) } + } + + /// Get the default debounce period. + fn debounce_period_default(&self) -> Result { + Ok(Duration::from_micros(unsafe { + gpiod::gpiod_line_config_get_debounce_period_us_default(self.config) + })) + } + + /// Get the debounce period for a given line. + /// + /// Debounce period for the line if the config object were used in a request, 0 if debouncing + /// is disabled. + fn debounce_period_offset(&self, offset: Offset) -> Result { + Ok(Duration::from_micros(unsafe { + gpiod::gpiod_line_config_get_debounce_period_us_offset(self.config, offset) + })) + } + + /// Set the default event clock setting. + fn set_event_clock_default(&mut self, clock: EventClock) { + unsafe { + gpiod::gpiod_line_config_set_event_clock_default( + self.config, + clock.gpiod_clock() as i32, + ) + } + } + + /// Set the event clock for a single line. + fn set_event_clock_override(&mut self, clock: EventClock, offset: Offset) { + unsafe { + gpiod::gpiod_line_config_set_event_clock_override( + self.config, + clock.gpiod_clock() as i32, + offset, + ) + } + } + + /// Clear the event clock for a single line. + fn clear_event_clock_override(&mut self, offset: Offset) { + unsafe { gpiod::gpiod_line_config_clear_event_clock_override(self.config, offset) } + } + + /// Check if the event clock is overridden for a line. + fn event_clock_is_overridden(&self, offset: Offset) -> bool { + unsafe { gpiod::gpiod_line_config_event_clock_is_overridden(self.config, offset) } + } + + /// Get the default event clock setting. + fn event_clock_default(&self) -> Result { + EventClock::new( + unsafe { gpiod::gpiod_line_config_get_event_clock_default(self.config) } as u32, + ) + } + + /// Get the event clock setting for a given line. + /// + /// Event clock setting for the line if the config object were used in a request. + fn event_clock_offset(&self, offset: Offset) -> Result { + EventClock::new(unsafe { + gpiod::gpiod_line_config_get_event_clock_offset(self.config, offset) + } as u32) + } + + /// Set the default output value setting. + fn set_output_value_default(&mut self, value: Value) { + unsafe { gpiod::gpiod_line_config_set_output_value_default(self.config, value.value()) } + } + + /// Set the output value for a line. + fn set_output_value_override(&mut self, value: Value, offset: Offset) { + unsafe { + gpiod::gpiod_line_config_set_output_value_override(self.config, value.value(), offset) + } + } + + /// Set the output values for a set of lines. + pub fn set_output_values(&mut 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()); + } + + unsafe { + gpiod::gpiod_line_config_set_output_values( + self.config, + values.len() as c_ulong, + offsets.as_ptr(), + values.as_ptr(), + ); + } + + Ok(()) + } + + /// Clear the output value for a line. + fn clear_output_value_override(&mut self, offset: Offset) { + unsafe { gpiod::gpiod_line_config_clear_output_value_override(self.config, offset) } + } + + /// Check if the output value is overridden for a line. + fn output_value_is_overridden(&self, offset: Offset) -> bool { + unsafe { gpiod::gpiod_line_config_output_value_is_overridden(self.config, offset) } + } + + /// Get the default output value, 0 or 1. + fn output_value_default(&self) -> Result { + let value = unsafe { gpiod::gpiod_line_config_get_output_value_default(self.config) }; + + if value != 0 && value != 1 { + Err(Error::OperationFailed( + OperationType::LineConfigGetOutValDefault, + Errno::last(), + )) + } else { + Value::new(value) + } + } + + /// Get the output value configured for a given line, 0 or 1. + fn output_value_offset(&self, offset: Offset) -> Result { + let value = + unsafe { gpiod::gpiod_line_config_get_output_value_offset(self.config, offset) }; + + if value != 0 && value != 1 { + Err(Error::OperationFailed( + OperationType::LineConfigGetOutValOffset, + Errno::last(), + )) + } else { + Value::new(value) + } + } + + /// Get the list of overridden offsets and the corresponding types of overridden settings. + pub fn overrides(&self) -> Result> { + let num = unsafe { gpiod::gpiod_line_config_get_num_overrides(self.config) } as usize; + if num == 0 { + return Ok(Vec::new()); + } + + let mut overrides = Vec::new(); + let mut offset: Vec = vec![0; num]; + let mut props = vec![0_i32; num]; + + unsafe { + gpiod::gpiod_line_config_get_overrides( + self.config, + offset.as_mut_ptr(), + props.as_mut_ptr(), + ) + }; + + for i in 0..num { + overrides.push((offset[i], SettingKind::new(props[i] as u32)?)); + } + + Ok(overrides) + } +} + +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 + +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, offset: Offset) -> Result { + 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, offset: Offset) -> Result { + 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 { + 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::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> { + 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::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> { + 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::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..617efaa34d58 --- /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 + +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, + rconfig: &request::Config, + lconfig: &line::Config, + ) -> Result { + 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 { + 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 { + 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 { + 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 { + 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) -> Result { + 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: &edge::event::Buffer) -> Result { + 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/request_config.rs b/bindings/rust/libgpiod/src/request_config.rs new file mode 100644 index 000000000000..36a78b8da8d5 --- /dev/null +++ b/bindings/rust/libgpiod/src/request_config.rs @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +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, Offset, 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. Similarly to the line-config - the mutators +/// don't return error values. If the values are invalid, in general they are +/// silently adjusted to acceptable ranges. + +#[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 { + 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 offsets of the lines to be requested. + /// + /// If too many offsets were specified, the offsets above the limit accepted + /// by the kernel (64 lines) are silently dropped. + pub fn set_offsets(&self, offsets: &[Offset]) { + unsafe { + gpiod::gpiod_request_config_set_offsets( + self.config, + offsets.len() as c_ulong, + offsets.as_ptr(), + ) + } + } + + /// Get the offsets of lines in the request config. + pub fn offsets(&self) -> Vec { + let num = unsafe { gpiod::gpiod_request_config_get_num_offsets(self.config) }; + let mut offsets = vec![0; num as usize]; + + unsafe { gpiod::gpiod_request_config_get_offsets(self.config, offsets.as_mut_ptr()) }; + offsets + } + + /// 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) } + } +} From patchwork Tue Aug 2 12:18:08 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 595943 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id E7EECC00140 for ; Tue, 2 Aug 2022 12:18:37 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236920AbiHBMSf (ORCPT ); Tue, 2 Aug 2022 08:18:35 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38848 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236865AbiHBMSe (ORCPT ); Tue, 2 Aug 2022 08:18:34 -0400 Received: from mail-pl1-x630.google.com (mail-pl1-x630.google.com [IPv6:2607:f8b0:4864:20::630]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id DBF8B4F641 for ; Tue, 2 Aug 2022 05:18:31 -0700 (PDT) Received: by mail-pl1-x630.google.com with SMTP id o3so13283159ple.5 for ; Tue, 02 Aug 2022 05:18:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=ZMGqBqIV0nC0OVfIDdIBImvZfRBkK8294g/iBwADKuM=; b=RvZx1L20Q6u5f9/n7aJCbNVnKzgCS9wU/X/h6giQHat6ScL2VxdAasWR+BWZ8vrdXg 9NQJHQgZe5lXQhAueOZBlVr19jfeKfhLrtaQSfL7LcLDqLW3Gzi2tTsg0Nkx+uN12Z+s mX3wfdww7M1JVPz4TO3crppN0m6MxzrN4IlmaYgHu50h0Dg2ysQ5AFuWESJnsPE4OZyJ lxRI4jVGZRqwExLgHShzlXAR0Mby81k2598hzjvplOQBAp6PoxP8bh0PgISXeALlEbw9 7Kru4w/MlJ9R08aGB+AnjTT5k/VdOr0E8RRHrLQE2PurIrcTKu6jUk4DpqvitjofK8an MhUg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=ZMGqBqIV0nC0OVfIDdIBImvZfRBkK8294g/iBwADKuM=; b=22CQfc/Z/Ldgmgl78PNczFncm/C3UjNWa5dfQM6CsrAQIa03IhWW8y9UG8+M9IWPin WsSbyRxPdLYMMez0YJ7Qz8MsDxjqqHiTWqVLB62iEkhhRLV5NXf4ytphngldIib26Zvj Tu7eoAqEM94TnUlyVIPSg6BYGLwYCGGxiVHnAZNlKjg9IzPGTn4SJHU2kBw3tFD0cQrB nnBrdoI7cM+B2W57m1csD7uw5rYB+ZDc8q/EACi+EKuma5Wv62P/eyNxfjCVZS1adunc b/1JErzIFUllycQxUhPldMRVVSUAJY8hxcmx6GkGYRkN6EKWD9Ot8sUTYFXTAYbYtKMx uAzQ== X-Gm-Message-State: ACgBeo3WJsF1bnqkBxeEB382AytZVuo0+olcJKSCMVn7cztRv/8HA29h jfrGFRHg6dwCNOfY84R9IVhLuw== X-Google-Smtp-Source: AA6agR5wddc439RnEXfavpil3To6aTqEtVD+SfFPrA3NxBMO2Yg+KIEqaytb7JKIWsjv/SxOs9b6rw== X-Received: by 2002:a17:90a:e616:b0:1f5:35a6:6008 with SMTP id j22-20020a17090ae61600b001f535a66008mr1025378pjy.134.1659442710333; Tue, 02 Aug 2022 05:18:30 -0700 (PDT) Received: from localhost ([122.171.18.80]) by smtp.gmail.com with ESMTPSA id u6-20020a170903124600b0016f04c098ddsm1692419plh.226.2022.08.02.05.18.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 02 Aug 2022 05:18:30 -0700 (PDT) From: Viresh Kumar To: Linus Walleij , Bartosz Golaszewski , Kent Gibson Cc: Viresh Kumar , Vincent Guittot , linux-gpio@vger.kernel.org, Miguel Ojeda , Wedson Almeida Filho , =?utf-8?q?Alex_Benn=C3=A9e?= , stratos-dev@op-lists.linaro.org, Gerard Ryan Subject: [PATCH V5 4/8] libgpiod: Add rust examples Date: Tue, 2 Aug 2022 17:48:08 +0530 Message-Id: X-Mailer: git-send-email 2.31.1.272.g89b43f80a514 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org Add examples for the usage of the rust bindings, quite similar to the ones in cxx bindings. Signed-off-by: Viresh Kumar --- bindings/rust/libgpiod/examples/gpiodetect.rs | 30 ++++++ bindings/rust/libgpiod/examples/gpiofind.rs | 35 +++++++ bindings/rust/libgpiod/examples/gpioget.rs | 44 +++++++++ bindings/rust/libgpiod/examples/gpioinfo.rs | 95 +++++++++++++++++++ bindings/rust/libgpiod/examples/gpiomon.rs | 71 ++++++++++++++ bindings/rust/libgpiod/examples/gpioset.rs | 64 +++++++++++++ bindings/rust/libgpiod/examples/gpiowatch.rs | 54 +++++++++++ 7 files changed, 393 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 +// +// 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 = 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 +// +// 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 = env::args().collect(); + if args.len() != 2 { + println!("Usage: {} ", 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..7dccde3debed --- /dev/null +++ b/bindings/rust/libgpiod/examples/gpioget.rs @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar +// +// Simplified Rust implementation of gpioget tool. + +use std::env; + +use libgpiod::{chip::Chip, line, request, Direction, Error, Offset, Result, Setting, SettingMap}; + +fn main() -> Result<()> { + let args: Vec = env::args().collect(); + if args.len() < 3 { + println!("Usage: {} ...", args[0]); + return Err(Error::InvalidArguments); + } + + let mut config = line::Config::new()?; + let mut offsets = Vec::::new(); + let mut map = SettingMap::new(); + + for arg in &args[2..] { + let offset = arg.parse::().map_err(|_| Error::InvalidArguments)?; + + offsets.push(offset); + map.insert(offset.into(), Setting::Direction(Direction::Input)); + } + + config.set_prop_override(map); + + let path = format!("/dev/gpiochip{}", args[1]); + let chip = Chip::open(&path)?; + + let rconfig = request::Config::new()?; + rconfig.set_consumer(&args[0]); + rconfig.set_offsets(&offsets); + + let request = chip.request_lines(&rconfig, &config)?; + 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 +// +// 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 = 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::() + .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..f2a52663b8ea --- /dev/null +++ b/bindings/rust/libgpiod/examples/gpiomon.rs @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar +// +// Simplified Rust implementation of the gpiomon tool. + +use std::env; + +use libgpiod::{chip::Chip, edge, line, request, Edge, EdgeKind, Error, Offset, Result, Setting}; + +fn usage(name: &str) { + println!("Usage: {} ...", name); +} + +fn main() -> Result<()> { + let args: Vec = env::args().collect(); + if args.len() < 3 { + usage(&args[0]); + return Err(Error::InvalidArguments); + } + + let mut config = line::Config::new()?; + let mut offsets = Vec::::new(); + + for arg in &args[2..] { + let offset = arg.parse::().map_err(|_| Error::InvalidArguments)?; + + offsets.push(offset); + } + + config.set_prop_default(&[Setting::EdgeDetection(Some(Edge::Both))]); + + let path = format!("/dev/gpiochip{}", args[1]); + let chip = Chip::open(&path)?; + + let rconfig = request::Config::new()?; + rconfig.set_offsets(&offsets); + + let buffer = edge::event::Buffer::new(1)?; + let request = chip.request_lines(&rconfig, &config)?; + + 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(&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..b92d8bcd39c5 --- /dev/null +++ b/bindings/rust/libgpiod/examples/gpioset.rs @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar +// +// 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, Setting, Value, ValueMap, +}; + +fn usage(name: &str) { + println!("Usage: {} = ...", name); +} + +fn main() -> Result<()> { + let args: Vec = env::args().collect(); + if args.len() < 3 { + usage(&args[0]); + return Err(Error::InvalidArguments); + } + + let mut config = line::Config::new()?; + let mut offsets = Vec::::new(); + let mut map = ValueMap::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::() + .map_err(|_| Error::InvalidArguments)?; + let value = pair[1] + .parse::() + .map_err(|_| Error::InvalidArguments)?; + + offsets.push(offset); + map.insert(offset.into(), Value::new(value)?); + } + + config.set_prop_default(&[Setting::Direction(Direction::Output)]); + config.set_output_values(map)?; + + let path = format!("/dev/gpiochip{}", args[1]); + let chip = Chip::open(&path)?; + + let rconfig = request::Config::new()?; + rconfig.set_consumer(&args[0]); + rconfig.set_offsets(&offsets); + + chip.request_lines(&rconfig, &config)?; + + // 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 +// +// Simplified Rust implementation of the gpiowatch tool. + +use std::env; + +use libgpiod::{chip::Chip, Error, Offset, Result}; + +fn usage(name: &str) { + println!("Usage: {} ...", name); +} + +fn main() -> Result<()> { + let args: Vec = 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::() + .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(()) +} From patchwork Tue Aug 2 12:18:09 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 595942 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5B1B7C00140 for ; Tue, 2 Aug 2022 12:18:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236338AbiHBMSi (ORCPT ); Tue, 2 Aug 2022 08:18:38 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38860 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236894AbiHBMSf (ORCPT ); Tue, 2 Aug 2022 08:18:35 -0400 Received: from mail-pg1-x52f.google.com (mail-pg1-x52f.google.com [IPv6:2607:f8b0:4864:20::52f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 88AEC4F18F for ; Tue, 2 Aug 2022 05:18:33 -0700 (PDT) Received: by mail-pg1-x52f.google.com with SMTP id q16so12199617pgq.6 for ; Tue, 02 Aug 2022 05:18:33 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=Cj3RZKURKt8Xu5v1/cctRzFsqTtNcw1t7dkd6bYhNrU=; b=rcF40XwpS6dCJ+td688LUwj9nQcD03tdeWhJgEh46aGDZgbTXY14q3z7sgpla42Hnj yGWn10pJdOfJvkXN4+nsiMORRWYCKlZIFfaQypbiRdlkjnvr+gYn42I5DE+pxDXhASnF XB2ADmVVfPFIW7HoKvwB5vTaWt49Jvkc4HevehEpuWsIHnm5Zb7Pod5aw12HSlmwqe5E 6yLgCFmlK3ukYD3Fi8RWn3Lhu93xm4GnMhexoCmCbxtJa71HDgQC/1jhWqDCv2aywl0e zPWL17/l8PAoANZ2r6+87p68SqJ6oFqyBXIjvEUU922+li/30tPPIQFHjC4JJWtdnfQf KJLQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=Cj3RZKURKt8Xu5v1/cctRzFsqTtNcw1t7dkd6bYhNrU=; b=O9eK7uQE3wAZdzp9nKrjMzBbTKCeSgb7j0jj4Z4QhYbjxX6S8bw9MXdBV9L/F4wXs1 9Z7nzCBVnF76l/34GHlDBLw2vUyhqm8UKDl8ytngk2F48++xWexA7NTHS0r83QqOssX0 bqhB4i8FQSDx56Aahbf3i51xc3+nCcZ6tVv3RTOJ3u4yvc4HwoqWkOF//rWUACu28r+i xovTRWxp4OgbV1hFFD40NxQaTX3oYHCejqmbID10OlXHRAgC6XuoELqsddmgH9cHN23h CXAlwejIoeRULIflihtbeB5aozN0L5Xip6EZRcj3Vyo5I8QB8lWWiiOGjxdV6zb55Ags rwuQ== X-Gm-Message-State: AJIora/laJjAwOJ/tlCUZ6gny1uvRYW0dF1BC6R5bjuPmVRh/v5Bh4jo AiUy9/dFwLp0LIQu9FCwdZa1WQ== X-Google-Smtp-Source: AGRyM1sb7NxkGNHBjVK/Nwspctsn0x6veY9NS4eE3jv1yXtzd4Fdvw5nrL7qgWOhdh115dOzcqFJfw== X-Received: by 2002:a65:6c0c:0:b0:41b:8696:d9d7 with SMTP id y12-20020a656c0c000000b0041b8696d9d7mr17053154pgu.16.1659442712826; Tue, 02 Aug 2022 05:18:32 -0700 (PDT) Received: from localhost ([122.171.18.80]) by smtp.gmail.com with ESMTPSA id b2-20020a1709027e0200b0016dbe37cebdsm11498680plm.246.2022.08.02.05.18.32 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 02 Aug 2022 05:18:32 -0700 (PDT) From: Viresh Kumar To: Linus Walleij , Bartosz Golaszewski , Kent Gibson Cc: Viresh Kumar , Vincent Guittot , linux-gpio@vger.kernel.org, Miguel Ojeda , Wedson Almeida Filho , =?utf-8?q?Alex_Benn=C3=A9e?= , stratos-dev@op-lists.linaro.org, Gerard Ryan Subject: [PATCH V5 5/8] libgpiod: Add gpiosim rust crate Date: Tue, 2 Aug 2022 17:48:09 +0530 Message-Id: <1139c3c1ccb9ecd42646b52c406c3c8271e48afe.1659442066.git.viresh.kumar@linaro.org> X-Mailer: git-send-email 2.31.1.272.g89b43f80a514 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org This adds gpiosim rust crate, which provides helpers to emulate GPIO chips. Signed-off-by: Viresh Kumar --- 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 + +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 { + 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 { + 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 { + 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 { + // 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 { + 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, label: Option<&str>, enable: bool) -> Result { + 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 { + 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() + } +} From patchwork Tue Aug 2 12:18:10 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 595941 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 28469C25B08 for ; Tue, 2 Aug 2022 12:18:42 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236905AbiHBMSi (ORCPT ); Tue, 2 Aug 2022 08:18:38 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38932 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236925AbiHBMSh (ORCPT ); Tue, 2 Aug 2022 08:18:37 -0400 Received: from mail-pl1-x62e.google.com (mail-pl1-x62e.google.com [IPv6:2607:f8b0:4864:20::62e]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id EBB9F4F18F for ; Tue, 2 Aug 2022 05:18:35 -0700 (PDT) Received: by mail-pl1-x62e.google.com with SMTP id m2so6511588pls.4 for ; Tue, 02 Aug 2022 05:18:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=SjJiMlrs/ZeYBiv0yhVqMLyEDnGsJrY4JvzsUIgfAw4=; b=h9rNW/idNQxjuiah6KQHvgMDAP+fWNUGlSLnABXSZ4dDZyfG29pilj2+G8DOpsOYBW RmXBxr6fk6QM7YlTw+Fh81vRug+SUbN1jwNiFGUghDSw2QhRTATQHQmhj26gVI+uGS6y i2HD1IpLqhj1LyocOhHN9jVI9LQKmh0mOK3lfJ5b097Pq/gwQFEVZ0nKQjhP9fdVsXgY Bvz5LnGmsjUm4z0pcxpVfsp1QJcX75yD4oCK22WSkpCrKPbJhDn8TGuTGTLMq5rEZop6 py6eOxPhAWbLW0EWqvUA+lRNpEH1dSgxgHTpjxYB0JXQ6tkNc5gOUYgN8y9k/XDjEPnZ UWCg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=SjJiMlrs/ZeYBiv0yhVqMLyEDnGsJrY4JvzsUIgfAw4=; b=OU8XLn+PiwW/6IhnzvwCBIMHFqMlcNle2/ykfja5+nh0LYdpgjRatKTT+F6+Yemv2t s0U+AlTSIs2L893wtRhIRe9mBdNdtjgZKV6HCmktZIgEdpmfulLdGENHwWzmiyCCiVvx 18ziNESe0Mmb5NcGyEkDQDxvRVvrgp9XKx7KuZQPHqlhIVGBtaLESjePQ6zhjb4VE8SU RdmWuy4HQrfm0xBeo4YlNT/ymDtYJgr5ahqaHIZwO54A3KPZrOE2tr760OM3rbXxlGns HRY3g2lpMLQp+MQ7oCSuuv3z+mtfPh9NVcnhLwkqEswWSGRqWMORmvGkHqvUwU5w0HoC peCQ== X-Gm-Message-State: ACgBeo0c0EpxQ+zu4xXuJb9UW5vlNnOShdQe7FVwVFTdleuozrr/C9tt POrfQZThQ/VptfASE97f9b6W8Q== X-Google-Smtp-Source: AA6agR54LvOA76aUHuIwBmz8kRDVPeATLVK5y++xjOvXhG70TcDOXoEE5yK99u9LEILnlnwy/n/jvw== X-Received: by 2002:a17:902:8a89:b0:16e:d8f4:e166 with SMTP id p9-20020a1709028a8900b0016ed8f4e166mr13589042plo.49.1659442715357; Tue, 02 Aug 2022 05:18:35 -0700 (PDT) Received: from localhost ([122.171.18.80]) by smtp.gmail.com with ESMTPSA id kk15-20020a17090b4a0f00b001f51903e03fsm2278446pjb.32.2022.08.02.05.18.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 02 Aug 2022 05:18:35 -0700 (PDT) From: Viresh Kumar To: Linus Walleij , Bartosz Golaszewski , Kent Gibson Cc: Viresh Kumar , Vincent Guittot , linux-gpio@vger.kernel.org, Miguel Ojeda , Wedson Almeida Filho , =?utf-8?q?Alex_Benn=C3=A9e?= , stratos-dev@op-lists.linaro.org, Gerard Ryan Subject: [PATCH V5 6/8] gpiosim: Add pre generated rust bindings Date: Tue, 2 Aug 2022 17:48:10 +0530 Message-Id: <41976c93e685c8a17859bd32db0dc4db8e8d1b82.1659442066.git.viresh.kumar@linaro.org> X-Mailer: git-send-email 2.31.1.272.g89b43f80a514 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org This adds a copy of pre generated bindings and adds the suggested way of updating those in README. Signed-off-by: Viresh Kumar --- 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 + +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::(), + 32usize, + concat!("Size of: ", stringify!(max_align_t)) + ); + assert_eq!( + ::std::mem::align_of::(), + 16usize, + concat!("Alignment of ", stringify!(max_align_t)) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).__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::())).__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; +} From patchwork Tue Aug 2 12:18:11 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 595000 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 42B44C19F2B for ; Tue, 2 Aug 2022 12:18:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236962AbiHBMS5 (ORCPT ); Tue, 2 Aug 2022 08:18:57 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:39074 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236947AbiHBMSm (ORCPT ); Tue, 2 Aug 2022 08:18:42 -0400 Received: from mail-pl1-x62f.google.com (mail-pl1-x62f.google.com [IPv6:2607:f8b0:4864:20::62f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 13F3B4F6B7 for ; Tue, 2 Aug 2022 05:18:39 -0700 (PDT) Received: by mail-pl1-x62f.google.com with SMTP id t2so13287956ply.2 for ; Tue, 02 Aug 2022 05:18:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=WcZJ/3LzE4uWF3RLXm/rKTMUdh1IFdgd8CiEFRrENFg=; b=dDQHn3Xo4t7yK3mv0v5nkVp1JeqCEQ9/LZYlB9X69j1aypn8sDQnMX+aTft1yqNWxv DUH7ToyYykqYtMfAY8rGreiaTCiA0jZAnzGLqzq9rB6LB623D8/yrLFY5fvDNDkh1VDk 5eWiXfozlcO9C/xoknsglOs62vcvBF051UWh5jn9WNJ1rIS+8ilGzy81g7ErZ9yV5sHL IzCoalVIY3GbJq6+s/a1R74rX54Bi+arOxGnE44ysOM7ApmUOntfFPa8OIh0ig7+TaoU Zgrnj65dSgK+NDTejxOcCpKv44GJPpSsBMn+JRngcVqinG2yufAgwUkGnOF7hfvSHsQ1 RJAA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=WcZJ/3LzE4uWF3RLXm/rKTMUdh1IFdgd8CiEFRrENFg=; b=oVDJaDL2193YR3eyU5A2YJNUsNOaM7I5YBAw9ZORtqatoTRRhL6GK6inrruj9D91wj ydWxplc3Gx3Y+u/CSyX1nmUyIwBsHSFSznIFSYO2M37/BxE4tzVLAYLJu1ZG0lIDNDqN 3nxeSTrinrbvv6PUWteQqb+de4cXM9tiQg9hGzEbY/os0kCUUKddxU2V/nACKkfgv9Em CBUZcjZ9UUQ4p6nKobOj5zBYsuqvpZ9V5Zy4RVutRYxs4/i7ix8ER6m4avbkT0MRvE3C FVBUd0HoAuFQMft8nf83CxefOE7IjZDJpPiv8zQC6/QID4vrfcrS7rkW66YzaCZgO8e3 c+kg== X-Gm-Message-State: ACgBeo1kULAWmL6T9CyHBgWnNqrLTmPm4reci1kXBkK6DeB7o+uMFkYX oy06upI/ErGqKOvZC6JOGYrV1A== X-Google-Smtp-Source: AA6agR5rlrzopFX4w5EYwCp6C7JtGTcwAHwqDtawVVOVn/ID2uJEBPztMuvkQxn2Fe0REpYePL5eyQ== X-Received: by 2002:a17:902:ce09:b0:16c:c7b6:8b63 with SMTP id k9-20020a170902ce0900b0016cc7b68b63mr21310267plg.35.1659442718210; Tue, 02 Aug 2022 05:18:38 -0700 (PDT) Received: from localhost ([122.171.18.80]) by smtp.gmail.com with ESMTPSA id e23-20020a17090a9a9700b001f4e0c71af4sm6557470pjp.28.2022.08.02.05.18.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 02 Aug 2022 05:18:37 -0700 (PDT) From: Viresh Kumar To: Linus Walleij , Bartosz Golaszewski , Kent Gibson Cc: Viresh Kumar , Vincent Guittot , linux-gpio@vger.kernel.org, Miguel Ojeda , Wedson Almeida Filho , =?utf-8?q?Alex_Benn=C3=A9e?= , stratos-dev@op-lists.linaro.org, Gerard Ryan Subject: [PATCH V5 7/8] libgpiod: Add rust tests Date: Tue, 2 Aug 2022 17:48:11 +0530 Message-Id: <709c1000929da4546e0598af930e27b4e437fc86.1659442066.git.viresh.kumar@linaro.org> X-Mailer: git-send-email 2.31.1.272.g89b43f80a514 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org Add tests for the rust bindings, quite similar to the ones in cxx bindings. Signed-off-by: Viresh Kumar --- bindings/rust/libgpiod/Cargo.toml | 3 + bindings/rust/libgpiod/tests/chip.rs | 97 ++++ bindings/rust/libgpiod/tests/common/config.rs | 142 ++++++ bindings/rust/libgpiod/tests/common/mod.rs | 10 + bindings/rust/libgpiod/tests/edge_event.rs | 294 +++++++++++ bindings/rust/libgpiod/tests/info_event.rs | 120 +++++ bindings/rust/libgpiod/tests/line_config.rs | 293 +++++++++++ bindings/rust/libgpiod/tests/line_info.rs | 272 ++++++++++ bindings/rust/libgpiod/tests/line_request.rs | 473 ++++++++++++++++++ .../rust/libgpiod/tests/request_config.rs | 42 ++ 10 files changed, 1746 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/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 + +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..cffc8830ea03 --- /dev/null +++ b/bindings/rust/libgpiod/tests/common/config.rs @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use std::sync::Arc; +use std::time::Duration; + +use gpiosim::Sim; +use libgpiod::{ + chip::Chip, line, request, Bias, Direction, Drive, Edge, EventClock, Offset, Result, Setting, + SettingMap, Value, +}; + +pub(crate) struct TestConfig { + sim: Arc, + chip: Option, + request: Option, + rconfig: request::Config, + lconfig: line::Config, +} + +impl TestConfig { + pub(crate) fn new(ngpio: u64) -> Result { + 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(), + }) + } + + 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 rconfig_set_offsets(&self, offsets: &[Offset]) { + self.rconfig.set_offsets(offsets); + } + + pub(crate) fn lconfig_val( + &mut self, + dir: Option, + val: Option, + val_override: Option<(u32, Value)>, + ) { + let mut settings = Vec::new(); + + if let Some(dir) = dir { + settings.push(Setting::Direction(dir)); + } + + if let Some(val) = val { + settings.push(Setting::OutputValue(val)); + } + + if !settings.is_empty() { + self.lconfig.set_prop_default(&settings); + } + + if let Some((offset, val)) = val_override { + let mut map = SettingMap::new(); + map.insert(offset.into(), Setting::OutputValue(val)); + + self.lconfig.set_prop_override(map); + } + } + + pub(crate) fn lconfig_bias(&mut self, dir: Direction, bias: Option) { + let settings = vec![Setting::Direction(dir), Setting::Bias(bias)]; + self.lconfig.set_prop_default(&settings); + } + + pub(crate) fn lconfig_clock(&mut self, clock: EventClock) { + let settings = vec![Setting::EventClock(clock)]; + self.lconfig.set_prop_default(&settings); + } + + pub(crate) fn lconfig_debounce(&mut self, duration: Duration) { + let settings = vec![ + Setting::Direction(Direction::Input), + Setting::DebouncePeriod(duration), + ]; + self.lconfig.set_prop_default(&settings); + } + + pub(crate) fn lconfig_drive(&mut self, dir: Direction, drive: Drive) { + let settings = vec![Setting::Direction(dir), Setting::Drive(drive)]; + self.lconfig.set_prop_default(&settings); + } + + pub(crate) fn lconfig_edge(&mut self, dir: Option, edge: Option) { + let mut settings = Vec::new(); + + if let Some(dir) = dir { + settings.push(Setting::Direction(dir)); + } + + settings.push(Setting::EdgeDetection(edge)); + self.lconfig.set_prop_default(&settings); + } + + pub(crate) fn lconfig_val_raw(&mut self) { + self.lconfig_val(None, None, None); + } + + pub(crate) fn request_lines(&mut self) -> Result<()> { + let chip = Chip::open(&self.sim.dev_path())?; + + self.request = Some(chip.request_lines(&self.rconfig, &self.lconfig)?); + self.chip = Some(chip); + + Ok(()) + } + + pub(crate) fn sim(&self) -> Arc { + self.sim.clone() + } + + pub(crate) fn chip(&self) -> &Chip { + self.chip.as_ref().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 + +#[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..b903969b25ae --- /dev/null +++ b/bindings/rust/libgpiod/tests/edge_event.rs @@ -0,0 +1,294 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +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, 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, 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, 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 buf = edge::event::Buffer::new(0).unwrap(); + let mut config = TestConfig::new(NGPIO).unwrap(); + config.rconfig_set_offsets(&[GPIO]); + config.lconfig_edge(None, Some(Edge::Both)); + 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(&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); + + // Falling event + assert!(config + .request() + .wait_edge_event(Some(Duration::from_secs(1))) + .unwrap()); + + assert_eq!(config.request().read_edge_events(&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 buf = edge::event::Buffer::new(0).unwrap(); + let mut config = TestConfig::new(NGPIO).unwrap(); + config.rconfig_set_offsets(&[GPIO]); + config.lconfig_edge(None, Some(Edge::Rising)); + 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(&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 buf = edge::event::Buffer::new(0).unwrap(); + let mut config = TestConfig::new(NGPIO).unwrap(); + config.rconfig_set_offsets(&[GPIO]); + config.lconfig_edge(None, Some(Edge::Falling)); + 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(&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.rconfig_set_offsets(&GPIO); + config.lconfig_edge(None, Some(Edge::Both)); + config.request_lines().unwrap(); + + // Generate events + trigger_rising_edge_events_on_two_offsets(config.sim(), GPIO); + + // Rising event GPIO 0 + let buf = 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(&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); + + // Rising event GPIO 1 + assert!(config + .request() + .wait_edge_event(Some(Duration::from_secs(1))) + .unwrap()); + + assert_eq!(config.request().read_edge_events(&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 buf = edge::event::Buffer::new(0).unwrap(); + let mut config = TestConfig::new(NGPIO).unwrap(); + config.rconfig_set_offsets(&[GPIO]); + config.lconfig_edge(None, Some(Edge::Both)); + 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(&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 buf = edge::event::Buffer::new(2).unwrap(); + let mut config = TestConfig::new(NGPIO).unwrap(); + config.rconfig_set_offsets(&[GPIO]); + config.lconfig_edge(None, Some(Edge::Both)); + 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(&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..230b0a0352df --- /dev/null +++ b/bindings/rust/libgpiod/tests/info_event.rs @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +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, Setting, + }; + + fn request_reconfigure_line(chip: Arc) { + spawn(move || { + sleep(Duration::from_millis(10)); + + let lconfig1 = line::Config::new().unwrap(); + let rconfig = request::Config::new().unwrap(); + rconfig.set_offsets(&[7]); + + let request = chip.request_lines(&rconfig, &lconfig1).unwrap(); + + sleep(Duration::from_millis(10)); + + let mut lconfig2 = line::Config::new().unwrap(); + lconfig2.set_prop_default(&[Setting::Direction(Direction::Output)]); + + 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..0d1a5ca7a0a8 --- /dev/null +++ b/bindings/rust/libgpiod/tests/line_config.rs @@ -0,0 +1,293 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +mod common; + +mod line_config { + use std::time::Duration; + + use libgpiod::{ + line, Bias, Direction, Drive, Edge, EventClock, Offset, Setting, SettingKind, + SettingKindMap, SettingMap, Value, ValueMap, + }; + + #[test] + fn default() { + let lconfig = line::Config::new().unwrap(); + + assert_eq!( + lconfig.prop_default(SettingKind::Direction).unwrap(), + Setting::Direction(Direction::AsIs) + ); + assert_eq!( + lconfig.prop_default(SettingKind::EdgeDetection).unwrap(), + Setting::EdgeDetection(None) + ); + assert_eq!( + lconfig.prop_default(SettingKind::Bias).unwrap(), + Setting::Bias(None) + ); + assert_eq!( + lconfig.prop_default(SettingKind::Drive).unwrap(), + Setting::Drive(Drive::PushPull) + ); + assert_eq!( + lconfig.prop_default(SettingKind::ActiveLow).unwrap(), + Setting::ActiveLow(false) + ); + assert_eq!( + lconfig.prop_default(SettingKind::DebouncePeriod).unwrap(), + Setting::DebouncePeriod(Duration::from_millis(0)) + ); + assert_eq!( + lconfig.prop_default(SettingKind::EventClock).unwrap(), + Setting::EventClock(EventClock::Monotonic) + ); + assert_eq!( + lconfig.prop_default(SettingKind::OutputValue).unwrap(), + Setting::OutputValue(Value::InActive) + ); + assert_eq!(lconfig.overrides().unwrap().len(), 0); + } + + mod overrides { + use super::*; + + #[test] + fn direction() { + const GPIO: Offset = 0; + let mut lconfig = line::Config::new().unwrap(); + + lconfig.set_prop_default(&[Setting::Direction(Direction::AsIs)]); + + let mut map = SettingMap::new(); + map.insert(GPIO.into(), Setting::Direction(Direction::Input)); + lconfig.set_prop_override(map); + + assert!(lconfig.prop_is_overridden(SettingKind::Direction, GPIO)); + assert_eq!( + lconfig.prop_offset(SettingKind::Direction, GPIO).unwrap(), + Setting::Direction(Direction::Input) + ); + + let mut map = SettingKindMap::new(); + map.insert(GPIO.into(), SettingKind::Direction); + lconfig.clear_prop_override(map); + assert!(!lconfig.prop_is_overridden(SettingKind::Direction, GPIO)); + assert_eq!( + lconfig.prop_offset(SettingKind::Direction, GPIO).unwrap(), + Setting::Direction(Direction::AsIs) + ); + } + + #[test] + fn edge_detection() { + const GPIO: Offset = 1; + let mut lconfig = line::Config::new().unwrap(); + + lconfig.set_prop_default(&[Setting::EdgeDetection(None)]); + + let mut map = SettingMap::new(); + map.insert(GPIO.into(), Setting::EdgeDetection(Some(Edge::Both))); + lconfig.set_prop_override(map); + + assert!(lconfig.prop_is_overridden(SettingKind::EdgeDetection, GPIO)); + assert_eq!( + lconfig + .prop_offset(SettingKind::EdgeDetection, GPIO) + .unwrap(), + Setting::EdgeDetection(Some(Edge::Both)) + ); + + let mut map = SettingKindMap::new(); + map.insert(GPIO.into(), SettingKind::EdgeDetection); + lconfig.clear_prop_override(map); + assert!(!lconfig.prop_is_overridden(SettingKind::EdgeDetection, GPIO)); + assert_eq!( + lconfig + .prop_offset(SettingKind::EdgeDetection, GPIO) + .unwrap(), + Setting::EdgeDetection(None) + ); + } + + #[test] + fn bias() { + const GPIO: Offset = 2; + let mut lconfig = line::Config::new().unwrap(); + + lconfig.set_prop_default(&[Setting::Bias(None)]); + + let mut map = SettingMap::new(); + map.insert(GPIO.into(), Setting::Bias(Some(Bias::PullDown))); + lconfig.set_prop_override(map); + + assert!(lconfig.prop_is_overridden(SettingKind::Bias, GPIO)); + assert_eq!( + lconfig.prop_offset(SettingKind::Bias, GPIO).unwrap(), + Setting::Bias(Some(Bias::PullDown)) + ); + + let mut map = SettingKindMap::new(); + map.insert(GPIO.into(), SettingKind::Bias); + lconfig.clear_prop_override(map); + assert!(!lconfig.prop_is_overridden(SettingKind::Bias, GPIO)); + assert_eq!( + lconfig.prop_offset(SettingKind::Bias, GPIO).unwrap(), + Setting::Bias(None) + ); + } + + #[test] + fn drive() { + const GPIO: Offset = 3; + let mut lconfig = line::Config::new().unwrap(); + + lconfig.set_prop_default(&[Setting::Drive(Drive::PushPull)]); + + let mut map = SettingMap::new(); + map.insert(GPIO.into(), Setting::Drive(Drive::OpenDrain)); + lconfig.set_prop_override(map); + + assert!(lconfig.prop_is_overridden(SettingKind::Drive, GPIO)); + assert_eq!( + lconfig.prop_offset(SettingKind::Drive, GPIO).unwrap(), + Setting::Drive(Drive::OpenDrain) + ); + + let mut map = SettingKindMap::new(); + map.insert(GPIO.into(), SettingKind::Drive); + lconfig.clear_prop_override(map); + assert!(!lconfig.prop_is_overridden(SettingKind::Drive, GPIO)); + assert_eq!( + lconfig.prop_offset(SettingKind::Drive, GPIO).unwrap(), + Setting::Drive(Drive::PushPull) + ); + } + + #[test] + fn active_low() { + const GPIO: Offset = 4; + let mut lconfig = line::Config::new().unwrap(); + + lconfig.set_prop_default(&[Setting::ActiveLow(false)]); + + let mut map = SettingMap::new(); + map.insert(GPIO.into(), Setting::ActiveLow(true)); + lconfig.set_prop_override(map); + + assert!(lconfig.prop_is_overridden(SettingKind::ActiveLow, GPIO)); + assert_eq!( + lconfig.prop_offset(SettingKind::ActiveLow, GPIO).unwrap(), + Setting::ActiveLow(true) + ); + + let mut map = SettingKindMap::new(); + map.insert(GPIO.into(), SettingKind::ActiveLow); + lconfig.clear_prop_override(map); + assert!(!lconfig.prop_is_overridden(SettingKind::ActiveLow, GPIO)); + assert_eq!( + lconfig.prop_offset(SettingKind::ActiveLow, GPIO).unwrap(), + Setting::ActiveLow(false) + ); + } + + #[test] + fn debounce_period() { + const GPIO: Offset = 5; + let mut lconfig = line::Config::new().unwrap(); + + lconfig.set_prop_default(&[Setting::DebouncePeriod(Duration::from_millis(5))]); + + let mut map = SettingMap::new(); + map.insert( + GPIO.into(), + Setting::DebouncePeriod(Duration::from_millis(3)), + ); + lconfig.set_prop_override(map); + + assert!(lconfig.prop_is_overridden(SettingKind::DebouncePeriod, GPIO)); + assert_eq!( + lconfig + .prop_offset(SettingKind::DebouncePeriod, GPIO) + .unwrap(), + Setting::DebouncePeriod(Duration::from_millis(3)) + ); + + let mut map = SettingKindMap::new(); + map.insert(GPIO.into(), SettingKind::DebouncePeriod); + lconfig.clear_prop_override(map); + assert!(!lconfig.prop_is_overridden(SettingKind::DebouncePeriod, GPIO)); + assert_eq!( + lconfig + .prop_offset(SettingKind::DebouncePeriod, GPIO) + .unwrap(), + Setting::DebouncePeriod(Duration::from_millis(5)) + ); + } + + #[test] + fn event_clock() { + const GPIO: Offset = 6; + let mut lconfig = line::Config::new().unwrap(); + + lconfig.set_prop_default(&[Setting::EventClock(EventClock::Monotonic)]); + + let mut map = SettingMap::new(); + map.insert(GPIO.into(), Setting::EventClock(EventClock::Realtime)); + lconfig.set_prop_override(map); + + assert!(lconfig.prop_is_overridden(SettingKind::EventClock, GPIO)); + assert_eq!( + lconfig.prop_offset(SettingKind::EventClock, GPIO).unwrap(), + Setting::EventClock(EventClock::Realtime) + ); + + let mut map = SettingKindMap::new(); + map.insert(GPIO.into(), SettingKind::EventClock); + lconfig.clear_prop_override(map); + assert!(!lconfig.prop_is_overridden(SettingKind::EventClock, GPIO)); + assert_eq!( + lconfig.prop_offset(SettingKind::EventClock, GPIO).unwrap(), + Setting::EventClock(EventClock::Monotonic) + ); + } + + #[test] + fn output_value() { + const GPIO: Offset = 0; + let mut lconfig = line::Config::new().unwrap(); + + lconfig.set_prop_default(&[Setting::OutputValue(Value::InActive)]); + + let mut map = SettingMap::new(); + map.insert(GPIO.into(), Setting::OutputValue(Value::Active)); + lconfig.set_prop_override(map); + + let mut map = ValueMap::new(); + map.insert(1, Value::Active); + map.insert(2, Value::Active); + map.insert(8, Value::Active); + lconfig.set_output_values(map).unwrap(); + + for line in [0, 1, 2, 8] { + assert!(lconfig.prop_is_overridden(SettingKind::OutputValue, line)); + assert_eq!( + lconfig.prop_offset(SettingKind::OutputValue, line).unwrap(), + Setting::OutputValue(Value::Active) + ); + + let mut map = SettingKindMap::new(); + map.insert(line.into(), SettingKind::OutputValue); + lconfig.clear_prop_override(map); + assert!(!lconfig.prop_is_overridden(SettingKind::OutputValue, line)); + assert_eq!( + lconfig.prop_offset(SettingKind::OutputValue, GPIO).unwrap(), + Setting::OutputValue(Value::InActive) + ); + } + } + } +} diff --git a/bindings/rust/libgpiod/tests/line_info.rs b/bindings/rust/libgpiod/tests/line_info.rs new file mode 100644 index 000000000000..54a8a1133e89 --- /dev/null +++ b/bindings/rust/libgpiod/tests/line_info.rs @@ -0,0 +1,272 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +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.rconfig_set_offsets(&[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.rconfig_set_offsets(&[0]); + config.lconfig_bias(Direction::Input, Some(Bias::PullUp)); + 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.rconfig_set_offsets(&[0]); + config.lconfig_bias(Direction::Input, Some(Bias::PullDown)); + 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.rconfig_set_offsets(&[0]); + config.lconfig_bias(Direction::Input, Some(Bias::Disabled)); + 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.rconfig_set_offsets(&[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.rconfig_set_offsets(&[0]); + config.lconfig_drive(Direction::Input, Drive::PushPull); + 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.rconfig_set_offsets(&[0]); + config.lconfig_drive(Direction::Output, Drive::OpenDrain); + 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.rconfig_set_offsets(&[0]); + config.lconfig_drive(Direction::Output, Drive::OpenSource); + 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.rconfig_set_offsets(&[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.rconfig_set_offsets(&[0]); + config.lconfig_edge(Some(Direction::Input), Some(Edge::Both)); + 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.rconfig_set_offsets(&[0]); + config.lconfig_edge(Some(Direction::Input), Some(Edge::Rising)); + 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.rconfig_set_offsets(&[0]); + config.lconfig_edge(Some(Direction::Input), Some(Edge::Falling)); + 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.rconfig_set_offsets(&[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.rconfig_set_offsets(&[0]); + config.lconfig_clock(EventClock::Monotonic); + 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.rconfig_set_offsets(&[0]); + config.lconfig_clock(EventClock::Realtime); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.event_clock().unwrap(), EventClock::Realtime); + } + + #[test] + fn debounce() { + let mut config = TestConfig::new(NGPIO).unwrap(); + config.rconfig_set_offsets(&[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.rconfig_set_offsets(&[0]); + config.lconfig_debounce(Duration::from_millis(100)); + 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..bcbc4fb5b711 --- /dev/null +++ b/bindings/rust/libgpiod/tests/line_request.rs @@ -0,0 +1,473 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +mod common; + +mod line_request { + use libc::{E2BIG, EBUSY, 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, + Setting, SettingMap, Value, ValueMap, + }; + + const NGPIO: u64 = 8; + + mod invalid_arguments { + use super::*; + + #[test] + fn no_offsets() { + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_val_raw(); + + assert_eq!( + config.request_lines().unwrap_err(), + ChipError::OperationFailed(OperationType::LineRequest, Errno::new(EINVAL)) + ); + } + + #[test] + fn duplicate_offsets() { + let mut config = TestConfig::new(NGPIO).unwrap(); + config.rconfig_set_offsets(&[2, 0, 0, 4]); + config.lconfig_val_raw(); + + assert_eq!( + config.request_lines().unwrap_err(), + ChipError::OperationFailed(OperationType::LineRequest, Errno::new(EBUSY)) + ); + } + + #[test] + fn out_of_bound_offsets() { + let mut config = TestConfig::new(NGPIO).unwrap(); + config.rconfig_set_offsets(&[2, 0, 8, 4]); + config.lconfig_val_raw(); + + 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.rconfig_set_offsets(&[0]); + config.lconfig_edge(Some(Direction::Output), Some(Edge::Both)); + + 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_offsets(&[GPIO]); + config.rconfig_set_consumer(CONSUMER); + config.lconfig_val_raw(); + 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.rconfig_set_offsets(&[GPIO]); + config.lconfig_val_raw(); + 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.rconfig_set_offsets(&offsets); + config.lconfig_val(Some(Direction::Input), None, None); + 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 lconfig = line::Config::new().unwrap(); + lconfig.set_prop_default(&[Setting::ActiveLow(true)]); + 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.rconfig_set_offsets(&offsets); + config.lconfig_val( + Some(Direction::Output), + Some(Value::Active), + Some((4, Value::InActive)), + ); + config.request_lines().unwrap(); + + assert_eq!(config.sim().val(0).unwrap(), GPIOSIM_VALUE_ACTIVE); + assert_eq!(config.sim().val(1).unwrap(), GPIOSIM_VALUE_ACTIVE); + assert_eq!(config.sim().val(3).unwrap(), GPIOSIM_VALUE_ACTIVE); + + // Overriden + assert_eq!(config.sim().val(4).unwrap(), GPIOSIM_VALUE_INACTIVE); + + // Default + assert_eq!(config.sim().val(2).unwrap(), GPIOSIM_VALUE_INACTIVE); + } + + #[test] + fn update_output_values() { + let offsets = [0, 1, 3, 4]; + let mut config = TestConfig::new(NGPIO).unwrap(); + config.rconfig_set_offsets(&offsets); + config.lconfig_val(Some(Direction::Output), Some(Value::InActive), None); + 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.rconfig_set_offsets(&offsets); + config.lconfig_bias(Direction::Input, Some(Bias::PullUp)); + 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.rconfig_set_offsets(&[0]); + config.lconfig_edge(None, Some(Edge::Both)); + 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 mut config = TestConfig::new(16).unwrap(); + config.rconfig_set_offsets(&[0]); + config.request_lines().unwrap(); + + let request = config.request(); + + // Reconfigure + let mut lconfig = line::Config::new().unwrap(); + lconfig.set_prop_default(&[Setting::Direction(Direction::Input)]); + + let mut map = SettingMap::new(); + + // The uAPI config has only 10 attribute slots, this should pass. + for offset in 0..10 { + map.insert( + offset, + Setting::DebouncePeriod(Duration::from_millis(100 + offset)), + ); + } + + lconfig.set_prop_override(map.clone()); + 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. + map.insert(11, Setting::DebouncePeriod(Duration::from_millis(100 + 11))); + lconfig.set_prop_override(map.clone()); + 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.rconfig_set_offsets(&[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 mut lconfig = line::Config::new().unwrap(); + lconfig.set_prop_default(&[ + Setting::Direction(Direction::Input), + Setting::Bias(Some(Bias::PullUp)), + ]); + request.reconfigure_lines(&lconfig).unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.bias().unwrap(), Some(Bias::PullUp)); + + let mut lconfig = line::Config::new().unwrap(); + lconfig.set_prop_default(&[ + Setting::Direction(Direction::Input), + Setting::Bias(Some(Bias::PullDown)), + ]); + request.reconfigure_lines(&lconfig).unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.bias().unwrap(), Some(Bias::PullDown)); + + let mut lconfig = line::Config::new().unwrap(); + lconfig.set_prop_default(&[ + Setting::Direction(Direction::Input), + Setting::Bias(Some(Bias::Disabled)), + ]); + 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.rconfig_set_offsets(&[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 mut lconfig = line::Config::new().unwrap(); + lconfig.set_prop_default(&[ + Setting::Direction(Direction::Input), + Setting::Drive(Drive::PushPull), + ]); + request.reconfigure_lines(&lconfig).unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.drive().unwrap(), Drive::PushPull); + + let mut lconfig = line::Config::new().unwrap(); + lconfig.set_prop_default(&[ + Setting::Direction(Direction::Output), + Setting::Drive(Drive::OpenDrain), + ]); + request.reconfigure_lines(&lconfig).unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.drive().unwrap(), Drive::OpenDrain); + + let mut lconfig = line::Config::new().unwrap(); + lconfig.set_prop_default(&[ + Setting::Direction(Direction::Output), + Setting::Drive(Drive::OpenSource), + ]); + 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.rconfig_set_offsets(&[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 mut lconfig = line::Config::new().unwrap(); + lconfig.set_prop_default(&[ + Setting::Direction(Direction::Input), + Setting::EdgeDetection(Some(Edge::Both)), + ]); + request.reconfigure_lines(&lconfig).unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.edge_detection().unwrap(), Some(Edge::Both)); + + let mut lconfig = line::Config::new().unwrap(); + lconfig.set_prop_default(&[ + Setting::Direction(Direction::Input), + Setting::EdgeDetection(Some(Edge::Rising)), + ]); + request.reconfigure_lines(&lconfig).unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.edge_detection().unwrap(), Some(Edge::Rising)); + + let mut lconfig = line::Config::new().unwrap(); + lconfig.set_prop_default(&[ + Setting::Direction(Direction::Input), + Setting::EdgeDetection(Some(Edge::Falling)), + ]); + 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.rconfig_set_offsets(&[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 mut lconfig = line::Config::new().unwrap(); + lconfig.set_prop_default(&[Setting::EventClock(EventClock::Monotonic)]); + request.reconfigure_lines(&lconfig).unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.event_clock().unwrap(), EventClock::Monotonic); + + let mut lconfig = line::Config::new().unwrap(); + lconfig.set_prop_default(&[Setting::EventClock(EventClock::Realtime)]); + request.reconfigure_lines(&lconfig).unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.event_clock().unwrap(), EventClock::Realtime); + } + + #[test] + fn debounce() { + let mut config = TestConfig::new(NGPIO).unwrap(); + config.rconfig_set_offsets(&[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 mut lconfig = line::Config::new().unwrap(); + lconfig.set_prop_default(&[ + Setting::Direction(Direction::Input), + Setting::DebouncePeriod(Duration::from_millis(100)), + ]); + 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/request_config.rs b/bindings/rust/libgpiod/tests/request_config.rs new file mode 100644 index 000000000000..1d2f9468ca4c --- /dev/null +++ b/bindings/rust/libgpiod/tests/request_config.rs @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +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.offsets().len(), 0); + assert_eq!(rconfig.event_buffer_size(), 0); + assert_eq!( + rconfig.consumer().unwrap_err(), + ChipError::OperationFailed(OperationType::RequestConfigGetConsumer, Errno::new(0)) + ); + } + + #[test] + fn initialized() { + let offsets = [0, 1, 2, 3]; + const CONSUMER: &str = "foobar"; + let rconfig = request::Config::new().unwrap(); + rconfig.set_consumer(CONSUMER); + rconfig.set_offsets(&offsets); + rconfig.set_event_buffer_size(64); + + assert_eq!(rconfig.offsets(), offsets); + assert_eq!(rconfig.event_buffer_size(), 64); + assert_eq!(rconfig.consumer().unwrap(), CONSUMER); + } + } +} From patchwork Tue Aug 2 12:18:12 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 595940 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7322FC00140 for ; Tue, 2 Aug 2022 12:18:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236934AbiHBMS5 (ORCPT ); Tue, 2 Aug 2022 08:18:57 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:39066 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236944AbiHBMSm (ORCPT ); Tue, 2 Aug 2022 08:18:42 -0400 Received: from mail-pj1-x102c.google.com (mail-pj1-x102c.google.com [IPv6:2607:f8b0:4864:20::102c]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4EDE650044 for ; Tue, 2 Aug 2022 05:18:41 -0700 (PDT) Received: by mail-pj1-x102c.google.com with SMTP id pm17so7871382pjb.3 for ; Tue, 02 Aug 2022 05:18:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=vqIR+uSXHOhVHHUZbf27VLh2KEaBPzoLiCQTSRtyybk=; b=o/uNDjJ++OQSbAVb00gYbagJMQkBfXUpSpLiK5yn+ew3+IaTrf2wY2NkfS3nWRM32R JNXj5EXD79nfs1VyF2Q1NjSfeZB0nzMJxiVoamphDo+GhqQeflbBQBXcGvoSdDWKTKki tSnnFyq/YLL42o+nCkklryFXhCe0rxzGrVLZ1Ie3aPDbJ40ST5uvSfNjsQmtQh/5svT+ qrjYnnPD9EZfnQwArczhwDjNXUWMcXWdu019A5tiKHGuCxemtOmLTHg6gKkfyvfp7FVh FJwpAZS8OwXk6ljsdRZ44wFvOKNHmdj6TB01tmWBGKN/9Oo4yd2Ap2dgc0zDat8npqCk tmLA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=vqIR+uSXHOhVHHUZbf27VLh2KEaBPzoLiCQTSRtyybk=; b=4ctMQ2Ack9MhYUDTskIY5plKnE4xNPEWmv1cpSpYzSiYJcmhSLn/BfWz/2bqfwGK/N HHCGs7yuXEhdAyP3cCwjJKfDoqX+Mk/u7y0zEVVmgBuXqDqEEDAHuZ9bgVE28M3P2/rJ 9xd1mPV2t+8GKIejl2UaB/rXTh5wNeBAe2o8NWi4m+clCOh+sFizVBrG42QsDDzasqU+ 17yMLLDr6IHDwHUrswBdAf7a8QONYDxRO6C2ai3ZbJxgATfiDCmoV8okBBsThOO0izJL hdW5BrE66qnOzDF3gN/XXCnGv2ni4zEKsBytSayDrUo0YO4/X/WozJDw1u5IOfCSHPhs rl1Q== X-Gm-Message-State: ACgBeo0+QQodCqfrP9Kk4O6WhxLewWZQqmxMGkbT4+kop0b88mR0Pcqn Db/r+rLVg4eFR4mbNFTCRx5E8Q== X-Google-Smtp-Source: AA6agR4cF1YIEErC84y+PcQSDZHFWOYdlKH3NUehwv28fd2rjRiI6b8cQMbwG/JSg1+fpEMqVkU1bg== X-Received: by 2002:a17:903:2406:b0:16d:6b7a:57c6 with SMTP id e6-20020a170903240600b0016d6b7a57c6mr21468141plo.149.1659442720739; Tue, 02 Aug 2022 05:18:40 -0700 (PDT) Received: from localhost ([122.171.18.80]) by smtp.gmail.com with ESMTPSA id j10-20020a17090276ca00b0016d5cf36ff4sm11481125plt.289.2022.08.02.05.18.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 02 Aug 2022 05:18:40 -0700 (PDT) From: Viresh Kumar To: Linus Walleij , Bartosz Golaszewski , Kent Gibson Cc: Viresh Kumar , Vincent Guittot , linux-gpio@vger.kernel.org, Miguel Ojeda , Wedson Almeida Filho , =?utf-8?q?Alex_Benn=C3=A9e?= , stratos-dev@op-lists.linaro.org, Gerard Ryan Subject: [PATCH V5 8/8] libgpiod: Integrate building of rust bindings with make Date: Tue, 2 Aug 2022 17:48:12 +0530 Message-Id: <59f2e34c3bfabbb3816e54b350a6927544f46652.1659442066.git.viresh.kumar@linaro.org> X-Mailer: git-send-email 2.31.1.272.g89b43f80a514 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org Lets make build rust bindings as well. Signed-off-by: Viresh Kumar --- 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 + +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 ab03673589e9..8458f734a606 100644 --- a/configure.ac +++ b/configure.ac @@ -211,6 +211,21 @@ then [AC_SUBST(PYTHON_LIBS, [`$PYTHON-config --libs`])]) fi +AC_ARG_ENABLE([bindings-rust], + [AS_HELP_STRING([--enable-bindings-rust],[enable rust bindings [default=no]])], + [if test "x$enableval" = xyes; then with_bindings_rust=true; fi], + [with_bindings_rust=false]) +AM_CONDITIONAL([WITH_BINDINGS_RUST], [test "x$with_bindings_rust" = xtrue]) + +if test "x$with_bindings_rust" = xtrue +then + AC_CHECK_PROG([has_cargo], [cargo], [true], [false]) + if test "x$has_cargo" = xfalse + then + AC_MSG_ERROR([cargo not found - needed for rust bindings]) + fi +fi + AC_CHECK_PROG([has_doxygen], [doxygen], [true], [false]) AM_CONDITIONAL([HAS_DOXYGEN], [test "x$has_doxygen" = xtrue]) if test "x$has_doxygen" = xfalse @@ -245,6 +260,7 @@ AC_CONFIG_FILES([Makefile bindings/python/Makefile bindings/python/examples/Makefile bindings/python/tests/Makefile + bindings/rust/Makefile man/Makefile]) AC_OUTPUT