From patchwork Fri Jul 8 11:34:54 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 588764 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 54079C433EF for ; Fri, 8 Jul 2022 11:35:12 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237622AbiGHLfL (ORCPT ); Fri, 8 Jul 2022 07:35:11 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33472 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237345AbiGHLfJ (ORCPT ); Fri, 8 Jul 2022 07:35:09 -0400 Received: from mail-pg1-x52c.google.com (mail-pg1-x52c.google.com [IPv6:2607:f8b0:4864:20::52c]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9E07D88F26 for ; Fri, 8 Jul 2022 04:35:08 -0700 (PDT) Received: by mail-pg1-x52c.google.com with SMTP id 73so6720867pgb.10 for ; Fri, 08 Jul 2022 04:35:08 -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=rbSHZnDrI1cUI+LHg8aFjHKSoTAP5G9hjPbqfKkCd3M=; b=wklQlmjMzzjJMzHov8N+18Wm7CHsTBgcpmBF6L2Y4IRr96ii/FKaVURT9oFIsSluMR fc0BZRm0X1+1my/P91M0SlYIqkzYGM1ZXyf/ywK9N09aKfbmTuU0ElxFoooWpIWXzCjs BPr8k2plqfbMxYFTBF4xerG3XLCb+kr7MsnaXX1pQpjcctWQJYKNKBzJKmkrJxCIB8Wp 6PifjT54DtlJHtz2rJWKBG3x60xyf00ZBztjgeq5nbDeQNFCvpiVXb48Czx8pXkHtpKZ KSCPv58mxG6uhG6rglvLXR4MMnnKCqpYlBi6+jH87bYgEaUVhtCBlLMff/LcIs3KJz3u CvEg== 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=rbSHZnDrI1cUI+LHg8aFjHKSoTAP5G9hjPbqfKkCd3M=; b=54wM5f/3IbL3qS78vGL+Yf7r1HndNuhPNoNRaNJCzMyCgK0PHJ6HrJgmoNEfTpEIXn nA1o5QvLyDNsyk4lcEZ3sYgNzfn7yb/qYm9QDVxy6+l7jMb3fwtIh6pUollU4hSULh0z vb+5BobWqpSCFA9yTTbaUK3fDsGBcqHEIz4S/BsYdjSF9PwkbZ9AI2tcochl8+u4wHfQ ebcTlNidvuiEMyv/DrHwuDbs3Wd0phQVLe7M1UH9DIuP3mNft3Ql+fUM0W9HpljjjXJt nsk3l3lxrCBs4xvcFpkT0kxTT4odPPNTlmzG1eDSzXAnpO8/IiLVUPF4VvfoOulLDqRg e0og== X-Gm-Message-State: AJIora/ud4fgIlBr7AQ4onoSZVudXh2a9w5/I0lyY/UxQhdSvdYEUbIs Efdjf0ZkjrhzPYXj33TpUoOdkA== X-Google-Smtp-Source: AGRyM1u5H+8Br87bG7zA2/eyQtN25IehkwfmJa7IGFRfEVtgCY/Ya+aT8tsVel023AWadQ+NORaPLA== X-Received: by 2002:a63:4c57:0:b0:40d:d28e:bfb1 with SMTP id m23-20020a634c57000000b0040dd28ebfb1mr3087209pgl.77.1657280108011; Fri, 08 Jul 2022 04:35:08 -0700 (PDT) Received: from localhost ([122.171.18.80]) by smtp.gmail.com with ESMTPSA id y144-20020a626496000000b0052aaf7fe731sm1461830pfb.45.2022.07.08.04.35.07 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 08 Jul 2022 04:35:07 -0700 (PDT) From: Viresh Kumar To: Linus Walleij , Bartosz Golaszewski Cc: Viresh Kumar , Vincent Guittot , linux-gpio@vger.kernel.org, Kent Gibson , Miguel Ojeda , Wedson Almeida Filho , =?utf-8?q?Alex_Benn=C3=A9e?= , stratos-dev@op-lists.linaro.org, Gerard Ryan Subject: [PATCH V4 1/8] libgpiod: Add libgpiod-sys rust crate Date: Fri, 8 Jul 2022 17:04:54 +0530 Message-Id: <44ee8c36d58049de2f653494e16cba04b198fb35.1657279685.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/libgpiod-sys/Cargo.toml | 15 ++++++ bindings/rust/libgpiod-sys/build.rs | 69 +++++++++++++++++++++++++++ bindings/rust/libgpiod-sys/src/lib.rs | 20 ++++++++ bindings/rust/libgpiod-sys/wrapper.h | 2 + 5 files changed, 111 insertions(+) create mode 100644 bindings/rust/libgpiod-sys/Cargo.toml create mode 100644 bindings/rust/libgpiod-sys/build.rs create mode 100644 bindings/rust/libgpiod-sys/src/lib.rs create mode 100644 bindings/rust/libgpiod-sys/wrapper.h 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/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..bbcd30f79d23 --- /dev/null +++ b/bindings/rust/libgpiod-sys/build.rs @@ -0,0 +1,69 @@ +#[cfg(feature = "generate")] +extern crate bindgen; +#[cfg(feature = "generate")] +use std::env; +#[cfg(feature = "generate")] +use std::path::PathBuf; + +#[cfg(feature = "generate")] +fn generate_bindings(files: &Vec<&str>) { + // Tell cargo to invalidate the built crate whenever following files change + println!("cargo:rerun-if-changed=wrapper.h"); + + for file in files { + println!("cargo:rerun-if-changed={}", file); + } + + // The bindgen::Builder is the main entry point + // to bindgen, and lets you build up options for + // the resulting bindings. + let bindings = bindgen::Builder::default() + // The input header we would like to generate + // bindings for. + .header("wrapper.h") + // Tell cargo to invalidate the built crate whenever any of the + // included header files changed. + .parse_callbacks(Box::new(bindgen::CargoCallbacks)) + // Finish the builder and generate the bindings. + .generate() + // Unwrap the Result and panic on failure. + .expect("Unable to generate bindings"); + + // Write the bindings to the $OUT_DIR/bindings.rs file. + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Couldn't write bindings!"); +} + +fn build_gpiod(files: Vec<&str>) { + // Tell Cargo that if the given file changes, to rerun this build script. + println!("cargo:rerun-if-changed=../../../lib/"); + + // Use the `cc` crate to build a C file and statically link it. + cc::Build::new() + .files(files) + .define("_GNU_SOURCE", None) + .define("GPIOD_VERSION_STR", "\"libgpio-sys\"") + .include("../../../include") + .compile("gpiod"); +} + +fn main() { + let files = vec![ + "../../../lib/chip.c", + "../../../lib/chip-info.c", + "../../../lib/edge-event.c", + "../../../lib/info-event.c", + "../../../lib/internal.c", + "../../../lib/line-config.c", + "../../../lib/line-info.c", + "../../../lib/line-request.c", + "../../../lib/misc.c", + "../../../lib/request-config.c", + ]; + + #[cfg(feature = "generate")] + generate_bindings(&files); + build_gpiod(files); +} diff --git a/bindings/rust/libgpiod-sys/src/lib.rs b/bindings/rust/libgpiod-sys/src/lib.rs new file mode 100644 index 000000000000..3384863a567c --- /dev/null +++ b/bindings/rust/libgpiod-sys/src/lib.rs @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0 + +#[allow( + clippy::all, + deref_nullptr, + dead_code, + non_camel_case_types, + non_upper_case_globals, + non_snake_case, + improper_ctypes +)] + +mod bindings_raw { + #[cfg(feature = "generate")] + include!(concat!(env!("OUT_DIR"), "/bindings.rs")); + + #[cfg(not(feature = "generate"))] + include!("bindings.rs"); +} +pub use bindings_raw::*; diff --git a/bindings/rust/libgpiod-sys/wrapper.h b/bindings/rust/libgpiod-sys/wrapper.h new file mode 100644 index 000000000000..7bc1158b7d90 --- /dev/null +++ b/bindings/rust/libgpiod-sys/wrapper.h @@ -0,0 +1,2 @@ +#include +#include "../../../include/gpiod.h" From patchwork Fri Jul 8 11:34:55 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 589022 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 D6F1DC43334 for ; Fri, 8 Jul 2022 11:35:17 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237623AbiGHLfR (ORCPT ); Fri, 8 Jul 2022 07:35:17 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33528 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237365AbiGHLfQ (ORCPT ); Fri, 8 Jul 2022 07:35:16 -0400 Received: from mail-pl1-x634.google.com (mail-pl1-x634.google.com [IPv6:2607:f8b0:4864:20::634]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 11AC0205C4 for ; Fri, 8 Jul 2022 04:35:13 -0700 (PDT) Received: by mail-pl1-x634.google.com with SMTP id b2so16119323plx.7 for ; Fri, 08 Jul 2022 04:35:13 -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=dd2O0OExS5fRsT2jsHbiKzSyJYyD2hWr3cZdBvl4sP8=; b=OiI7Bh7/TXrbBAkjigwkL0mx+9m3O50Mub9DXoVgBmwWoqyMODwJS2OrUr2FW00JqZ Po/zjmU/80NWf5g5DmfPXc0jfPX+892Dh6BQ5wkGA+adrtY/jWSQ1GP2YO33wd9Cndhn KBv1g/10b2zOFbevkuzeubeI513izc0I/opr8z7mxzgyUjSgjfKTb0cFeGwQcT3hqEgX zdSDPNvZpkhBU/GcCUeerpjEtQTNCKTi/JEyfWGrbUm/2mpWKOUbtjoo+XZKxkCWshmx XTkglcDcF+VFA++ysfBur9dVa2XeRi7MDzFLApQpbSTz20zk26qbgdKPgLQV9mqmmgDc 3mMQ== 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=dd2O0OExS5fRsT2jsHbiKzSyJYyD2hWr3cZdBvl4sP8=; b=qtVbPtaJBTIEf/DIxTWiuQ15kC5MOtJJLxgp6w7SdF0NPejaMSkCbuet2bgwpmh1Kp +xFT06BOHcyi2mBctUbUolfnEvoe4GXOIDqxMBhFlcRBxD+7x9YE9BPbOh9CwOLv9bTj HmWfW4PFBTXYoYQJd6zOsnojv7XwwdIW0lNvmhDtEB3+gshS9kLv65zfhmkuSxwyCKU+ 0FMRh1Zw4rM7EtiJbl4wXfnYGOQwlNh1OkB8XFFoaYzOJhkj03BXfxFnvSUWUousUkqh HIEO7RAf4KSo8RpAL2gtqCdqMm1Y+EdeqbJLnh0FaWwX/SjWkj1/yEC+7wYtGa31IjLs 2PAw== X-Gm-Message-State: AJIora+PcPtEnzUU2A6FSIPmPDQSxjXp6nwdSAqQuZL2VJxLBBaFTfpW oTOWFMgct0VFDYYypu4hcThEWQ== X-Google-Smtp-Source: AGRyM1sfPWNBTr/xrEdOSzmzJzrMs3rEUQ6AAw/TWPoR4KzaY/p/ArCYh8rl5/2MHNcv3B+dE4MTuQ== X-Received: by 2002:a17:902:bb8f:b0:16a:80e7:e5d9 with SMTP id m15-20020a170902bb8f00b0016a80e7e5d9mr3350420pls.25.1657280110861; Fri, 08 Jul 2022 04:35:10 -0700 (PDT) Received: from localhost ([122.171.18.80]) by smtp.gmail.com with ESMTPSA id gg17-20020a17090b0a1100b001ecbd9aa1a7sm1362396pjb.1.2022.07.08.04.35.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 08 Jul 2022 04:35:10 -0700 (PDT) From: Viresh Kumar To: Linus Walleij , Bartosz Golaszewski Cc: Viresh Kumar , Vincent Guittot , linux-gpio@vger.kernel.org, Kent Gibson , Miguel Ojeda , Wedson Almeida Filho , =?utf-8?q?Alex_Benn=C3=A9e?= , stratos-dev@op-lists.linaro.org, Gerard Ryan Subject: [PATCH V4 2/8] libgpiod: Add pre generated rust bindings Date: Fri, 8 Jul 2022 17:04:55 +0530 Message-Id: <07889ab056a7c69d30569fdd4b035691dd2d6248.1657279685.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/libgpiod-sys/README.md | 10 + bindings/rust/libgpiod-sys/src/bindings.rs | 1920 ++++++++++++++++++++ 2 files changed, 1930 insertions(+) create mode 100644 bindings/rust/libgpiod-sys/README.md create mode 100644 bindings/rust/libgpiod-sys/src/bindings.rs diff --git a/bindings/rust/libgpiod-sys/README.md b/bindings/rust/libgpiod-sys/README.md new file mode 100644 index 000000000000..ea037d6d7803 --- /dev/null +++ b/bindings/rust/libgpiod-sys/README.md @@ -0,0 +1,10 @@ +# Generated libgpiod-sys Rust FFI bindings +Automatically generated Rust FFI bindings via + [bindgen](https://github.com/rust-lang/rust-bindgen). + +## Updating bindings +1. Clone the source from + +2. run `cd libgpiod/bindings/rust/libgpiod-sys/` +2. run `cargo build --features generate` +3. 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..930eb894f66f --- /dev/null +++ b/bindings/rust/libgpiod-sys/src/bindings.rs @@ -0,0 +1,1920 @@ +/* automatically generated by rust-bindgen 0.59.2 */ + +pub const _STRING_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_LOCALE_T_H: u32 = 1; +pub const _BITS_TYPES___LOCALE_T_H: u32 = 1; +pub const _STRINGS_H: u32 = 1; +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 _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; +extern "C" { + pub fn memcpy( + __dest: *mut ::std::os::raw::c_void, + __src: *const ::std::os::raw::c_void, + __n: ::std::os::raw::c_ulong, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn memmove( + __dest: *mut ::std::os::raw::c_void, + __src: *const ::std::os::raw::c_void, + __n: ::std::os::raw::c_ulong, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn memccpy( + __dest: *mut ::std::os::raw::c_void, + __src: *const ::std::os::raw::c_void, + __c: ::std::os::raw::c_int, + __n: ::std::os::raw::c_ulong, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn memset( + __s: *mut ::std::os::raw::c_void, + __c: ::std::os::raw::c_int, + __n: ::std::os::raw::c_ulong, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn memcmp( + __s1: *const ::std::os::raw::c_void, + __s2: *const ::std::os::raw::c_void, + __n: ::std::os::raw::c_ulong, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn memchr( + __s: *const ::std::os::raw::c_void, + __c: ::std::os::raw::c_int, + __n: ::std::os::raw::c_ulong, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn strcpy( + __dest: *mut ::std::os::raw::c_char, + __src: *const ::std::os::raw::c_char, + ) -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn strncpy( + __dest: *mut ::std::os::raw::c_char, + __src: *const ::std::os::raw::c_char, + __n: ::std::os::raw::c_ulong, + ) -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn strcat( + __dest: *mut ::std::os::raw::c_char, + __src: *const ::std::os::raw::c_char, + ) -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn strncat( + __dest: *mut ::std::os::raw::c_char, + __src: *const ::std::os::raw::c_char, + __n: ::std::os::raw::c_ulong, + ) -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn strcmp( + __s1: *const ::std::os::raw::c_char, + __s2: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn strncmp( + __s1: *const ::std::os::raw::c_char, + __s2: *const ::std::os::raw::c_char, + __n: ::std::os::raw::c_ulong, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn strcoll( + __s1: *const ::std::os::raw::c_char, + __s2: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn strxfrm( + __dest: *mut ::std::os::raw::c_char, + __src: *const ::std::os::raw::c_char, + __n: ::std::os::raw::c_ulong, + ) -> ::std::os::raw::c_ulong; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __locale_struct { + pub __locales: [*mut __locale_data; 13usize], + pub __ctype_b: *const ::std::os::raw::c_ushort, + pub __ctype_tolower: *const ::std::os::raw::c_int, + pub __ctype_toupper: *const ::std::os::raw::c_int, + pub __names: [*const ::std::os::raw::c_char; 13usize], +} +#[test] +fn bindgen_test_layout___locale_struct() { + assert_eq!( + ::std::mem::size_of::<__locale_struct>(), + 232usize, + concat!("Size of: ", stringify!(__locale_struct)) + ); + assert_eq!( + ::std::mem::align_of::<__locale_struct>(), + 8usize, + concat!("Alignment of ", stringify!(__locale_struct)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__locale_struct>())).__locales as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__locale_struct), + "::", + stringify!(__locales) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__locale_struct>())).__ctype_b as *const _ as usize }, + 104usize, + concat!( + "Offset of field: ", + stringify!(__locale_struct), + "::", + stringify!(__ctype_b) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__locale_struct>())).__ctype_tolower as *const _ as usize }, + 112usize, + concat!( + "Offset of field: ", + stringify!(__locale_struct), + "::", + stringify!(__ctype_tolower) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__locale_struct>())).__ctype_toupper as *const _ as usize }, + 120usize, + concat!( + "Offset of field: ", + stringify!(__locale_struct), + "::", + stringify!(__ctype_toupper) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__locale_struct>())).__names as *const _ as usize }, + 128usize, + concat!( + "Offset of field: ", + stringify!(__locale_struct), + "::", + stringify!(__names) + ) + ); +} +pub type __locale_t = *mut __locale_struct; +pub type locale_t = __locale_t; +extern "C" { + pub fn strcoll_l( + __s1: *const ::std::os::raw::c_char, + __s2: *const ::std::os::raw::c_char, + __l: locale_t, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn strxfrm_l( + __dest: *mut ::std::os::raw::c_char, + __src: *const ::std::os::raw::c_char, + __n: size_t, + __l: locale_t, + ) -> size_t; +} +extern "C" { + pub fn strdup(__s: *const ::std::os::raw::c_char) -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn strndup( + __string: *const ::std::os::raw::c_char, + __n: ::std::os::raw::c_ulong, + ) -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn strchr( + __s: *const ::std::os::raw::c_char, + __c: ::std::os::raw::c_int, + ) -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn strrchr( + __s: *const ::std::os::raw::c_char, + __c: ::std::os::raw::c_int, + ) -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn strcspn( + __s: *const ::std::os::raw::c_char, + __reject: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_ulong; +} +extern "C" { + pub fn strspn( + __s: *const ::std::os::raw::c_char, + __accept: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_ulong; +} +extern "C" { + pub fn strpbrk( + __s: *const ::std::os::raw::c_char, + __accept: *const ::std::os::raw::c_char, + ) -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn strstr( + __haystack: *const ::std::os::raw::c_char, + __needle: *const ::std::os::raw::c_char, + ) -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn strtok( + __s: *mut ::std::os::raw::c_char, + __delim: *const ::std::os::raw::c_char, + ) -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn __strtok_r( + __s: *mut ::std::os::raw::c_char, + __delim: *const ::std::os::raw::c_char, + __save_ptr: *mut *mut ::std::os::raw::c_char, + ) -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn strtok_r( + __s: *mut ::std::os::raw::c_char, + __delim: *const ::std::os::raw::c_char, + __save_ptr: *mut *mut ::std::os::raw::c_char, + ) -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn strlen(__s: *const ::std::os::raw::c_char) -> ::std::os::raw::c_ulong; +} +extern "C" { + pub fn strnlen(__string: *const ::std::os::raw::c_char, __maxlen: size_t) -> size_t; +} +extern "C" { + pub fn strerror(__errnum: ::std::os::raw::c_int) -> *mut ::std::os::raw::c_char; +} +extern "C" { + #[link_name = "\u{1}__xpg_strerror_r"] + pub fn strerror_r( + __errnum: ::std::os::raw::c_int, + __buf: *mut ::std::os::raw::c_char, + __buflen: size_t, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn strerror_l( + __errnum: ::std::os::raw::c_int, + __l: locale_t, + ) -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn bcmp( + __s1: *const ::std::os::raw::c_void, + __s2: *const ::std::os::raw::c_void, + __n: ::std::os::raw::c_ulong, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn bcopy( + __src: *const ::std::os::raw::c_void, + __dest: *mut ::std::os::raw::c_void, + __n: size_t, + ); +} +extern "C" { + pub fn bzero(__s: *mut ::std::os::raw::c_void, __n: ::std::os::raw::c_ulong); +} +extern "C" { + pub fn index( + __s: *const ::std::os::raw::c_char, + __c: ::std::os::raw::c_int, + ) -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn rindex( + __s: *const ::std::os::raw::c_char, + __c: ::std::os::raw::c_int, + ) -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn ffs(__i: ::std::os::raw::c_int) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn ffsl(__l: ::std::os::raw::c_long) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn ffsll(__ll: ::std::os::raw::c_longlong) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn strcasecmp( + __s1: *const ::std::os::raw::c_char, + __s2: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn strncasecmp( + __s1: *const ::std::os::raw::c_char, + __s2: *const ::std::os::raw::c_char, + __n: ::std::os::raw::c_ulong, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn strcasecmp_l( + __s1: *const ::std::os::raw::c_char, + __s2: *const ::std::os::raw::c_char, + __loc: locale_t, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn strncasecmp_l( + __s1: *const ::std::os::raw::c_char, + __s2: *const ::std::os::raw::c_char, + __n: size_t, + __loc: locale_t, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn explicit_bzero(__s: *mut ::std::os::raw::c_void, __n: size_t); +} +extern "C" { + pub fn strsep( + __stringp: *mut *mut ::std::os::raw::c_char, + __delim: *const ::std::os::raw::c_char, + ) -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn strsignal(__sig: ::std::os::raw::c_int) -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn __stpcpy( + __dest: *mut ::std::os::raw::c_char, + __src: *const ::std::os::raw::c_char, + ) -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn stpcpy( + __dest: *mut ::std::os::raw::c_char, + __src: *const ::std::os::raw::c_char, + ) -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn __stpncpy( + __dest: *mut ::std::os::raw::c_char, + __src: *const ::std::os::raw::c_char, + __n: size_t, + ) -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn stpncpy( + __dest: *mut ::std::os::raw::c_char, + __src: *const ::std::os::raw::c_char, + __n: ::std::os::raw::c_ulong, + ) -> *mut ::std::os::raw::c_char; +} +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; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __locale_data { + pub _address: u8, +} From patchwork Fri Jul 8 11:34:56 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 588763 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 3CE4EC433EF for ; Fri, 8 Jul 2022 11:35:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237487AbiGHLfP (ORCPT ); Fri, 8 Jul 2022 07:35:15 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33514 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237365AbiGHLfO (ORCPT ); Fri, 8 Jul 2022 07:35:14 -0400 Received: from mail-pf1-x430.google.com (mail-pf1-x430.google.com [IPv6:2607:f8b0:4864:20::430]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C484188F26 for ; Fri, 8 Jul 2022 04:35:13 -0700 (PDT) Received: by mail-pf1-x430.google.com with SMTP id y9so9086959pff.12 for ; Fri, 08 Jul 2022 04:35:13 -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=Rk3DhN0hlB//7nCiRduAgUt6fPdLaH7zTfFGKPx/lAc=; b=CDwOpxbTZbtataTIs2ct2+DJnlwq4U+Wk6c7M40dFMEMmZhToKIsIMTcaA5TwEVDSi qKyZTnBnKwZEtEVL2k3yBzoFucN9esXK2PYo4xFMbcGKssdLq3KoxX9GkEiVoEi5fAQb VO8H4bvvfOe5NLLH3tCsqtfrwzV7G9nUmXiL1JOsIbAuPKaFFoRnfns70Luo3+UFXQsx BKwJIwC+OF/D8eNlumAtvuM84okNgKngDFUJOgy171fLCUnXfJi3BJUUjFBjWVQP07oc 5lYK1G7u0iOane4jyDQTmYOTI3CgNrdRaf8HwtS/PXGq2cY5KowDyvB6vWPgn+oD48u/ e2Pg== 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=Rk3DhN0hlB//7nCiRduAgUt6fPdLaH7zTfFGKPx/lAc=; b=yJxxg8rPpIPUXiAeck3jrnZy0ku/gZtNtalZWDnVt4VU93otCSDqASwlkNzMOgqgFa xj7NNeqlSPSFhnVlUCzNNGlF1DFHBShSIpQOpHKys+Wg1PW0l2loSaS7QBsSD8ylMLbM mlIrzpBplBu+CULt7qJViMbVSedO0ZfI/EYqXiKOGUJu0C9QDLL4N4t9oa3rYWyfwlZf MHAQ/e0Ua6DO/TB4ke6bP0DBmCx41PspW3L83JLlWom7lQSQOMyGYC7YoZh5AtnYfTc7 LOXMeUflwWSzGeqH1FPq+T7ZJByFxECHo6oNm0/ZcDzELHHgARqFKguYq2wiKzr98b29 yX4A== X-Gm-Message-State: AJIora8w6uCHHazyCY0599nKRO/kyNmy4PzBS9aW1p3gAsSxDsw15bvP 4fyPyXMNaWh8dkwmUIKG3NDQAw== X-Google-Smtp-Source: AGRyM1uUhPeejzuWkvgt5zuMz5BkOXH2LwTQtPiRxavo0G+/EzfNruyhET4KcdKLq2eHb++cQyJ6xQ== X-Received: by 2002:a63:e446:0:b0:412:937b:5a3c with SMTP id i6-20020a63e446000000b00412937b5a3cmr2952728pgk.316.1657280113285; Fri, 08 Jul 2022 04:35:13 -0700 (PDT) Received: from localhost ([122.171.18.80]) by smtp.gmail.com with ESMTPSA id f80-20020a623853000000b0052ab37ef3absm865557pfa.116.2022.07.08.04.35.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 08 Jul 2022 04:35:13 -0700 (PDT) From: Viresh Kumar To: Linus Walleij , Bartosz Golaszewski Cc: Viresh Kumar , Vincent Guittot , linux-gpio@vger.kernel.org, Kent Gibson , Miguel Ojeda , Wedson Almeida Filho , =?utf-8?q?Alex_Benn=C3=A9e?= , stratos-dev@op-lists.linaro.org, Gerard Ryan Subject: [PATCH V4 3/8] libgpiod-sys: Add support to generate gpiosim bindings Date: Fri, 8 Jul 2022 17:04:56 +0530 Message-Id: <9cc6890c1039f75eadb77a1c9c16bf947ec9eb9e.1657279685.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 support to generate gpiosim bindings. Signed-off-by: Viresh Kumar --- bindings/rust/libgpiod-sys/Cargo.toml | 1 + bindings/rust/libgpiod-sys/build.rs | 19 +++++++++++++++++-- bindings/rust/libgpiod-sys/gpiosim_wrapper.h | 1 + 3 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 bindings/rust/libgpiod-sys/gpiosim_wrapper.h diff --git a/bindings/rust/libgpiod-sys/Cargo.toml b/bindings/rust/libgpiod-sys/Cargo.toml index 77f82719d269..73b6761d16dd 100644 --- a/bindings/rust/libgpiod-sys/Cargo.toml +++ b/bindings/rust/libgpiod-sys/Cargo.toml @@ -9,6 +9,7 @@ edition = "2018" [features] generate = [ "bindgen" ] +gpiosim = [ "generate", "bindgen" ] [build-dependencies] bindgen = { version = "0.59.1", optional = true } diff --git a/bindings/rust/libgpiod-sys/build.rs b/bindings/rust/libgpiod-sys/build.rs index bbcd30f79d23..147daaf6b1da 100644 --- a/bindings/rust/libgpiod-sys/build.rs +++ b/bindings/rust/libgpiod-sys/build.rs @@ -14,13 +14,25 @@ fn generate_bindings(files: &Vec<&str>) { println!("cargo:rerun-if-changed={}", file); } + if cfg!(feature = "gpiosim") { + println!("cargo:rerun-if-changed=gpiosim_wrapper.h"); + } + // The bindgen::Builder is the main entry point // to bindgen, and lets you build up options for // the resulting bindings. - let bindings = bindgen::Builder::default() + let mut builder = bindgen::Builder::default() // The input header we would like to generate // bindings for. - .header("wrapper.h") + .header("wrapper.h"); + + if cfg!(feature = "gpiosim") { + builder = builder.header("gpiosim_wrapper.h"); + println!("cargo:rustc-link-lib=kmod"); + println!("cargo:rustc-link-lib=mount"); + } + + let bindings = builder // Tell cargo to invalidate the built crate whenever any of the // included header files changed. .parse_callbacks(Box::new(bindgen::CargoCallbacks)) @@ -46,6 +58,7 @@ fn build_gpiod(files: Vec<&str>) { .define("_GNU_SOURCE", None) .define("GPIOD_VERSION_STR", "\"libgpio-sys\"") .include("../../../include") + .include("/usr/include/libmount") .compile("gpiod"); } @@ -61,6 +74,8 @@ fn main() { "../../../lib/line-request.c", "../../../lib/misc.c", "../../../lib/request-config.c", + #[cfg(feature = "gpiosim")] + "../../../tests/gpiosim/gpiosim.c", ]; #[cfg(feature = "generate")] diff --git a/bindings/rust/libgpiod-sys/gpiosim_wrapper.h b/bindings/rust/libgpiod-sys/gpiosim_wrapper.h new file mode 100644 index 000000000000..47dc12a87917 --- /dev/null +++ b/bindings/rust/libgpiod-sys/gpiosim_wrapper.h @@ -0,0 +1 @@ +#include "../../../tests/gpiosim/gpiosim.h" From patchwork Fri Jul 8 11:34:57 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 589021 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 D03EFCCA47B for ; Fri, 8 Jul 2022 11:35:22 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237694AbiGHLfW (ORCPT ); Fri, 8 Jul 2022 07:35:22 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33624 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237365AbiGHLfU (ORCPT ); Fri, 8 Jul 2022 07:35:20 -0400 Received: from mail-pg1-x533.google.com (mail-pg1-x533.google.com [IPv6:2607:f8b0:4864:20::533]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id CD63F205C4 for ; Fri, 8 Jul 2022 04:35:17 -0700 (PDT) Received: by mail-pg1-x533.google.com with SMTP id g4so22151307pgc.1 for ; Fri, 08 Jul 2022 04:35:17 -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=yaJ7M9xEih1NlLzmbZYxOftLVrTwB5WYxjjCMOrFf20=; b=hTX9sIb0lUXIou6u4nKGwEfMXJ196fx5ASZL0oFn+Rczk9DYDjcJLSRZiR/ghPtakk HBr6YDQuOIFOFRgsTDhzPQvA0f/T+2ZZtRuryM48Nxm7rg4JdbryJh0jZm1yu1XSFbZK bdtRSjk0x9N+9lp7sgUVk14UYTcphcanJtJttHn2jQ8U1r1xCBuM0T4cwaN3mC3epBQW 2poPv2AgwJzhdXzRNO8S1B7IVs6jrdAX5TzWp/p+TWAEtBDwbLGy4rUudRYDbDG/DNie FBC4rnARmmvHoXpuNLkIyjdkIKAsWLqb7Sc5IKgV3widxTffbjPGFoDkzOEjGwRaJKrL Tj4w== 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=yaJ7M9xEih1NlLzmbZYxOftLVrTwB5WYxjjCMOrFf20=; b=Fhg2CM9kUyI0aCSTE5rpGH8DlNSEsJwhk8359eeU3XWLDYbJe2UeTm1uRcGWuGlu/k pt3iwELwIWRotSJYx6cps+czDzcT50qEUzZpPKohX7aXgkriRD/ol9MJeE7/QT3KKava haKU1UZdd32iULtysjcQRjrUutZ4z/c/jjsBhxN4i+i3Bng5QZ3//H0FlWipdSPzu21d tZbKuEyVLPP5ZgbMVYDvQmaOXh4uDW+DViM3tBUEnbPxmu5oovcLcCseenbPmY3WX8nz jn4L6LqRlIDH+rtyRuJugcTvAKxk49k6+S1r1irqg6hhZA2Vx8e6HBQjcNF8rCvKwelp LB2w== X-Gm-Message-State: AJIora8O/OhmJP3vqHsb38/+RleVR7scyghY0S82CVeDsp4bjvaipvZ3 algO98vTRsdJ9Ex88xVAoGqxTQ== X-Google-Smtp-Source: AGRyM1sHZmod47icYCjgCKhvvjo+aVY3O0LwuSaE7zChokjljbkiESmEiI3qYcpvtTWrxy19En6pow== X-Received: by 2002:a63:5c5e:0:b0:412:a2f1:d0dd with SMTP id n30-20020a635c5e000000b00412a2f1d0ddmr2979617pgm.251.1657280116001; Fri, 08 Jul 2022 04:35:16 -0700 (PDT) Received: from localhost ([122.171.18.80]) by smtp.gmail.com with ESMTPSA id r3-20020a170903020300b0016a11b7472csm29533964plh.166.2022.07.08.04.35.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 08 Jul 2022 04:35:15 -0700 (PDT) From: Viresh Kumar To: Linus Walleij , Bartosz Golaszewski Cc: Viresh Kumar , Vincent Guittot , linux-gpio@vger.kernel.org, Kent Gibson , Miguel Ojeda , Wedson Almeida Filho , =?utf-8?q?Alex_Benn=C3=A9e?= , stratos-dev@op-lists.linaro.org, Gerard Ryan Subject: [PATCH V4 4/8] libgpiod: Add rust wrapper crate Date: Fri, 8 Jul 2022 17:04:57 +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 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 | 12 + bindings/rust/src/chip.rs | 179 ++++++++++ bindings/rust/src/chip_info.rs | 69 ++++ bindings/rust/src/edge_event.rs | 105 ++++++ bindings/rust/src/event_buffer.rs | 88 +++++ bindings/rust/src/info_event.rs | 68 ++++ bindings/rust/src/lib.rs | 317 ++++++++++++++++++ bindings/rust/src/line_config.rs | 493 ++++++++++++++++++++++++++++ bindings/rust/src/line_info.rs | 189 +++++++++++ bindings/rust/src/line_request.rs | 248 ++++++++++++++ bindings/rust/src/request_config.rs | 122 +++++++ 11 files changed, 1890 insertions(+) create mode 100644 bindings/rust/Cargo.toml create mode 100644 bindings/rust/src/chip.rs create mode 100644 bindings/rust/src/chip_info.rs create mode 100644 bindings/rust/src/edge_event.rs create mode 100644 bindings/rust/src/event_buffer.rs create mode 100644 bindings/rust/src/info_event.rs create mode 100644 bindings/rust/src/lib.rs create mode 100644 bindings/rust/src/line_config.rs create mode 100644 bindings/rust/src/line_info.rs create mode 100644 bindings/rust/src/line_request.rs create mode 100644 bindings/rust/src/request_config.rs diff --git a/bindings/rust/Cargo.toml b/bindings/rust/Cargo.toml new file mode 100644 index 000000000000..d5d81486fa2f --- /dev/null +++ b/bindings/rust/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "libgpiod" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +libc = ">=0.2.39" +libgpiod-sys = { path = "libgpiod-sys" } +thiserror = "1.0" +vmm-sys-util = "=0.9.0" diff --git a/bindings/rust/src/chip.rs b/bindings/rust/src/chip.rs new file mode 100644 index 000000000000..50b5d6102f96 --- /dev/null +++ b/bindings/rust/src/chip.rs @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use std::os::raw::c_char; +use std::sync::Arc; +use std::time::Duration; +use std::{slice, str}; + +use vmm_sys_util::errno::Error as IoError; + +use super::{ + bindings, chip_info::ChipInfo, Error, InfoEvent, LineConfig, LineInfo, LineRequest, + RequestConfig, Result, +}; + +/// GPIO chip +/// +/// A GPIO chip object is associated with an open file descriptor to the GPIO +/// character device. It exposes basic information about the chip and allows +/// callers to retrieve information about each line, watch lines for state +/// changes and make line requests. +pub(crate) struct ChipInternal { + chip: *mut bindings::gpiod_chip, +} + +impl ChipInternal { + /// Find a chip by path. + pub(crate) fn open(path: &str) -> Result { + // Null-terminate the string + let path = path.to_owned() + "\0"; + + let chip = unsafe { bindings::gpiod_chip_open(path.as_ptr() as *const c_char) }; + if chip.is_null() { + return Err(Error::OperationFailed("Gpio Chip open", IoError::last())); + } + + Ok(Self { chip }) + } + + /// Private helper, Returns gpiod_chip + pub(crate) fn chip(&self) -> *mut bindings::gpiod_chip { + self.chip + } +} + +impl Drop for ChipInternal { + /// Close the chip and release all associated resources. + fn drop(&mut self) { + unsafe { bindings::gpiod_chip_close(self.chip) } + } +} + +pub struct Chip { + ichip: Arc, + info: ChipInfo, +} + +impl Chip { + /// Find a chip by path. + pub fn open(path: &str) -> Result { + let ichip = Arc::new(ChipInternal::open(path)?); + let info = ChipInfo::new(ichip.clone())?; + + Ok(Self { ichip, info }) + } + + /// Get the chip name as represented in the kernel. + pub fn get_name(&self) -> Result<&str> { + self.info.name() + } + + /// Get the chip label as represented in the kernel. + pub fn get_label(&self) -> Result<&str> { + self.info.label() + } + + /// Get the number of GPIO lines exposed by the chip. + pub fn get_num_lines(&self) -> u32 { + self.info.num_lines() + } + + /// Get the path used to find the chip. + pub fn get_path(&self) -> Result<&str> { + // SAFETY: The string returned by libgpiod is guaranteed to live as long + // as the `struct Chip`. + let path = unsafe { bindings::gpiod_chip_get_path(self.ichip.chip()) }; + + // SAFETY: The string is guaranteed to be valid here. + str::from_utf8(unsafe { + slice::from_raw_parts(path as *const u8, bindings::strlen(path) as usize) + }) + .map_err(Error::InvalidString) + } + + /// Get information about the chip. + pub fn info(&self) -> Result { + ChipInfo::new(self.ichip.clone()) + } + + /// Get a snapshot of information about the line. + pub fn line_info(&self, offset: u32) -> Result { + LineInfo::new(self.ichip.clone(), offset, false) + } + + /// Get the current snapshot of information about the line at given offset + /// and optionally start watching it for future changes. + pub fn watch_line_info(&self, offset: u32) -> Result { + LineInfo::new(self.ichip.clone(), offset, true) + } + + /// Get the file descriptor associated with the chip. + /// + /// The returned file descriptor must not be closed by the caller, else other methods for the + /// `struct Chip` may fail. + pub fn get_fd(&self) -> Result { + let fd = unsafe { bindings::gpiod_chip_get_fd(self.ichip.chip()) }; + + if fd < 0 { + Err(Error::OperationFailed("Gpio Chip get-fd", IoError::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: Duration) -> Result<()> { + let ret = unsafe { + bindings::gpiod_chip_wait_info_event(self.ichip.chip(), timeout.as_nanos() as i64) + }; + + match ret { + -1 => Err(Error::OperationFailed( + "Gpio Chip info-event-wait", + IoError::last(), + )), + 0 => Err(Error::OperationTimedOut), + _ => Ok(()), + } + } + + /// Read a single line status change event from the chip. If no events are + /// pending, this function will block. + pub fn read_info_event(&self) -> Result { + InfoEvent::new(&self.ichip) + } + + /// Map a GPIO line's name to its offset within the chip. + pub fn find_line(&self, name: &str) -> Result { + // Null-terminate the string + let name = name.to_owned() + "\0"; + + let ret = unsafe { + bindings::gpiod_chip_get_line_offset_from_name( + self.ichip.chip(), + name.as_ptr() as *const c_char, + ) + }; + + if ret == -1 { + Err(Error::OperationFailed( + "Gpio Chip find-line", + IoError::last(), + )) + } else { + Ok(ret as u32) + } + } + + /// Request a set of lines for exclusive usage. + pub fn request_lines( + &self, + rconfig: &RequestConfig, + lconfig: &LineConfig, + ) -> Result { + LineRequest::new(&self.ichip, rconfig, lconfig) + } +} diff --git a/bindings/rust/src/chip_info.rs b/bindings/rust/src/chip_info.rs new file mode 100644 index 000000000000..950368b54c6f --- /dev/null +++ b/bindings/rust/src/chip_info.rs @@ -0,0 +1,69 @@ +// 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::{slice, str}; + +use vmm_sys_util::errno::Error as IoError; + +use super::{bindings, ChipInternal, Error, Result}; + +/// GPIO chip Information +pub struct ChipInfo { + info: *mut bindings::gpiod_chip_info, +} + +impl ChipInfo { + /// Find a GPIO chip by path. + pub(crate) fn new(chip: Arc) -> Result { + let info = unsafe { bindings::gpiod_chip_get_info(chip.chip()) }; + if info.is_null() { + return Err(Error::OperationFailed( + "Gpio Chip get info", + IoError::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 { bindings::gpiod_chip_info_get_name(self.info) }; + + // SAFETY: The string is guaranteed to be valid here. + str::from_utf8(unsafe { + slice::from_raw_parts(name as *const u8, bindings::strlen(name) as usize) + }) + .map_err(Error::InvalidString) + } + + /// Get the GPIO chip label as represented in the kernel. + pub(crate) fn label(&self) -> Result<&str> { + // SAFETY: The string returned by libgpiod is guaranteed to live as long + // as the `struct Chip`. + let label = unsafe { bindings::gpiod_chip_info_get_label(self.info) }; + + // SAFETY: The string is guaranteed to be valid here. + str::from_utf8(unsafe { + slice::from_raw_parts(label as *const u8, bindings::strlen(label) as usize) + }) + .map_err(Error::InvalidString) + } + + /// Get the number of GPIO lines exposed by the chip. + pub(crate) fn num_lines(&self) -> u32 { + unsafe { bindings::gpiod_chip_info_get_num_lines(self.info) as u32 } + } +} + +impl Drop for ChipInfo { + /// Close the GPIO chip info and release all associated resources. + fn drop(&mut self) { + unsafe { bindings::gpiod_chip_info_free(self.info) } + } +} diff --git a/bindings/rust/src/edge_event.rs b/bindings/rust/src/edge_event.rs new file mode 100644 index 000000000000..89bda58709d2 --- /dev/null +++ b/bindings/rust/src/edge_event.rs @@ -0,0 +1,105 @@ +// 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 IoError; + +use super::{bindings, EdgeEventBufferInternal, Error, LineEdgeEvent, Result}; + +/// Line edge events handling +/// +/// An edge event object contains information about a single line edge event. +/// It contains the event type, timestamp and the offset of the line on which +/// the event occurred as well as two sequence numbers (global for all lines +/// in the associated request and local for this line only). +/// +/// Edge events are stored into an edge-event buffer object to improve +/// performance and to limit the number of memory allocations when a large +/// number of events are being read. + +pub struct EdgeEvent { + ibuffer: Option>, + event: *mut bindings::gpiod_edge_event, +} + +impl EdgeEvent { + /// Get an event stored in the buffer. + pub(crate) fn new( + ibuffer: &Arc, + index: u64, + copy: bool, + ) -> Result { + let event = unsafe { bindings::gpiod_edge_event_buffer_get_event(ibuffer.buffer(), index) }; + if event.is_null() { + return Err(Error::OperationFailed( + "Gpio EdgeEvent buffer-get-event", + IoError::last(), + )); + } + + if copy { + let event = unsafe { bindings::gpiod_edge_event_copy(event) }; + if event.is_null() { + return Err(Error::OperationFailed( + "Gpio EdgeEvent copy", + IoError::last(), + )); + } + + Ok(Self { + ibuffer: None, + event, + }) + } else { + Ok(Self { + ibuffer: Some(ibuffer.clone()), + event, + }) + } + } + + /// Get the event type. + pub fn get_event_type(&self) -> Result { + LineEdgeEvent::new(unsafe { bindings::gpiod_edge_event_get_event_type(self.event) } as u32) + } + + /// Get the timestamp of the event. + pub fn get_timestamp(&self) -> Duration { + Duration::from_nanos(unsafe { bindings::gpiod_edge_event_get_timestamp_ns(self.event) }) + } + + /// Get the offset of the line on which the event was triggered. + pub fn get_line_offset(&self) -> u32 { + unsafe { bindings::gpiod_edge_event_get_line_offset(self.event) } + } + + /// Get the global sequence number of the event. + /// + /// Returns sequence number of the event relative to all lines in the + /// associated line request. + pub fn get_global_seqno(&self) -> u64 { + unsafe { bindings::gpiod_edge_event_get_global_seqno(self.event) } + } + + /// Get the event sequence number specific to concerned line. + /// + /// Returns sequence number of the event relative to the line within the + /// lifetime of the associated line request. + pub fn get_line_seqno(&self) -> u64 { + unsafe { bindings::gpiod_edge_event_get_line_seqno(self.event) } + } +} + +impl Drop for EdgeEvent { + /// Free the edge event. + fn drop(&mut self) { + // Free the event only if a copy is made + if self.ibuffer.is_none() { + unsafe { bindings::gpiod_edge_event_free(self.event) }; + } + } +} diff --git a/bindings/rust/src/event_buffer.rs b/bindings/rust/src/event_buffer.rs new file mode 100644 index 000000000000..bb568a80e09c --- /dev/null +++ b/bindings/rust/src/event_buffer.rs @@ -0,0 +1,88 @@ +// 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 IoError; + +use super::{bindings, EdgeEvent, Error, Result}; + +/// Line edge events buffer +pub(crate) struct EdgeEventBufferInternal { + buffer: *mut bindings::gpiod_edge_event_buffer, +} + +impl EdgeEventBufferInternal { + /// Create a new edge event buffer. + /// + /// If capacity equals 0, it will be set to a default value of 64. If + /// capacity is larger than 1024, it will be limited to 1024. + pub fn new(capacity: u32) -> Result { + let buffer = unsafe { bindings::gpiod_edge_event_buffer_new(capacity as c_ulong) }; + if buffer.is_null() { + return Err(Error::OperationFailed( + "Gpio EdgeEventBuffer new", + IoError::last(), + )); + } + + Ok(Self { buffer }) + } + + /// Private helper, Returns gpiod_edge_event_buffer + pub(crate) fn buffer(&self) -> *mut bindings::gpiod_edge_event_buffer { + self.buffer + } +} + +impl Drop for EdgeEventBufferInternal { + /// Free the edge event buffer and release all associated resources. + fn drop(&mut self) { + unsafe { bindings::gpiod_edge_event_buffer_free(self.buffer) }; + } +} + +/// Line edge events buffer +pub struct EdgeEventBuffer { + ibuffer: Arc, +} + +impl EdgeEventBuffer { + /// Create a new edge event buffer. + /// + /// If capacity equals 0, it will be set to a default value of 64. If + /// capacity is larger than 1024, it will be limited to 1024. + pub fn new(capacity: u32) -> Result { + Ok(Self { + ibuffer: Arc::new(EdgeEventBufferInternal::new(capacity)?), + }) + } + + /// Private helper, Returns gpiod_edge_event_buffer + pub(crate) fn buffer(&self) -> *mut bindings::gpiod_edge_event_buffer { + self.ibuffer.buffer() + } + + /// Get the capacity of the event buffer. + pub fn get_capacity(&self) -> u32 { + unsafe { bindings::gpiod_edge_event_buffer_get_capacity(self.buffer()) as u32 } + } + + /// Read an event stored in the buffer. + pub fn get_event(&self, index: u64) -> Result { + EdgeEvent::new(&self.ibuffer, index, false) + } + + /// Make copy of an edge event stored in the buffer. + pub fn get_event_copy(&self, index: u64) -> Result { + EdgeEvent::new(&self.ibuffer, index, true) + } + + /// Get the number of events the buffers stores. + pub fn get_num_events(&self) -> u32 { + unsafe { bindings::gpiod_edge_event_buffer_get_num_events(self.buffer()) as u32 } + } +} diff --git a/bindings/rust/src/info_event.rs b/bindings/rust/src/info_event.rs new file mode 100644 index 000000000000..ffba23f350be --- /dev/null +++ b/bindings/rust/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::convert::TryFrom; +use std::sync::Arc; +use std::time::Duration; + +use vmm_sys_util::errno::Error as IoError; + +use super::{bindings, ChipInternal, Error, Event, LineInfo, Result}; + +/// Line status watch events +/// +/// Accessors for the info event objects allowing to monitor changes in GPIO +/// line state. +/// +/// Callers can be notified about changes in line's state using the interfaces +/// exposed by GPIO chips. Each info event contains information about the event +/// itself (timestamp, type) as well as a snapshot of line's state in the form +/// of a line-info object. + +pub struct InfoEvent { + event: *mut bindings::gpiod_info_event, +} + +impl InfoEvent { + /// Get a single chip's line's status change event. + pub(crate) fn new(ichip: &Arc) -> Result { + let event = unsafe { bindings::gpiod_chip_read_info_event(ichip.chip()) }; + if event.is_null() { + return Err(Error::OperationFailed( + "Gpio InfoEvent event-read", + IoError::last(), + )); + } + + Ok(Self { event }) + } + + /// Private helper, Returns gpiod_info_event + pub(crate) fn event(&self) -> *mut bindings::gpiod_info_event { + self.event + } + + /// Get the event type of the status change event. + pub fn get_event_type(&self) -> Result { + Event::new(unsafe { bindings::gpiod_info_event_get_event_type(self.event) } as u32) + } + + /// Get the timestamp of the event, read from the monotonic clock. + pub fn get_timestamp(&self) -> Duration { + Duration::from_nanos(unsafe { bindings::gpiod_info_event_get_timestamp_ns(self.event) }) + } + + /// Get the line-info object associated with the event. + pub fn line_info(&self) -> Result { + LineInfo::try_from(self) + } +} + +impl Drop for InfoEvent { + /// Free the info event object and release all associated resources. + fn drop(&mut self) { + unsafe { bindings::gpiod_info_event_free(self.event) } + } +} diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs new file mode 100644 index 000000000000..2f2ac515d353 --- /dev/null +++ b/bindings/rust/src/lib.rs @@ -0,0 +1,317 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Rust wrappers for GPIOD APIs +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +//! libgpiod public API +//! +//! This is the complete documentation of the public Rust API made available to +//! users of libgpiod. +//! +//! The API is logically split into several parts such as: GPIO chip & line +//! operators, GPIO events handling etc. + +mod chip; +mod chip_info; +mod edge_event; +mod event_buffer; +mod info_event; +mod line_config; +mod line_info; +mod line_request; +mod request_config; + +use libgpiod_sys as bindings; + +pub use crate::chip::*; +pub use crate::edge_event::*; +pub use crate::event_buffer::*; +pub use crate::info_event::*; +pub use crate::line_config::*; +pub use crate::line_info::*; +pub use crate::line_request::*; +pub use crate::request_config::*; + +use std::os::raw::c_char; +use std::{slice, str}; + +use thiserror::Error as ThisError; +use vmm_sys_util::errno::Error as IoError; + +/// 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 find {0}")] + NameNotFound(&'static str), + #[error("Invalid String: {0:?}")] + InvalidString(str::Utf8Error), + #[error("Invalid {0} value: {1}")] + InvalidValue(&'static str, u32), + #[error("Operation {0} Failed: {1}")] + OperationFailed(&'static str, IoError), + #[error("Operation Timed-out")] + OperationTimedOut, +} + +/// Direction settings. +pub enum Direction { + /// Request the line(s), but don't change direction. + AsIs, + /// Direction is input - for reading the value of an externally driven GPIO line. + Input, + /// Direction is output - for driving the GPIO line. + Output, +} + +impl Direction { + fn new(dir: u32) -> Result { + match dir { + bindings::GPIOD_LINE_DIRECTION_AS_IS => Ok(Direction::AsIs), + bindings::GPIOD_LINE_DIRECTION_INPUT => Ok(Direction::Input), + bindings::GPIOD_LINE_DIRECTION_OUTPUT => Ok(Direction::Output), + _ => Err(Error::InvalidValue("direction", dir)), + } + } + + fn gpiod_direction(&self) -> u32 { + match self { + Direction::AsIs => bindings::GPIOD_LINE_DIRECTION_AS_IS, + Direction::Input => bindings::GPIOD_LINE_DIRECTION_INPUT, + Direction::Output => bindings::GPIOD_LINE_DIRECTION_OUTPUT, + } + } +} + +/// Internal bias settings. +pub enum Bias { + /// Don't change the bias setting when applying line config. + AsIs, + /// The internal bias state is unknown. + Unknown, + /// The internal bias is disabled. + Disabled, + /// The internal pull-up bias is enabled. + PullUp, + /// The internal pull-down bias is enabled. + PullDown, +} + +impl Bias { + fn new(bias: u32) -> Result { + match bias { + bindings::GPIOD_LINE_BIAS_AS_IS => Ok(Bias::AsIs), + bindings::GPIOD_LINE_BIAS_UNKNOWN => Ok(Bias::Unknown), + bindings::GPIOD_LINE_BIAS_DISABLED => Ok(Bias::Disabled), + bindings::GPIOD_LINE_BIAS_PULL_UP => Ok(Bias::PullUp), + bindings::GPIOD_LINE_BIAS_PULL_DOWN => Ok(Bias::PullDown), + _ => Err(Error::InvalidValue("bias", bias)), + } + } + + fn gpiod_bias(&self) -> u32 { + match self { + Bias::AsIs => bindings::GPIOD_LINE_BIAS_AS_IS, + Bias::Unknown => bindings::GPIOD_LINE_BIAS_UNKNOWN, + Bias::Disabled => bindings::GPIOD_LINE_BIAS_DISABLED, + Bias::PullUp => bindings::GPIOD_LINE_BIAS_PULL_UP, + Bias::PullDown => bindings::GPIOD_LINE_BIAS_PULL_DOWN, + } + } +} + +/// Drive settings. +pub enum Drive { + /// Drive setting is push-pull. + PushPull, + /// Line output is open-drain. + OpenDrain, + /// Line output is open-source. + OpenSource, +} + +impl Drive { + fn new(drive: u32) -> Result { + match drive { + bindings::GPIOD_LINE_DRIVE_PUSH_PULL => Ok(Drive::PushPull), + bindings::GPIOD_LINE_DRIVE_OPEN_DRAIN => Ok(Drive::OpenDrain), + bindings::GPIOD_LINE_DRIVE_OPEN_SOURCE => Ok(Drive::OpenSource), + _ => Err(Error::InvalidValue("drive", drive)), + } + } + + fn gpiod_drive(&self) -> u32 { + match self { + Drive::PushPull => bindings::GPIOD_LINE_DRIVE_PUSH_PULL, + Drive::OpenDrain => bindings::GPIOD_LINE_DRIVE_OPEN_DRAIN, + Drive::OpenSource => bindings::GPIOD_LINE_DRIVE_OPEN_SOURCE, + } + } +} + +/// Edge detection settings. +pub enum Edge { + /// Line edge detection is disabled. + None, + /// Line detects rising edge events. + Rising, + /// Line detects falling edge events. + Falling, + /// Line detects both rising and falling edge events. + Both, +} + +impl Edge { + fn new(edge: u32) -> Result { + match edge { + bindings::GPIOD_LINE_EDGE_NONE => Ok(Edge::None), + bindings::GPIOD_LINE_EDGE_RISING => Ok(Edge::Rising), + bindings::GPIOD_LINE_EDGE_FALLING => Ok(Edge::Falling), + bindings::GPIOD_LINE_EDGE_BOTH => Ok(Edge::Both), + _ => Err(Error::InvalidValue("edge", edge)), + } + } + + fn gpiod_edge(&self) -> u32 { + match self { + Edge::None => bindings::GPIOD_LINE_EDGE_NONE, + Edge::Rising => bindings::GPIOD_LINE_EDGE_RISING, + Edge::Falling => bindings::GPIOD_LINE_EDGE_FALLING, + Edge::Both => bindings::GPIOD_LINE_EDGE_BOTH, + } + } +} + +/// Line config settings. +pub enum Config { + /// Line direction. + Direction, + /// Edge detection. + EdgeDetection, + /// Bias. + Bias, + /// Drive. + Drive, + /// Active-low setting. + ActiveLow, + /// Debounce period. + DebouncePeriodUs, + /// Event clock type. + EventClock, + /// Output value. + OutputValue, +} + +impl Config { + fn new(config: u32) -> Result { + match config { + bindings::GPIOD_LINE_CONFIG_PROP_DIRECTION => Ok(Config::Direction), + bindings::GPIOD_LINE_CONFIG_PROP_EDGE_DETECTION => Ok(Config::EdgeDetection), + bindings::GPIOD_LINE_CONFIG_PROP_BIAS => Ok(Config::Bias), + bindings::GPIOD_LINE_CONFIG_PROP_DRIVE => Ok(Config::Drive), + bindings::GPIOD_LINE_CONFIG_PROP_ACTIVE_LOW => Ok(Config::ActiveLow), + bindings::GPIOD_LINE_CONFIG_PROP_DEBOUNCE_PERIOD_US => Ok(Config::DebouncePeriodUs), + bindings::GPIOD_LINE_CONFIG_PROP_EVENT_CLOCK => Ok(Config::EventClock), + bindings::GPIOD_LINE_CONFIG_PROP_OUTPUT_VALUE => Ok(Config::OutputValue), + _ => Err(Error::InvalidValue("config", config)), + } + } +} + +/// Event clock settings. +pub enum EventClock { + /// Line uses the monotonic clock for edge event timestamps. + Monotonic, + /// Line uses the realtime clock for edge event timestamps. + Realtime, +} + +impl EventClock { + fn new(clock: u32) -> Result { + match clock { + bindings::GPIOD_LINE_EVENT_CLOCK_MONOTONIC => Ok(EventClock::Monotonic), + bindings::GPIOD_LINE_EVENT_CLOCK_REALTIME => Ok(EventClock::Realtime), + _ => Err(Error::InvalidValue("event clock", clock)), + } + } + + fn gpiod_clock(&self) -> u32 { + match self { + EventClock::Monotonic => bindings::GPIOD_LINE_EVENT_CLOCK_MONOTONIC, + EventClock::Realtime => bindings::GPIOD_LINE_EVENT_CLOCK_REALTIME, + } + } +} + +/// Line status change event types. +pub enum Event { + /// Line has been requested. + LineRequested, + /// Previously requested line has been released. + LineReleased, + /// Line configuration has changed. + LineConfigChanged, +} + +impl Event { + fn new(event: u32) -> Result { + match event { + bindings::GPIOD_INFO_EVENT_LINE_REQUESTED => Ok(Event::LineRequested), + bindings::GPIOD_INFO_EVENT_LINE_RELEASED => Ok(Event::LineReleased), + bindings::GPIOD_INFO_EVENT_LINE_CONFIG_CHANGED => Ok(Event::LineConfigChanged), + _ => Err(Error::InvalidValue("event", event)), + } + } +} + +#[derive(Copy, Clone)] +/// Edge event types. +pub enum LineEdgeEvent { + /// Rising edge event. + Rising, + /// Falling edge event. + Falling, +} + +impl LineEdgeEvent { + fn new(event: u32) -> Result { + match event { + bindings::GPIOD_EDGE_EVENT_RISING_EDGE => Ok(LineEdgeEvent::Rising), + bindings::GPIOD_EDGE_EVENT_FALLING_EDGE => Ok(LineEdgeEvent::Falling), + _ => Err(Error::InvalidValue("edge event", event)), + } + } +} + +/// Various libgpiod-related functions. + +/// Check if the file pointed to by path is a GPIO chip character device. +/// +/// Returns true if the file exists and is a GPIO chip character device or a +/// symbolic link to it. +pub fn gpiod_is_gpiochip_device(path: &str) -> bool { + // Null-terminate the string + let path = path.to_owned() + "\0"; + + unsafe { bindings::gpiod_is_gpiochip_device(path.as_ptr() as *const c_char) } +} + +/// Get the API version of the library as a human-readable string. +pub fn gpiod_version_string() -> Result<&'static str> { + // SAFETY: The string returned by libgpiod is guaranteed to live forever. + let version = unsafe { bindings::gpiod_version_string() }; + + if version.is_null() { + return Err(Error::NameNotFound("GPIO library version")); + } + + // SAFETY: The string is guaranteed to be valid here. + str::from_utf8(unsafe { + slice::from_raw_parts(version as *const u8, bindings::strlen(version) as usize) + }) + .map_err(Error::InvalidString) +} diff --git a/bindings/rust/src/line_config.rs b/bindings/rust/src/line_config.rs new file mode 100644 index 000000000000..0fc8d0b736a3 --- /dev/null +++ b/bindings/rust/src/line_config.rs @@ -0,0 +1,493 @@ +// 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::time::Duration; + +use vmm_sys_util::errno::Error as IoError; + +use super::{bindings, Bias, Config, Direction, Drive, Edge, Error, EventClock, Result}; + +/// Line configuration objects. +/// +/// The line-config object contains the configuration for lines that can be +/// used in two cases: +/// - when making a line request +/// - when reconfiguring a set of already requested lines. +/// +/// A new line-config object is instantiated with a set of sane defaults +/// for all supported configuration settings. Those defaults can be modified by +/// the caller. Default values can be overridden by applying different values +/// 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 LineConfig has no immediate effect on real +/// GPIOs, it only manipulates the config object in memory. Those changes are +/// only applied to the hardware at the time of the request or reconfiguration. +/// +/// Overrides for lines that don't end up being requested are silently ignored +/// both in LineRequest::new() as well as in LineRequest::reconfigure_lines(). +/// +/// In cases where all requested lines are using the one configuration, the +/// line overrides can be entirely ignored when preparing the configuration. + +pub struct LineConfig { + config: *mut bindings::gpiod_line_config, +} + +impl LineConfig { + /// Create a new line config object. + pub fn new() -> Result { + let config = unsafe { bindings::gpiod_line_config_new() }; + + if config.is_null() { + return Err(Error::OperationFailed( + "Gpio LineConfig new", + IoError::last(), + )); + } + + Ok(Self { config }) + } + + /// Private helper, Returns gpiod_line_config + pub(crate) fn config(&self) -> *mut bindings::gpiod_line_config { + self.config + } + + /// Resets the entire configuration stored in the object. This is useful if + /// the user wants to reuse the object without reallocating it. + pub fn reset(&mut self) { + unsafe { bindings::gpiod_line_config_reset(self.config) } + } + + /// Set the default line direction. + pub fn set_direction_default(&mut self, direction: Direction) { + unsafe { + bindings::gpiod_line_config_set_direction_default( + self.config, + direction.gpiod_direction() as i32, + ) + } + } + + /// Set the direction for a line. + pub fn set_direction_override(&mut self, direction: Direction, offset: u32) { + unsafe { + bindings::gpiod_line_config_set_direction_override( + self.config, + direction.gpiod_direction() as i32, + offset, + ) + } + } + + /// Clear the direction for a line. + pub fn clear_direction_override(&mut self, offset: u32) { + unsafe { bindings::gpiod_line_config_clear_direction_override(self.config, offset) } + } + + /// Check if the direction is overridden for a line. + pub fn direction_is_overridden(&self, offset: u32) -> bool { + unsafe { bindings::gpiod_line_config_direction_is_overridden(self.config, offset) } + } + + /// Get the default direction setting. + pub fn get_direction_default(&self) -> Result { + Direction::new( + unsafe { bindings::gpiod_line_config_get_direction_default(self.config) } as u32, + ) + } + + /// Get the direction of a given line. + /// + /// Direction setting for the line if the config object were used in a request. + pub fn get_direction_offset(&self, offset: u32) -> Result { + Direction::new(unsafe { + bindings::gpiod_line_config_get_direction_offset(self.config, offset) + } as u32) + } + + /// Set the default edge event detection setting. + pub fn set_edge_detection_default(&mut self, edge: Edge) { + unsafe { + bindings::gpiod_line_config_set_edge_detection_default( + self.config, + edge.gpiod_edge() as i32, + ) + } + } + + /// Set the edge event detection for a single line. + pub fn set_edge_detection_override(&mut self, edge: Edge, offset: u32) { + unsafe { + bindings::gpiod_line_config_set_edge_detection_override( + self.config, + edge.gpiod_edge() as i32, + offset, + ) + } + } + + /// Clear the edge event detection for a single line. + pub fn clear_edge_detection_override(&mut self, offset: u32) { + unsafe { bindings::gpiod_line_config_clear_edge_detection_override(self.config, offset) } + } + + /// Check if the edge event detection is overridden for a line. + pub fn edge_detection_is_overridden(&self, offset: u32) -> bool { + unsafe { bindings::gpiod_line_config_edge_detection_is_overridden(self.config, offset) } + } + + /// Get the default edge event detection setting. + pub fn get_edge_detection_default(&self) -> Result { + Edge::new( + unsafe { bindings::gpiod_line_config_get_edge_detection_default(self.config) } as u32, + ) + } + + /// Get the edge event detection setting for a given line. + /// + /// Edge event detection setting for the line if the config object were used in a request. + pub fn get_edge_detection_offset(&self, offset: u32) -> Result { + Edge::new(unsafe { + bindings::gpiod_line_config_get_edge_detection_offset(self.config, offset) + } as u32) + } + + /// Set the default bias setting. + pub fn set_bias_default(&mut self, bias: Bias) { + unsafe { + bindings::gpiod_line_config_set_bias_default(self.config, bias.gpiod_bias() as i32) + } + } + + /// Set the bias for a single line. + pub fn set_bias_override(&mut self, bias: Bias, offset: u32) { + unsafe { + bindings::gpiod_line_config_set_bias_override( + self.config, + bias.gpiod_bias() as i32, + offset, + ) + } + } + + /// Clear the bias for a single line. + pub fn clear_bias_override(&mut self, offset: u32) { + unsafe { bindings::gpiod_line_config_clear_bias_override(self.config, offset) } + } + + /// Check if the bias is overridden for a line. + pub fn bias_is_overridden(&self, offset: u32) -> bool { + unsafe { bindings::gpiod_line_config_bias_is_overridden(self.config, offset) } + } + + /// Get the default bias setting. + pub fn get_bias_default(&self) -> Result { + Bias::new(unsafe { bindings::gpiod_line_config_get_bias_default(self.config) } as u32) + } + + /// Get the bias setting for a given line. + /// + /// Bias setting used for the line if the config object were used in a request. + pub fn get_bias_offset(&self, offset: u32) -> Result { + Bias::new( + unsafe { bindings::gpiod_line_config_get_bias_offset(self.config, offset) } as u32, + ) + } + + /// Set the default drive setting. + pub fn set_drive_default(&mut self, drive: Drive) { + unsafe { + bindings::gpiod_line_config_set_drive_default(self.config, drive.gpiod_drive() as i32) + } + } + + /// Set the drive for a single line. + pub fn set_drive_override(&mut self, drive: Drive, offset: u32) { + unsafe { + bindings::gpiod_line_config_set_drive_override( + self.config, + drive.gpiod_drive() as i32, + offset, + ) + } + } + + /// clear the drive for a single line. + pub fn clear_drive_override(&mut self, offset: u32) { + unsafe { bindings::gpiod_line_config_clear_drive_override(self.config, offset) } + } + + /// Check if the drive is overridden for a line. + pub fn drive_is_overridden(&self, offset: u32) -> bool { + unsafe { bindings::gpiod_line_config_drive_is_overridden(self.config, offset) } + } + + /// Get the default drive setting. + pub fn get_drive_default(&self) -> Result { + Drive::new(unsafe { bindings::gpiod_line_config_get_drive_default(self.config) } as u32) + } + + /// Get the drive setting for a given line. + /// + /// The offset of the line for which to read the drive setting. Drive setting for the line if + /// the config object were used in a request. + pub fn get_drive_offset(&self, offset: u32) -> Result { + Drive::new( + unsafe { bindings::gpiod_line_config_get_drive_offset(self.config, offset) } as u32, + ) + } + + /// Set default active-low setting. + pub fn set_active_low_default(&mut self, active_low: bool) { + unsafe { bindings::gpiod_line_config_set_active_low_default(self.config, active_low) } + } + + /// Set active-low setting for a single line. + pub fn set_active_low_override(&mut self, active_low: bool, offset: u32) { + unsafe { + bindings::gpiod_line_config_set_active_low_override(self.config, active_low, offset) + } + } + + /// Clear a single line's active-low setting. + pub fn clear_active_low_override(&mut self, offset: u32) { + unsafe { bindings::gpiod_line_config_clear_active_low_override(self.config, offset) } + } + + /// Check if the active-low is overridden for a line. + pub fn active_low_is_overridden(&mut self, offset: u32) -> bool { + unsafe { bindings::gpiod_line_config_active_low_is_overridden(self.config, offset) } + } + + /// Check the default active-low setting. + pub fn get_active_low_default(&self) -> bool { + unsafe { bindings::gpiod_line_config_get_active_low_default(self.config) } + } + + /// Check the active-low setting of a line. + /// + /// Active-low setting for the line if the config object were used in a request. + pub fn get_active_low_offset(&self, offset: u32) -> bool { + unsafe { bindings::gpiod_line_config_get_active_low_offset(self.config, offset) } + } + + /// Set the deafult debounce period setting. + pub fn set_debounce_period_default(&mut self, period: Duration) { + unsafe { + bindings::gpiod_line_config_set_debounce_period_us_default( + self.config, + period.as_micros() as u64, + ) + } + } + + /// Set the debounce period for a single line. + pub fn set_debounce_period_override(&mut self, period: Duration, offset: u32) { + unsafe { + bindings::gpiod_line_config_set_debounce_period_us_override( + self.config, + period.as_micros() as u64, + offset, + ) + } + } + + /// Clear the debounce period for a single line. + pub fn clear_debounce_period_override(&mut self, offset: u32) { + unsafe { + bindings::gpiod_line_config_clear_debounce_period_us_override(self.config, offset) + } + } + + /// Check if the debounce period setting is overridden. + pub fn debounce_period_is_overridden(&self, offset: u32) -> bool { + unsafe { bindings::gpiod_line_config_debounce_period_us_is_overridden(self.config, offset) } + } + + /// Get the default debounce period. + pub fn get_debounce_period_default(&self) -> Result { + Ok(Duration::from_micros(unsafe { + bindings::gpiod_line_config_get_debounce_period_us_default(self.config) + })) + } + + /// Get the debounce period for a given line. + /// + /// Debounce period for the line if the config object were used in a request, 0 if debouncing + /// is disabled. + pub fn get_debounce_period_offset(&self, offset: u32) -> Result { + Ok(Duration::from_micros(unsafe { + bindings::gpiod_line_config_get_debounce_period_us_offset(self.config, offset) + })) + } + + /// Set the default event clock setting. + pub fn set_event_clock_default(&mut self, clock: EventClock) { + unsafe { + bindings::gpiod_line_config_set_event_clock_default( + self.config, + clock.gpiod_clock() as i32, + ) + } + } + + /// Set the event clock for a single line. + pub fn set_event_clock_override(&mut self, clock: EventClock, offset: u32) { + unsafe { + bindings::gpiod_line_config_set_event_clock_override( + self.config, + clock.gpiod_clock() as i32, + offset, + ) + } + } + + /// Clear the event clock for a single line. + pub fn clear_event_clock_override(&mut self, offset: u32) { + unsafe { bindings::gpiod_line_config_clear_event_clock_override(self.config, offset) } + } + + /// Check if the event clock is overridden for a line. + pub fn event_clock_is_overridden(&mut self, offset: u32) -> bool { + unsafe { bindings::gpiod_line_config_event_clock_is_overridden(self.config, offset) } + } + + /// Get the default event clock setting. + pub fn get_event_clock_default(&self) -> Result { + EventClock::new( + unsafe { bindings::gpiod_line_config_get_event_clock_default(self.config) } as u32, + ) + } + + /// Get the event clock setting for a given line. + /// + /// Event clock setting for the line if the config object were used in a request. + pub fn get_event_clock_offset(&self, offset: u32) -> Result { + EventClock::new(unsafe { + bindings::gpiod_line_config_get_event_clock_offset(self.config, offset) + } as u32) + } + + /// Set the default output value setting. + pub fn set_output_value_default(&mut self, value: u32) { + unsafe { bindings::gpiod_line_config_set_output_value_default(self.config, value as i32) } + } + + /// Set the output value for a line. + pub fn set_output_value_override(&mut self, value: u32, offset: u32) { + unsafe { + bindings::gpiod_line_config_set_output_value_override(self.config, value as i32, offset) + } + } + + /// Set the output values for a set of lines. + pub fn set_output_values(&mut self, offsets: &[u32], values: &[i32]) -> Result<()> { + if offsets.len() != values.len() { + return Err(Error::OperationFailed( + "Gpio LineConfig array size mismatch", + IoError::new(EINVAL), + )); + } + + unsafe { + bindings::gpiod_line_config_set_output_values( + self.config, + values.len() as c_ulong, + offsets.as_ptr(), + values.as_ptr(), + ); + } + + Ok(()) + } + + /// Clear the output value for a line. + pub fn clear_output_value_override(&mut self, offset: u32) { + unsafe { bindings::gpiod_line_config_clear_output_value_override(self.config, offset) } + } + + /// Check if the output value is overridden for a line. + pub fn output_value_is_overridden(&self, offset: u32) -> bool { + unsafe { bindings::gpiod_line_config_output_value_is_overridden(self.config, offset) } + } + + /// Get the default output value, 0 or 1. + pub fn get_output_value_default(&self) -> Result { + let value = unsafe { bindings::gpiod_line_config_get_output_value_default(self.config) }; + + if value != 0 && value != 1 { + Err(Error::OperationFailed( + "Gpio LineConfig get-output-value", + IoError::last(), + )) + } else { + Ok(value as u32) + } + } + + /// Get the output value configured for a given line, 0 or 1. + pub fn get_output_value_offset(&self, offset: u32) -> Result { + let value = + unsafe { bindings::gpiod_line_config_get_output_value_offset(self.config, offset) }; + + if value != 0 && value != 1 { + Err(Error::OperationFailed( + "Gpio LineConfig get-output-value", + IoError::last(), + )) + } else { + Ok(value as u32) + } + } + + /// Get the list of overridden offsets and the corresponding types of overridden settings. + pub fn get_overrides(&self) -> Result> { + let num = unsafe { bindings::gpiod_line_config_get_num_overrides(self.config) } as usize; + if num == 0 { + return Ok(Vec::new()); + } + + let mut overrides = Vec::new(); + let mut offset = vec![0_u32; num]; + let mut props = vec![0_i32; num]; + + unsafe { + bindings::gpiod_line_config_get_overrides( + self.config, + offset.as_mut_ptr(), + props.as_mut_ptr(), + ) + }; + + for i in 0..num { + overrides.push((offset[i], Config::new(props[i] as u32)?)); + } + + Ok(overrides) + } +} + +impl Drop for LineConfig { + /// Free the line config object and release all associated resources. + fn drop(&mut self) { + unsafe { bindings::gpiod_line_config_free(self.config) } + } +} diff --git a/bindings/rust/src/line_info.rs b/bindings/rust/src/line_info.rs new file mode 100644 index 000000000000..70b6bd6a84bb --- /dev/null +++ b/bindings/rust/src/line_info.rs @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use std::convert::TryFrom; +use std::sync::Arc; +use std::time::Duration; +use std::{slice, str}; + +use vmm_sys_util::errno::Error as IoError; + +use super::{ + bindings, Bias, ChipInternal, Direction, Drive, Edge, Error, EventClock, InfoEvent, Result, +}; + +/// Line info +/// +/// Exposes functions for retrieving kernel information about both requested and +/// free lines. Line info object contains an immutable snapshot of a line's status. +/// +/// The line info contains all the publicly available information about a +/// line, which does not include the line value. The line must be requested +/// to access the line value. + +pub struct LineInfo { + info: *mut bindings::gpiod_line_info, + ichip: Option>, + free: bool, +} + +impl LineInfo { + /// Get a snapshot of information about the line and optionally start watching it for changes. + pub(crate) fn new(ichip: Arc, offset: u32, watch: bool) -> Result { + let info = if watch { + unsafe { bindings::gpiod_chip_watch_line_info(ichip.chip(), offset) } + } else { + unsafe { bindings::gpiod_chip_get_line_info(ichip.chip(), offset) } + }; + + if info.is_null() { + return Err(Error::OperationFailed( + "Gpio LineInfo line-info", + IoError::last(), + )); + } + + Ok(Self { + info, + ichip: if watch { Some(ichip) } else { None }, + free: watch, + }) + } + + /// Stop watching the line + pub fn unwatch(&mut self) { + if let Some(ichip) = &self.ichip { + unsafe { + bindings::gpiod_chip_unwatch_line_info(ichip.chip(), self.get_offset()); + } + self.ichip = None; + } + } + + /// Get the offset of the line within the GPIO chip. + /// + /// The offset uniquely identifies the line on the chip. The combination of the chip and offset + /// uniquely identifies the line within the system. + + pub fn get_offset(&self) -> u32 { + unsafe { bindings::gpiod_line_info_get_offset(self.info) } + } + + /// Get GPIO line's name. + pub fn get_name(&self) -> Result<&str> { + // SAFETY: The string returned by libgpiod is guaranteed to live as long + // as the `struct LineInfo`. + let name = unsafe { bindings::gpiod_line_info_get_name(self.info) }; + if name.is_null() { + return Err(Error::NameNotFound("GPIO line's name")); + } + + // SAFETY: The string is guaranteed to be valid here. + str::from_utf8(unsafe { + slice::from_raw_parts(name as *const u8, bindings::strlen(name) as usize) + }) + .map_err(Error::InvalidString) + } + + /// Returns True if the line is in use, false otherwise. + /// + /// The user space can't know exactly why a line is busy. It may have been + /// requested by another process or hogged by the kernel. It only matters that + /// the line is used and we can't request it. + pub fn is_used(&self) -> bool { + unsafe { bindings::gpiod_line_info_is_used(self.info) } + } + + /// Get the GPIO line's consumer name. + pub fn get_consumer(&self) -> Result<&str> { + // SAFETY: The string returned by libgpiod is guaranteed to live as long + // as the `struct LineInfo`. + let name = unsafe { bindings::gpiod_line_info_get_consumer(self.info) }; + if name.is_null() { + return Err(Error::NameNotFound("GPIO line's consumer name")); + } + + // SAFETY: The string is guaranteed to be valid here. + str::from_utf8(unsafe { + slice::from_raw_parts(name as *const u8, bindings::strlen(name) as usize) + }) + .map_err(Error::InvalidString) + } + + /// Get the GPIO line's direction. + pub fn get_direction(&self) -> Result { + Direction::new(unsafe { bindings::gpiod_line_info_get_direction(self.info) } as u32) + } + + /// Returns true if the line is "active-low", false otherwise. + pub fn is_active_low(&self) -> bool { + unsafe { bindings::gpiod_line_info_is_active_low(self.info) } + } + + /// Get the GPIO line's bias setting. + pub fn get_bias(&self) -> Result { + Bias::new(unsafe { bindings::gpiod_line_info_get_bias(self.info) } as u32) + } + + /// Get the GPIO line's drive setting. + pub fn get_drive(&self) -> Result { + Drive::new(unsafe { bindings::gpiod_line_info_get_drive(self.info) } as u32) + } + + /// Get the current edge detection setting of the line. + pub fn get_edge_detection(&self) -> Result { + Edge::new(unsafe { bindings::gpiod_line_info_get_edge_detection(self.info) } as u32) + } + + /// Get the current event clock setting used for edge event timestamps. + pub fn get_event_clock(&self) -> Result { + EventClock::new(unsafe { bindings::gpiod_line_info_get_event_clock(self.info) } as u32) + } + + /// Returns true if the line is debounced (either by hardware or by the + /// kernel software debouncer), false otherwise. + pub fn is_debounced(&self) -> bool { + unsafe { bindings::gpiod_line_info_is_debounced(self.info) } + } + + /// Get the debounce period of the line. + pub fn get_debounce_period(&self) -> Duration { + Duration::from_micros(unsafe { + bindings::gpiod_line_info_get_debounce_period_us(self.info) + }) + } +} + +impl TryFrom<&InfoEvent> for LineInfo { + type Error = Error; + + /// Get the Line info object associated with a event. + fn try_from(event: &InfoEvent) -> Result { + let info = unsafe { bindings::gpiod_info_event_get_line_info(event.event()) }; + if info.is_null() { + return Err(Error::OperationFailed( + "Gpio LineInfo try-from", + IoError::last(), + )); + } + + Ok(Self { + info, + ichip: None, + free: false, + }) + } +} + +impl Drop for LineInfo { + fn drop(&mut self) { + // We must not free the Line info object created from `struct InfoEvent` by calling + // libgpiod API. + if self.free { + self.unwatch(); + unsafe { bindings::gpiod_line_info_free(self.info) } + } + } +} diff --git a/bindings/rust/src/line_request.rs b/bindings/rust/src/line_request.rs new file mode 100644 index 000000000000..bb338e72671d --- /dev/null +++ b/bindings/rust/src/line_request.rs @@ -0,0 +1,248 @@ +// 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 IoError; + +use super::{bindings, ChipInternal, EdgeEventBuffer, Error, LineConfig, RequestConfig, Result}; + +/// Line request operations +/// +/// Allows interaction with a set of requested lines. +pub struct LineRequest { + request: *mut bindings::gpiod_line_request, +} + +impl LineRequest { + /// Request a set of lines for exclusive usage. + pub(crate) fn new( + ichip: &Arc, + rconfig: &RequestConfig, + lconfig: &LineConfig, + ) -> Result { + let request = unsafe { + bindings::gpiod_chip_request_lines(ichip.chip(), rconfig.config(), lconfig.config()) + }; + + if request.is_null() { + return Err(Error::OperationFailed( + "Gpio LineRequest request-lines", + IoError::last(), + )); + } + + Ok(Self { request }) + } + + /// Get the number of lines in the request. + pub fn get_num_lines(&self) -> u32 { + unsafe { bindings::gpiod_line_request_get_num_lines(self.request) as u32 } + } + + /// Get the offsets of lines in the request. + pub fn get_offsets(&self) -> Vec { + let mut offsets = vec![0; self.get_num_lines() as usize]; + + unsafe { bindings::gpiod_line_request_get_offsets(self.request, offsets.as_mut_ptr()) }; + offsets + } + + /// Get the value (0 or 1) of a single line associated with the request. + pub fn get_value(&self, offset: u32) -> Result { + let value = unsafe { bindings::gpiod_line_request_get_value(self.request, offset) }; + + if value != 0 && value != 1 { + Err(Error::OperationFailed( + "Gpio LineRequest get-value", + IoError::last(), + )) + } else { + Ok(value as u32) + } + } + + /// Get values of a subset of lines associated with the request. + pub fn get_values_subset(&self, offsets: &[u32], values: &mut Vec) -> Result<()> { + if offsets.len() != values.len() { + return Err(Error::OperationFailed( + "Gpio LineRequest array size mismatch", + IoError::new(EINVAL), + )); + } + + let ret = unsafe { + bindings::gpiod_line_request_get_values_subset( + self.request, + offsets.len() as c_ulong, + offsets.as_ptr(), + values.as_mut_ptr(), + ) + }; + + if ret == -1 { + Err(Error::OperationFailed( + "Gpio LineRequest get-values-subset", + IoError::last(), + )) + } else { + Ok(()) + } + } + + /// Get values of all lines associated with the request. + pub fn get_values(&self, values: &mut Vec) -> Result<()> { + if values.len() != self.get_num_lines() as usize { + return Err(Error::OperationFailed( + "Gpio LineRequest array size mismatch", + IoError::new(EINVAL), + )); + } + + let ret = + unsafe { bindings::gpiod_line_request_get_values(self.request, values.as_mut_ptr()) }; + + if ret == -1 { + Err(Error::OperationFailed( + "Gpio LineRequest get-values", + IoError::last(), + )) + } else { + Ok(()) + } + } + + /// Set the value of a single line associated with the request. + pub fn set_value(&self, offset: u32, value: i32) -> Result<()> { + let ret = unsafe { bindings::gpiod_line_request_set_value(self.request, offset, !!value) }; + + if ret == -1 { + Err(Error::OperationFailed( + "Gpio LineRequest set-value", + IoError::last(), + )) + } else { + Ok(()) + } + } + + /// Get values of a subset of lines associated with the request. + pub fn set_values_subset(&self, offsets: &[u32], values: &[i32]) -> Result<()> { + if offsets.len() != values.len() { + return Err(Error::OperationFailed( + "Gpio LineRequest array size mismatch", + IoError::new(EINVAL), + )); + } + + let ret = unsafe { + bindings::gpiod_line_request_set_values_subset( + self.request, + offsets.len() as c_ulong, + offsets.as_ptr(), + values.as_ptr(), + ) + }; + + if ret == -1 { + Err(Error::OperationFailed( + "Gpio LineRequest set-values-subset", + IoError::last(), + )) + } else { + Ok(()) + } + } + + /// Get values of all lines associated with the request. + pub fn set_values(&self, values: &[i32]) -> Result<()> { + if values.len() != self.get_num_lines() as usize { + return Err(Error::OperationFailed( + "Gpio LineRequest array size mismatch", + IoError::new(EINVAL), + )); + } + + let ret = unsafe { bindings::gpiod_line_request_set_values(self.request, values.as_ptr()) }; + + if ret == -1 { + Err(Error::OperationFailed( + "Gpio LineRequest set-values", + IoError::last(), + )) + } else { + Ok(()) + } + } + + /// Update the configuration of lines associated with the line request. + pub fn reconfigure_lines(&self, lconfig: &LineConfig) -> Result<()> { + let ret = unsafe { + bindings::gpiod_line_request_reconfigure_lines(self.request, lconfig.config()) + }; + + if ret == -1 { + Err(Error::OperationFailed( + "Gpio LineRequest reconfigure-lines", + IoError::last(), + )) + } else { + Ok(()) + } + } + + /// Get the file descriptor associated with the line request. + pub fn get_fd(&self) -> u32 { + unsafe { bindings::gpiod_line_request_get_fd(self.request) as u32 } + } + + /// Wait for edge events on any of the lines associated with the request. + pub fn wait_edge_event(&self, timeout: Duration) -> Result<()> { + let ret = unsafe { + bindings::gpiod_line_request_wait_edge_event(self.request, timeout.as_nanos() as i64) + }; + + match ret { + -1 => Err(Error::OperationFailed( + "Gpio LineRequest edge-event-wait", + IoError::last(), + )), + 0 => Err(Error::OperationTimedOut), + _ => Ok(()), + } + } + + /// Get a number of edge events from a line request. + /// + /// This function will block if no event was queued for the line. + pub fn read_edge_event(&self, buffer: &EdgeEventBuffer, max_events: u32) -> Result { + let ret = unsafe { + bindings::gpiod_line_request_read_edge_event( + self.request, + buffer.buffer(), + max_events as c_ulong, + ) + }; + + if ret == -1 { + Err(Error::OperationFailed( + "Gpio LineRequest edge-event-read", + IoError::last(), + )) + } else { + Ok(ret as u32) + } + } +} + +impl Drop for LineRequest { + /// Release the requested lines and free all associated resources. + fn drop(&mut self) { + unsafe { bindings::gpiod_line_request_release(self.request) } + } +} diff --git a/bindings/rust/src/request_config.rs b/bindings/rust/src/request_config.rs new file mode 100644 index 000000000000..3e667d28cc1f --- /dev/null +++ b/bindings/rust/src/request_config.rs @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use std::os::raw::{c_char, c_ulong}; +use std::{slice, str}; + +use vmm_sys_util::errno::Error as IoError; + +use super::{bindings, Error, Result}; + +/// Request configuration objects +/// +/// Request config objects are used to pass a set of options to the kernel at +/// the time of the line request. Similarly to the line-config - the mutators +/// don't return error values. If the values are invalid, in general they are +/// silently adjusted to acceptable ranges. + +pub struct RequestConfig { + config: *mut bindings::gpiod_request_config, +} + +impl RequestConfig { + /// Create a new request config object. + pub fn new() -> Result { + let config = unsafe { bindings::gpiod_request_config_new() }; + if config.is_null() { + return Err(Error::OperationFailed( + "Gpio RequestConfig new", + IoError::last(), + )); + } + + Ok(Self { config }) + } + + /// Private helper, Returns gpiod_request_config + pub(crate) fn config(&self) -> *mut bindings::gpiod_request_config { + self.config + } + + /// Set the consumer name for the request. + /// + /// If the consumer string is too long, it will be truncated to the max + /// accepted length. + pub fn set_consumer(&self, consumer: &str) { + // Null-terminate the string + let consumer = consumer.to_owned() + "\0"; + + unsafe { + bindings::gpiod_request_config_set_consumer( + self.config, + consumer.as_ptr() as *const c_char, + ) + } + } + + /// Get the consumer name configured in the request config. + pub fn get_consumer(&self) -> Result<&str> { + // SAFETY: The string returned by libgpiod is guaranteed to live as long + // as the `struct RequestConfig`. + let consumer = unsafe { bindings::gpiod_request_config_get_consumer(self.config) }; + if consumer.is_null() { + return Err(Error::OperationFailed( + "Gpio RequestConfig get-consumer", + IoError::last(), + )); + } + + // SAFETY: The string is guaranteed to be valid here. + str::from_utf8(unsafe { + slice::from_raw_parts(consumer as *const u8, bindings::strlen(consumer) as usize) + }) + .map_err(Error::InvalidString) + } + + /// Set the offsets of the lines to be requested. + /// + /// If too many offsets were specified, the offsets above the limit accepted + /// by the kernel (64 lines) are silently dropped. + pub fn set_offsets(&self, offsets: &[u32]) { + unsafe { + bindings::gpiod_request_config_set_offsets( + self.config, + offsets.len() as c_ulong, + offsets.as_ptr(), + ) + } + } + + /// Get the offsets of lines in the request config. + pub fn get_offsets(&self) -> Vec { + let num = unsafe { bindings::gpiod_request_config_get_num_offsets(self.config) }; + let mut offsets = vec![0; num as usize]; + + unsafe { bindings::gpiod_request_config_get_offsets(self.config, offsets.as_mut_ptr()) }; + offsets + } + + /// Set the size of the kernel event buffer for the request. + /// + /// The kernel may adjust the value if it's too high. If set to 0, the + /// default value will be used. + pub fn set_event_buffer_size(&self, size: u32) { + unsafe { + bindings::gpiod_request_config_set_event_buffer_size(self.config, size as c_ulong) + } + } + + /// Get the edge event buffer size for the request config. + pub fn get_event_buffer_size(&self) -> u32 { + unsafe { bindings::gpiod_request_config_get_event_buffer_size(self.config) as u32 } + } +} + +impl Drop for RequestConfig { + /// Free the request config object and release all associated resources. + fn drop(&mut self) { + unsafe { bindings::gpiod_request_config_free(self.config) } + } +} From patchwork Fri Jul 8 11:34:58 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 588762 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 04F5DC433EF for ; Fri, 8 Jul 2022 11:35:24 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237365AbiGHLfW (ORCPT ); Fri, 8 Jul 2022 07:35:22 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33626 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237499AbiGHLfV (ORCPT ); Fri, 8 Jul 2022 07:35:21 -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 0908A951D2 for ; Fri, 8 Jul 2022 04:35:19 -0700 (PDT) Received: by mail-pl1-x62f.google.com with SMTP id l12so10776913plk.13 for ; Fri, 08 Jul 2022 04:35:18 -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=ua8WVVBVbwvOiDb6cmEltYIfkj/SDJgEAU17pUygcsg=; b=LsmmFibcAHV0ggtyIm2RDpXXPFh248zn3HbGZE5WiAaSRE2v0OhrKFjD6g5pDFpOsQ VNSLLhpgP0JAJUSMEhR49UU0TURWOfRyKPuQwoK0HjBe+okv8XMvp/sDQuAWAXTyfT8c F+/nqFPonwbgBjAeGIaOvzu4k+N+KQFlw5NJn+mZuw0Chjfylf4An8CaxOjMAU9q5xSa BkEHdK6Z/yn7IyMKNpqn0tloRW5KdA7mW3IXNDs4OPcZnjyo06SUdt9ucDXfD3TzjHSI ayNV5BG+mtr2gMAPERdcWiYcRjH/KNs8+YW/YDc9IlVsVTUbvH2kp+Vn6bHL4Lqxvuka BnuA== 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=ua8WVVBVbwvOiDb6cmEltYIfkj/SDJgEAU17pUygcsg=; b=6MWB4OGD5BoYEovSkVSJ5b7ZpPbLMnCnR1hwQSu4Fw50Zm+VwOKe4HL6hYYBr48SK8 qbcb4Cm3Xclvq9x64UfBpce52B5na3Np53m6pr44toSaZy4+zm5+2+ltAQ++2QIxAuVf VCq/oYq+ZvcFc70gtgjR2WeBBHnePSyB83DA0FiX3lCJfZEjgJnaKpYTB7JYO1c9sXQi To6B0tZTG8lmKkAsQgSoy1fUIApuoDrhn4lTHL+dw8EJWACAzlTJCqvGky9U6n9TE4Ej VAU7Ce5ctLJUNVKoufXyFrRFntAgrEN2wpll5oKC1nsLoUUFBT4K/cyK9KxEMmHisnPx /RWg== X-Gm-Message-State: AJIora+fB98HFRtngaBsQqdcpao3rAszxFXWundkObVm0wbWrrMOX94E DtL+DRZPgyXcMckvnyRhFjurTQ== X-Google-Smtp-Source: AGRyM1uVuUT8OPf+8g9GA8t5jG3f63SkxXSMxje/1b3N1V+5X3lDOo/ppbGeNptngEubCHxKt+okrQ== X-Received: by 2002:a17:902:b7c9:b0:16c:354:a029 with SMTP id v9-20020a170902b7c900b0016c0354a029mr3196861plz.58.1657280118358; Fri, 08 Jul 2022 04:35:18 -0700 (PDT) Received: from localhost ([122.171.18.80]) by smtp.gmail.com with ESMTPSA id j18-20020a63e752000000b0040aeb7e9358sm26932459pgk.90.2022.07.08.04.35.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 08 Jul 2022 04:35:18 -0700 (PDT) From: Viresh Kumar To: Linus Walleij , Bartosz Golaszewski Cc: Viresh Kumar , Vincent Guittot , linux-gpio@vger.kernel.org, Kent Gibson , Miguel Ojeda , Wedson Almeida Filho , =?utf-8?q?Alex_Benn=C3=A9e?= , stratos-dev@op-lists.linaro.org, Gerard Ryan Subject: [PATCH V4 5/8] libgpiod: Add rust examples Date: Fri, 8 Jul 2022 17:04:58 +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/examples/gpiodetect.rs | 37 ++++++++++++ bindings/rust/examples/gpiofind.rs | 42 +++++++++++++ bindings/rust/examples/gpioget.rs | 42 +++++++++++++ bindings/rust/examples/gpioinfo.rs | 89 ++++++++++++++++++++++++++++ bindings/rust/examples/gpiomon.rs | 68 +++++++++++++++++++++ bindings/rust/examples/gpioset.rs | 52 ++++++++++++++++ 6 files changed, 330 insertions(+) create mode 100644 bindings/rust/examples/gpiodetect.rs create mode 100644 bindings/rust/examples/gpiofind.rs create mode 100644 bindings/rust/examples/gpioget.rs create mode 100644 bindings/rust/examples/gpioinfo.rs create mode 100644 bindings/rust/examples/gpiomon.rs create mode 100644 bindings/rust/examples/gpioset.rs diff --git a/bindings/rust/examples/gpiodetect.rs b/bindings/rust/examples/gpiodetect.rs new file mode 100644 index 000000000000..82307e4eecea --- /dev/null +++ b/bindings/rust/examples/gpiodetect.rs @@ -0,0 +1,37 @@ +// 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::fs; +use std::path::Path; + +use libgpiod::{gpiod_is_gpiochip_device, Chip}; + +fn main() { + let args: Vec = env::args().collect(); + if args.len() > 1 { + println!("Usage: {}", args[0]); + return; + } + + for entry in fs::read_dir(Path::new("/dev")).unwrap() { + let pathbuf = entry.unwrap().path(); + let path = pathbuf.to_str().unwrap(); + + if gpiod_is_gpiochip_device(path) { + let chip = Chip::open(path).unwrap(); + let ngpio = chip.get_num_lines(); + + println!( + "{} [{}] ({})", + chip.get_name().unwrap(), + chip.get_label().unwrap(), + ngpio + ); + } + } +} diff --git a/bindings/rust/examples/gpiofind.rs b/bindings/rust/examples/gpiofind.rs new file mode 100644 index 000000000000..bbbd7a87ece8 --- /dev/null +++ b/bindings/rust/examples/gpiofind.rs @@ -0,0 +1,42 @@ +// 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::fs; +use std::path::Path; + +use libgpiod::{gpiod_is_gpiochip_device, Chip}; + +fn main() { + let args: Vec = env::args().collect(); + if args.len() != 2 { + println!("Usage: {} ", args[0]); + return; + } + + for entry in fs::read_dir(Path::new("/dev")).unwrap() { + let pathbuf = entry.unwrap().path(); + let path = pathbuf.to_str().unwrap(); + + if gpiod_is_gpiochip_device(path) { + let chip = Chip::open(path).unwrap(); + + let offset = chip.find_line(&args[1]); + if offset.is_ok() { + println!( + "Line {} found: Chip: {}, offset: {}", + args[1], + chip.get_name().unwrap(), + offset.unwrap() + ); + return; + } + } + } + + println!("Failed to find line: {}", args[1]); +} diff --git a/bindings/rust/examples/gpioget.rs b/bindings/rust/examples/gpioget.rs new file mode 100644 index 000000000000..c3bc35fcfdb6 --- /dev/null +++ b/bindings/rust/examples/gpioget.rs @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar +// +// Simplified Rust implementation of gpioget tool. + +use std::env; + +use libgpiod::{Chip, Direction, LineConfig, RequestConfig}; + +fn main() { + let args: Vec = env::args().collect(); + if args.len() < 3 { + println!("Usage: {} ...", args[0]); + return; + } + + let mut config = LineConfig::new().unwrap(); + let mut offsets = Vec::::new(); + + for arg in &args[2..] { + let offset = arg.parse::().unwrap(); + + offsets.push(offset); + config.set_direction_override(Direction::Input, offset); + } + + let path = format!("/dev/gpiochip{}", args[1]); + let chip = Chip::open(&path).unwrap(); + + let rconfig = RequestConfig::new().unwrap(); + rconfig.set_consumer(&args[0]); + rconfig.set_offsets(&offsets); + + let request = chip.request_lines(&rconfig, &config).unwrap(); + + let mut values: Vec = vec![0; offsets.len()]; + request.get_values(&mut values).unwrap(); + + println!("{:?}", values); +} diff --git a/bindings/rust/examples/gpioinfo.rs b/bindings/rust/examples/gpioinfo.rs new file mode 100644 index 000000000000..bd30d9096ce8 --- /dev/null +++ b/bindings/rust/examples/gpioinfo.rs @@ -0,0 +1,89 @@ +// 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::fs; +use std::path::Path; + +use libgpiod::{gpiod_is_gpiochip_device, Chip, Direction}; + +fn line_info(chip: &Chip, offset: u32) { + let info = chip.line_info(offset).unwrap(); + let off = info.get_offset(); + + let name = match info.get_name() { + Ok(name) => name, + _ => "unused", + }; + + let consumer = match info.get_consumer() { + Ok(name) => name, + _ => "unnamed", + }; + + let low = if info.is_active_low() { + "active-low" + } else { + "active-high" + }; + + let dir = match info.get_direction().unwrap() { + Direction::AsIs => "None", + Direction::Input => "Input", + Direction::Output => "Output", + }; + + println!( + "\tline {:>3}\ + \t{:>10}\ + \t{:>10}\ + \t{:>6}\ + \t{:>14}", + off, name, consumer, dir, low + ); +} + +fn chip_info(path: &str) { + if gpiod_is_gpiochip_device(path) { + let chip = Chip::open(path).unwrap(); + let ngpio = chip.get_num_lines(); + + println!("GPIO Chip name: {}", chip.get_name().unwrap()); + println!("\tlabel: {}", chip.get_label().unwrap()); + println!("\tpath: {}", chip.get_path().unwrap()); + println!("\tngpio: {}\n", ngpio); + + println!("\tLine information:"); + + for offset in 0..ngpio { + line_info(&chip, offset); + } + println!("\n"); + } +} + +fn main() { + let args: Vec = env::args().collect(); + if args.len() > 2 { + println!("Usage: {}", args[0]); + return; + } + + if args.len() == 1 { + for entry in fs::read_dir(Path::new("/dev")).unwrap() { + let pathbuf = entry.unwrap().path(); + let path = pathbuf.to_str().unwrap(); + + chip_info(path); + } + } else { + let index = args[1].parse::().unwrap(); + let path = format!("/dev/gpiochip{}", index); + + chip_info(&path); + } +} diff --git a/bindings/rust/examples/gpiomon.rs b/bindings/rust/examples/gpiomon.rs new file mode 100644 index 000000000000..872907b386f3 --- /dev/null +++ b/bindings/rust/examples/gpiomon.rs @@ -0,0 +1,68 @@ +// 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 std::time::Duration; + +use libgpiod::{Chip, Edge, EdgeEventBuffer, Error, LineConfig, LineEdgeEvent, RequestConfig}; + +fn usage(name: &str) { + println!("Usage: {} ...", name); +} + +fn main() { + let args: Vec = env::args().collect(); + if args.len() < 3 { + usage(&args[0]); + return; + } + + let mut config = LineConfig::new().unwrap(); + let mut offsets = Vec::::new(); + + for arg in &args[2..] { + let offset = arg.parse::().unwrap(); + + offsets.push(offset); + } + + config.set_edge_detection_default(Edge::Both); + + let path = format!("/dev/gpiochip{}", args[1]); + let chip = Chip::open(&path).unwrap(); + + let rconfig = RequestConfig::new().unwrap(); + rconfig.set_offsets(&offsets); + + let buffer = EdgeEventBuffer::new(1).unwrap(); + let request = chip.request_lines(&rconfig, &config).unwrap(); + + loop { + match request.wait_edge_event(Duration::new(1, 0)) { + Err(Error::OperationTimedOut) => continue, + Err(x) => { + println!("{:?}", x); + return; + } + Ok(()) => (), + } + + let count = request.read_edge_event(&buffer, 1).unwrap(); + if count == 1 { + let event = buffer.get_event(0).unwrap(); + println!( + "line: {} type: {}, time: {:?}", + event.get_line_offset(), + match event.get_event_type().unwrap() { + LineEdgeEvent::Rising => "Rising", + LineEdgeEvent::Falling => "Falling", + }, + event.get_timestamp() + ); + } + } +} diff --git a/bindings/rust/examples/gpioset.rs b/bindings/rust/examples/gpioset.rs new file mode 100644 index 000000000000..ef70e8edbaae --- /dev/null +++ b/bindings/rust/examples/gpioset.rs @@ -0,0 +1,52 @@ +// 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 libgpiod::{Chip, Direction, LineConfig, RequestConfig}; + +fn usage(name: &str) { + println!("Usage: {} = ...", name); +} + +fn main() { + let args: Vec = env::args().collect(); + if args.len() < 3 { + usage(&args[0]); + return; + } + + let mut config = LineConfig::new().unwrap(); + let mut offsets = Vec::::new(); + let mut values = Vec::::new(); + + for arg in &args[2..] { + let pair: Vec<&str> = arg.split('=').collect(); + if pair.len() != 2 { + usage(&args[0]); + return; + } + + let offset = pair[0].parse::().unwrap(); + let value = pair[1].parse::().unwrap(); + + offsets.push(offset); + values.push(value as i32); + } + + config.set_direction_default(Direction::Output); + config.set_output_values(&offsets, &values).unwrap(); + + let path = format!("/dev/gpiochip{}", args[1]); + let chip = Chip::open(&path).unwrap(); + + let rconfig = RequestConfig::new().unwrap(); + rconfig.set_consumer(&args[0]); + rconfig.set_offsets(&offsets); + + chip.request_lines(&rconfig, &config).unwrap(); +} From patchwork Fri Jul 8 11:34:59 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 588761 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 0399FC43334 for ; Fri, 8 Jul 2022 11:35:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237760AbiGHLfY (ORCPT ); Fri, 8 Jul 2022 07:35:24 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33650 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237549AbiGHLfW (ORCPT ); Fri, 8 Jul 2022 07:35:22 -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 C1D98205C4 for ; Fri, 8 Jul 2022 04:35:21 -0700 (PDT) Received: by mail-pj1-x1029.google.com with SMTP id x18-20020a17090a8a9200b001ef83b332f5so1624264pjn.0 for ; Fri, 08 Jul 2022 04:35: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=u/136Pn2e5zcdxkV0/au4yNKOb6M10Nw+36EnmaP59I=; b=tOc3MnXI5f60usRQHJg1eGYA5hbgjOsk/pA0Mwbt/G9mXDGBS3n58i4lVrPBEfyoKY y/wmMbYuzbiO4ETHsxUkwKzKtem9NhfoTqSV25cksbSTBDWKUi3E+niiRgLGnWdMe90Z 2j/YFFkNmizrFa3QoKB7/og3Lp30PQ6huA7B0+0OxLT8KbOQ6HPT+W3jvXyyMeFH2rx6 spbDEitLP6A9HeugDZjb/2F4uuxXFlWTWi3h1ZPT16A6sseeZnaqIegsrUF6lKhHu0Bt VWH/bzvVKhzqNMbgWdHWf6ysrK2iD072fqnl7iNFxpMGaBvDrns70CbKF9yO9oQ+amsR 47+w== 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=u/136Pn2e5zcdxkV0/au4yNKOb6M10Nw+36EnmaP59I=; b=0R+bW96d3P7gPbDQZAft6nJKGdXrZ02QFxtEGzPJPePGRKlV5aC/Xp69X96xDo0QkF VB+y9lAXEnMMyVgylcV63/GaFPJ0XXbw2TierjS7h5dwAEoA5GyFFTmubpbFR60g+/96 BH9yq6KARHn9bTgQHZFXOtPCHcmddm5ykvZX02wa5FEHsbiYgBKjajQl4r+KgS5M1jl1 Zwhut82rUPq0peoxo3Y/zEgpn165Ed6ZXu3boBjplhFkCNZnTa2kF5g2UK/jLtYl6/Ng BXp1RlI06vVLI06p6vZIQbRUspIZ8mTMk2wCM1CbrNqHEU6KogdG4ah/LGaQBbJ81Mex p3iQ== X-Gm-Message-State: AJIora8WNW78abv4mbd2t8vdBO/xjb3Tq3rNYTydxO8cCV2xgLGXXG2H d4N7AmBdlF1gS9DcQXXPy7HOZw== X-Google-Smtp-Source: AGRyM1tQDV8RfGdkA4ml1/A33GCuF9Ry1zz09emg9HjiGZYLIyQvQD0FPAn+K6OIpKgS85NN68KTZA== X-Received: by 2002:a17:902:d707:b0:16b:e35e:abd4 with SMTP id w7-20020a170902d70700b0016be35eabd4mr3240728ply.111.1657280120771; Fri, 08 Jul 2022 04:35:20 -0700 (PDT) Received: from localhost ([122.171.18.80]) by smtp.gmail.com with ESMTPSA id i11-20020a1709026acb00b001640aad2f71sm29612722plt.180.2022.07.08.04.35.20 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 08 Jul 2022 04:35:20 -0700 (PDT) From: Viresh Kumar To: Linus Walleij , Bartosz Golaszewski Cc: Viresh Kumar , Vincent Guittot , linux-gpio@vger.kernel.org, Kent Gibson , Miguel Ojeda , Wedson Almeida Filho , =?utf-8?q?Alex_Benn=C3=A9e?= , stratos-dev@op-lists.linaro.org, Gerard Ryan Subject: [PATCH V4 6/8] libgpiod: Derive debug traits for few definitions Date: Fri, 8 Jul 2022 17:04:59 +0530 Message-Id: <490e4efc900d8173fb3e2f1373c97e1a20cb9ac3.1657279685.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 These are required for tests, which will be added by a later commit. Signed-off-by: Viresh Kumar --- bindings/rust/src/chip.rs | 5 +++++ bindings/rust/src/chip_info.rs | 1 + bindings/rust/src/lib.rs | 8 +++++++- bindings/rust/src/line_info.rs | 1 + 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/bindings/rust/src/chip.rs b/bindings/rust/src/chip.rs index 50b5d6102f96..ecff4b003cd9 100644 --- a/bindings/rust/src/chip.rs +++ b/bindings/rust/src/chip.rs @@ -21,6 +21,7 @@ use super::{ /// character device. It exposes basic information about the chip and allows /// callers to retrieve information about each line, watch lines for state /// changes and make line requests. +#[derive(Debug)] pub(crate) struct ChipInternal { chip: *mut bindings::gpiod_chip, } @@ -52,11 +53,15 @@ impl Drop for ChipInternal { } } +#[derive(Debug)] pub struct Chip { ichip: Arc, info: ChipInfo, } +unsafe impl Send for Chip {} +unsafe impl Sync for Chip {} + impl Chip { /// Find a chip by path. pub fn open(path: &str) -> Result { diff --git a/bindings/rust/src/chip_info.rs b/bindings/rust/src/chip_info.rs index 950368b54c6f..7188f91a92a6 100644 --- a/bindings/rust/src/chip_info.rs +++ b/bindings/rust/src/chip_info.rs @@ -11,6 +11,7 @@ use vmm_sys_util::errno::Error as IoError; use super::{bindings, ChipInternal, Error, Result}; /// GPIO chip Information +#[derive(Debug)] pub struct ChipInfo { info: *mut bindings::gpiod_chip_info, } diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs index 2f2ac515d353..63b0b82281b7 100644 --- a/bindings/rust/src/lib.rs +++ b/bindings/rust/src/lib.rs @@ -59,6 +59,7 @@ pub enum Error { } /// Direction settings. +#[derive(Debug, PartialEq)] pub enum Direction { /// Request the line(s), but don't change direction. AsIs, @@ -88,6 +89,7 @@ impl Direction { } /// Internal bias settings. +#[derive(Debug, PartialEq)] pub enum Bias { /// Don't change the bias setting when applying line config. AsIs, @@ -125,6 +127,7 @@ impl Bias { } /// Drive settings. +#[derive(Debug, PartialEq)] pub enum Drive { /// Drive setting is push-pull. PushPull, @@ -154,6 +157,7 @@ impl Drive { } /// Edge detection settings. +#[derive(Debug, PartialEq)] pub enum Edge { /// Line edge detection is disabled. None, @@ -223,6 +227,7 @@ impl Config { } /// Event clock settings. +#[derive(Debug, PartialEq)] pub enum EventClock { /// Line uses the monotonic clock for edge event timestamps. Monotonic, @@ -248,6 +253,7 @@ impl EventClock { } /// Line status change event types. +#[derive(Debug, PartialEq)] pub enum Event { /// Line has been requested. LineRequested, @@ -268,7 +274,7 @@ impl Event { } } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug, PartialEq)] /// Edge event types. pub enum LineEdgeEvent { /// Rising edge event. diff --git a/bindings/rust/src/line_info.rs b/bindings/rust/src/line_info.rs index 70b6bd6a84bb..426bd16aa616 100644 --- a/bindings/rust/src/line_info.rs +++ b/bindings/rust/src/line_info.rs @@ -23,6 +23,7 @@ use super::{ /// line, which does not include the line value. The line must be requested /// to access the line value. +#[derive(Debug)] pub struct LineInfo { info: *mut bindings::gpiod_line_info, ichip: Option>, From patchwork Fri Jul 8 11:35:00 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 589020 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 CD094CCA47B for ; Fri, 8 Jul 2022 11:35:28 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237731AbiGHLf2 (ORCPT ); Fri, 8 Jul 2022 07:35:28 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33698 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237768AbiGHLf0 (ORCPT ); Fri, 8 Jul 2022 07:35:26 -0400 Received: from mail-pl1-x62d.google.com (mail-pl1-x62d.google.com [IPv6:2607:f8b0:4864:20::62d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 363D2205C4 for ; Fri, 8 Jul 2022 04:35:24 -0700 (PDT) Received: by mail-pl1-x62d.google.com with SMTP id y18so8119180plb.2 for ; Fri, 08 Jul 2022 04:35:24 -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=Ds8Lr8b/1Qf9J5alzfmtH/Gy059q/JeOaHSsXt46Eqs=; b=RGt6g1a5BGL2szpVANc4TsirvYABK6MsSnpEfqL9s2c8XAeHEkmQamVpUrRadNy6Ja zwtS5GN6t0DiPLLVPWapD1q/Bbj/Oeqe2lyTOoKSUfGteliIDoVXPxEVN8Fz8yI4ykib flamQ73iCJLZbccIoE9oVlBg1C1qJ3DOqGcp/Tsq6hSAG6Gl1hYU+jlJB/CLvv/hLKgq M88Rffqs5wojDaJERuiScBb6U1cVbm7iqtWY5iABEqfCTXIhv4BiMMWEWgR86de+ckvJ KNOpb5rSryui7t2wWIrmGgHgl01PXKS2LzNROifudnHWo+QzFuF/CTkOHcCz+GFuTw5k 7a7g== 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=Ds8Lr8b/1Qf9J5alzfmtH/Gy059q/JeOaHSsXt46Eqs=; b=AgqOArewZkQ2pwu62w4iK45t/PgWOFUn/biyQ0hVevdXj+UrYalrSPsWHECecdxthb p7Swy51D7LBT40TC50irMsYbNU+dqjeeQ7LagL5lkZUaUU4bcj7XRTvcsz2TY3+uspw9 t7ajZgquHUh74JLH/qqUZDj6oiREOLTEUwbmDM0tdu6aOjNIE6EN+sj9k6m7p8+oGz13 vCCbONGXdoz/u9mT8tgAiAbcA39ES/AUwuVUAxuAUrvD9KQICM+GWe95isgVK96rWLuI h9AJzFC+0iEn5Nah+V10YL/F8T0qyXGdR61/+DAVrrYoUlHQNoqqRjgovNBhEnTXc3el iXOA== X-Gm-Message-State: AJIora869WwlfZKFckJkeImcYRMo0b/zwqyMtDDtUXx1BtO4jxAKdoDe x3hsHswZoTRo24cQDWO/75kemQ== X-Google-Smtp-Source: AGRyM1ub5jTIdESfbqnpH6vQRkPROXlvW0o47FKopTfCTGnYLBTEJ5puhKwcMmI0y/dqJqnI1M31ow== X-Received: by 2002:a17:903:3093:b0:16b:deea:4d36 with SMTP id u19-20020a170903309300b0016bdeea4d36mr3208308plc.126.1657280123409; Fri, 08 Jul 2022 04:35:23 -0700 (PDT) Received: from localhost ([122.171.18.80]) by smtp.gmail.com with ESMTPSA id u13-20020a62790d000000b0052896629f66sm3504270pfc.208.2022.07.08.04.35.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 08 Jul 2022 04:35:23 -0700 (PDT) From: Viresh Kumar To: Linus Walleij , Bartosz Golaszewski Cc: Viresh Kumar , Vincent Guittot , linux-gpio@vger.kernel.org, Kent Gibson , Miguel Ojeda , Wedson Almeida Filho , =?utf-8?q?Alex_Benn=C3=A9e?= , stratos-dev@op-lists.linaro.org, Gerard Ryan Subject: [PATCH V4 7/8] libgpiod: Add rust tests Date: Fri, 8 Jul 2022 17:05:00 +0530 Message-Id: <78b3ee21dec2a66003c2eae2800e9699a8ecd180.1657279685.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. This is how "cargo test" prints results: Running tests/chip.rs (target/debug/deps/chip-b19008e6b9a10d2f) running 6 tests test chip::create::nonexistent_file_failure ... ok test chip::create::no_dev_file_failure ... ok test chip::create::non_gpio_char_dev_file_failure ... ok test chip::create::existing ... ok test chip::configure::line_lookup ... ok test chip::configure::verify ... ok test result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.14s Running tests/edge_event.rs (target/debug/deps/edge_event-5897a36c91efe0d4) running 11 tests test edge_event::buffer_settings::default_capacity ... ok test edge_event::buffer_settings::user_defined_capacity ... ok test edge_event::buffer_settings::max_capacity ... ok test edge_event::failure::dir_out_edge_failure ... ok test edge_event::failure::wait_timeout ... ok test edge_event::verify::both_edges ... ok test edge_event::verify::multiple_events ... ok test edge_event::verify::edge_sequence ... ok test edge_event::verify::falling_edge ... ok test edge_event::verify::over_capacity ... ok test edge_event::verify::rising_edge ... ok test result: ok. 11 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.98s Running tests/info_event.rs (target/debug/deps/info_event-707c734e0820381c) running 3 tests test info_event::watch::verify ... ok test info_event::watch::failure ... ok test info_event::watch::reconfigure ... ok test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.26s Running tests/line_config.rs (target/debug/deps/line_config-d376966750bf38a0) running 9 tests test line_config::overrides::active_low ... ok test line_config::overrides::bias ... ok test line_config::default::verify ... ok test line_config::overrides::debounce_period ... ok test line_config::overrides::direction ... ok test line_config::overrides::drive ... ok test line_config::overrides::edge_detection ... ok test line_config::overrides::event_clock ... ok test line_config::overrides::output_value ... ok test result: ok. 9 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.06s Running tests/line_info.rs (target/debug/deps/line_info-5069196cfaa74ab7) running 2 tests [40570.967204] gpio-1992 (hog): hogged as output/high [40570.974774] gpio-1956 (hog4): hogged as output/low [40570.975314] gpio-1955 (hog3): hogged as output/high test line_info::properties::verify ... ok test line_info::basic::verify ... ok test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.31s Running tests/line_request.rs (target/debug/deps/line_request-914b9fd8c84e2c4b) running 7 tests test line_request::invalid_arguments::no_offsets ... ok test line_request::verify::read_values ... ok test line_request::verify::set_bias ... ok test line_request::verify::reconfigure_output_values ... ok test line_request::verify::empty_consumer ... ok test line_request::verify::set_output_values ... ok test line_request::verify::custom_consumer ... ok test result: ok. 7 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.48s Running tests/request_config.rs (target/debug/deps/request_config-99e366517cc0feda) running 2 tests test request_config::verify::default ... ok test request_config::verify::initialized ... ok test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.03s Doc-tests libgpiod running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s Signed-off-by: Viresh Kumar --- bindings/rust/Cargo.toml | 3 + bindings/rust/src/line_request.rs | 1 + bindings/rust/tests/chip.rs | 96 +++++++ bindings/rust/tests/common/config.rs | 117 ++++++++ bindings/rust/tests/common/mod.rs | 16 ++ bindings/rust/tests/common/sim.rs | 306 ++++++++++++++++++++ bindings/rust/tests/edge_event.rs | 389 ++++++++++++++++++++++++++ bindings/rust/tests/info_event.rs | 126 +++++++++ bindings/rust/tests/line_config.rs | 187 +++++++++++++ bindings/rust/tests/line_info.rs | 90 ++++++ bindings/rust/tests/line_request.rs | 234 ++++++++++++++++ bindings/rust/tests/request_config.rs | 42 +++ 12 files changed, 1607 insertions(+) create mode 100644 bindings/rust/tests/chip.rs create mode 100644 bindings/rust/tests/common/config.rs create mode 100644 bindings/rust/tests/common/mod.rs create mode 100644 bindings/rust/tests/common/sim.rs create mode 100644 bindings/rust/tests/edge_event.rs create mode 100644 bindings/rust/tests/info_event.rs create mode 100644 bindings/rust/tests/line_config.rs create mode 100644 bindings/rust/tests/line_info.rs create mode 100644 bindings/rust/tests/line_request.rs create mode 100644 bindings/rust/tests/request_config.rs diff --git a/bindings/rust/Cargo.toml b/bindings/rust/Cargo.toml index d5d81486fa2f..e2d6d5bb91b6 100644 --- a/bindings/rust/Cargo.toml +++ b/bindings/rust/Cargo.toml @@ -10,3 +10,6 @@ libc = ">=0.2.39" libgpiod-sys = { path = "libgpiod-sys" } thiserror = "1.0" vmm-sys-util = "=0.9.0" + +[dev-dependencies] +libgpiod-sys = { path = "libgpiod-sys", features = ["gpiosim"] } diff --git a/bindings/rust/src/line_request.rs b/bindings/rust/src/line_request.rs index bb338e72671d..c1dbbb397e73 100644 --- a/bindings/rust/src/line_request.rs +++ b/bindings/rust/src/line_request.rs @@ -15,6 +15,7 @@ use super::{bindings, ChipInternal, EdgeEventBuffer, Error, LineConfig, RequestC /// Line request operations /// /// Allows interaction with a set of requested lines. +#[derive(Debug)] pub struct LineRequest { request: *mut bindings::gpiod_line_request, } diff --git a/bindings/rust/tests/chip.rs b/bindings/rust/tests/chip.rs new file mode 100644 index 000000000000..4e64e9c7e291 --- /dev/null +++ b/bindings/rust/tests/chip.rs @@ -0,0 +1,96 @@ +// 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 vmm_sys_util::errno::Error as IoError; + + use crate::common::*; + use libgpiod::{Chip, Error as ChipError}; + + mod create { + use super::*; + + #[test] + fn nonexistent_file_failure() { + assert_eq!( + Chip::open("/dev/nonexistent").unwrap_err(), + ChipError::OperationFailed("Gpio Chip open", IoError::new(ENOENT)) + ); + } + + #[test] + fn no_dev_file_failure() { + assert_eq!( + Chip::open("/tmp").unwrap_err(), + ChipError::OperationFailed("Gpio Chip open", IoError::new(ENOTTY)) + ); + } + + #[test] + fn non_gpio_char_dev_file_failure() { + assert_eq!( + Chip::open("/dev/null").unwrap_err(), + ChipError::OperationFailed("Gpio Chip open", IoError::new(ENODEV)) + ); + } + + #[test] + fn existing() { + let sim = Sim::new(None, None, true).unwrap(); + Chip::open(sim.dev_path()).unwrap(); + } + } + + mod configure { + use super::*; + const NGPIO: u64 = 16; + const LABEL: &str = "foobar"; + + #[test] + fn verify() { + let sim = Sim::new(Some(NGPIO), Some(LABEL), true).unwrap(); + let chip = Chip::open(sim.dev_path()).unwrap(); + + assert_eq!(chip.get_label().unwrap(), LABEL); + assert_eq!(chip.get_name().unwrap(), sim.chip_name()); + assert_eq!(chip.get_path().unwrap(), sim.dev_path()); + assert_eq!(chip.get_num_lines(), NGPIO as u32); + chip.get_fd().unwrap(); + } + + #[test] + fn line_lookup() { + let sim = Sim::new(Some(NGPIO), None, false).unwrap(); + sim.set_line_name(0, "zero").unwrap(); + sim.set_line_name(2, "two").unwrap(); + sim.set_line_name(3, "three").unwrap(); + sim.set_line_name(5, "five").unwrap(); + sim.set_line_name(10, "ten").unwrap(); + sim.set_line_name(11, "ten").unwrap(); + sim.enable().unwrap(); + + let chip = Chip::open(sim.dev_path()).unwrap(); + + // Success case + assert_eq!(chip.find_line("zero").unwrap(), 0); + assert_eq!(chip.find_line("two").unwrap(), 2); + assert_eq!(chip.find_line("three").unwrap(), 3); + assert_eq!(chip.find_line("five").unwrap(), 5); + + // Success with duplicate names, should return first entry + assert_eq!(chip.find_line("ten").unwrap(), 10); + + // Failure + assert_eq!( + chip.find_line("nonexistent").unwrap_err(), + ChipError::OperationFailed("Gpio Chip find-line", IoError::new(ENOENT)) + ); + } + } +} diff --git a/bindings/rust/tests/common/config.rs b/bindings/rust/tests/common/config.rs new file mode 100644 index 000000000000..3abd9a8c4c8b --- /dev/null +++ b/bindings/rust/tests/common/config.rs @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use std::sync::Arc; + +use crate::common::*; + +use libgpiod::{Bias, Chip, Direction, Edge, LineConfig, LineRequest, RequestConfig, Result}; + +//#[derive(Debug)] +pub(crate) struct TestConfig { + sim: Arc, + chip: Option, + request: Option, + rconfig: RequestConfig, + lconfig: LineConfig, +} + +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: RequestConfig::new().unwrap(), + lconfig: LineConfig::new().unwrap(), + }) + } + + pub(crate) fn set_pull(&self, offsets: &[u32], pulls: &[u32]) { + for i in 0..pulls.len() { + self.sim.set_pull(offsets[i], pulls[i] as i32).unwrap(); + } + } + + pub(crate) fn rconfig_consumer(&self, offsets: Option<&[u32]>, consumer: Option<&str>) { + if let Some(offsets) = offsets { + self.rconfig.set_offsets(offsets); + } + + if let Some(consumer) = consumer { + self.rconfig.set_consumer(consumer); + } + } + + pub(crate) fn rconfig(&self, offsets: Option<&[u32]>) { + self.rconfig_consumer(offsets, None); + } + + pub(crate) fn lconfig( + &mut self, + dir: Option, + val: Option, + val_override: Option<(u32, u32)>, + edge: Option, + bias: Option, + ) { + if let Some(bias) = bias { + self.lconfig.set_bias_default(bias); + } + + if let Some(edge) = edge { + self.lconfig.set_edge_detection_default(edge); + } + + if let Some(dir) = dir { + self.lconfig.set_direction_default(dir); + } + + if let Some(val) = val { + self.lconfig.set_output_value_default(val); + } + + if let Some((offset, val)) = val_override { + self.lconfig.set_output_value_override(val, offset); + } + } + + pub(crate) fn lconfig_raw(&mut self) { + self.lconfig(None, None, None, None, None); + } + + pub(crate) fn lconfig_edge(&mut self, edge: Option) { + self.lconfig(None, None, None, edge, None); + } + + pub(crate) fn request_lines(&mut self) -> Result<()> { + let chip = Chip::open(self.sim.dev_path())?; + + self.request = Some(chip.request_lines(&self.rconfig, &self.lconfig)?); + self.chip = Some(chip); + + Ok(()) + } + + pub(crate) fn sim(&self) -> Arc { + self.sim.clone() + } + + pub(crate) fn chip(&self) -> &Chip { + &self.chip.as_ref().unwrap() + } + + pub(crate) fn request(&self) -> &LineRequest { + &self.request.as_ref().unwrap() + } +} + +impl Drop for TestConfig { + fn drop(&mut self) { + // Explicit freeing is important to make sure "request" get freed + // before "sim" and "chip". + self.request = None; + } +} diff --git a/bindings/rust/tests/common/mod.rs b/bindings/rust/tests/common/mod.rs new file mode 100644 index 000000000000..2dc37986396b --- /dev/null +++ b/bindings/rust/tests/common/mod.rs @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +#[allow(dead_code)] +mod sim; + +#[allow(unused_imports)] +pub(crate) use sim::*; + +#[allow(dead_code)] +mod config; + +#[allow(unused_imports)] +pub(crate) use config::*; diff --git a/bindings/rust/tests/common/sim.rs b/bindings/rust/tests/common/sim.rs new file mode 100644 index 000000000000..cd5ec66c3da5 --- /dev/null +++ b/bindings/rust/tests/common/sim.rs @@ -0,0 +1,306 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use std::os::raw::c_char; +use std::{slice, str}; + +use vmm_sys_util::errno::Error as IoError; + +use libgpiod::{Error, Result}; +use libgpiod_sys as bindings; + +/// Sim Ctx +#[derive(Debug)] +struct SimCtx { + ctx: *mut bindings::gpiosim_ctx, +} + +unsafe impl Send for SimCtx {} +unsafe impl Sync for SimCtx {} + +impl SimCtx { + fn new() -> Result { + let ctx = unsafe { bindings::gpiosim_ctx_new() }; + if ctx.is_null() { + return Err(Error::OperationFailed("gpio-sim ctx new", IoError::last())); + } + + Ok(Self { ctx }) + } + + fn ctx(&self) -> *mut bindings::gpiosim_ctx { + self.ctx + } +} + +impl Drop for SimCtx { + fn drop(&mut self) { + unsafe { bindings::gpiosim_ctx_unref(self.ctx) } + } +} + +/// Sim Dev +#[derive(Debug)] +struct SimDev { + dev: *mut bindings::gpiosim_dev, +} + +unsafe impl Send for SimDev {} +unsafe impl Sync for SimDev {} + +impl SimDev { + fn new(ctx: &SimCtx) -> Result { + let dev = unsafe { bindings::gpiosim_dev_new(ctx.ctx()) }; + if dev.is_null() { + return Err(Error::OperationFailed("gpio-sim dev new", IoError::last())); + } + + Ok(Self { dev }) + } + + fn dev(&self) -> *mut bindings::gpiosim_dev { + self.dev + } + + fn enable(&self) -> Result<()> { + let ret = unsafe { bindings::gpiosim_dev_enable(self.dev) }; + + if ret == -1 { + Err(Error::OperationFailed( + "gpio-sim dev-enable", + IoError::last(), + )) + } else { + Ok(()) + } + } + + fn disable(&self) -> Result<()> { + let ret = unsafe { bindings::gpiosim_dev_disable(self.dev) }; + + if ret == -1 { + Err(Error::OperationFailed( + "gpio-sim dev-disable", + IoError::last(), + )) + } else { + Ok(()) + } + } +} + +impl Drop for SimDev { + fn drop(&mut self) { + unsafe { bindings::gpiosim_dev_unref(self.dev) } + } +} + +/// Sim Bank +#[derive(Debug)] +struct SimBank { + bank: *mut bindings::gpiosim_bank, +} + +unsafe impl Send for SimBank {} +unsafe impl Sync for SimBank {} + +impl SimBank { + fn new(dev: &SimDev) -> Result { + let bank = unsafe { bindings::gpiosim_bank_new(dev.dev()) }; + if bank.is_null() { + return Err(Error::OperationFailed("gpio-sim Bank new", IoError::last())); + } + + Ok(Self { bank }) + } + + fn chip_name(&self) -> Result<&str> { + // SAFETY: The string returned by gpiosim is guaranteed to live as long + // as the `struct SimBank`. + let name = unsafe { bindings::gpiosim_bank_get_chip_name(self.bank) }; + + // SAFETY: The string is guaranteed to be valid here. + str::from_utf8(unsafe { + slice::from_raw_parts(name as *const u8, bindings::strlen(name) as usize) + }) + .map_err(Error::InvalidString) + } + + fn dev_path(&self) -> Result<&str> { + // SAFETY: The string returned by gpiosim is guaranteed to live as long + // as the `struct SimBank`. + let path = unsafe { bindings::gpiosim_bank_get_dev_path(self.bank) }; + + // SAFETY: The string is guaranteed to be valid here. + str::from_utf8(unsafe { + slice::from_raw_parts(path as *const u8, bindings::strlen(path) as usize) + }) + .map_err(Error::InvalidString) + } + + fn val(&self, offset: u32) -> Result { + let ret = unsafe { bindings::gpiosim_bank_get_value(self.bank, offset) }; + + if ret == -1 { + Err(Error::OperationFailed( + "gpio-sim get-value", + IoError::last(), + )) + } else { + Ok(ret as u32) + } + } + + fn set_label(&self, label: &str) -> Result<()> { + // Null-terminate the string + let label = label.to_owned() + "\0"; + + let ret = + unsafe { bindings::gpiosim_bank_set_label(self.bank, label.as_ptr() as *const c_char) }; + + if ret == -1 { + Err(Error::OperationFailed( + "gpio-sim set-label", + IoError::last(), + )) + } else { + Ok(()) + } + } + + fn set_num_lines(&self, num: u64) -> Result<()> { + let ret = unsafe { bindings::gpiosim_bank_set_num_lines(self.bank, num) }; + if ret == -1 { + Err(Error::OperationFailed( + "gpio-sim set-num-lines", + IoError::last(), + )) + } else { + Ok(()) + } + } + + fn set_line_name(&self, offset: u32, name: &str) -> Result<()> { + // Null-terminate the string + let name = name.to_owned() + "\0"; + + let ret = unsafe { + bindings::gpiosim_bank_set_line_name(self.bank, offset, name.as_ptr() as *const c_char) + }; + + if ret == -1 { + Err(Error::OperationFailed( + "gpio-sim set-line-name", + IoError::last(), + )) + } else { + Ok(()) + } + } + + fn set_pull(&self, offset: u32, pull: i32) -> Result<()> { + let ret = unsafe { bindings::gpiosim_bank_set_pull(self.bank, offset, pull) }; + + if ret == -1 { + Err(Error::OperationFailed("gpio-sim set-pull", IoError::last())) + } else { + Ok(()) + } + } + + fn hog_line(&self, offset: u32, name: &str, dir: i32) -> Result<()> { + // Null-terminate the string + let name = name.to_owned() + "\0"; + + let ret = unsafe { + bindings::gpiosim_bank_hog_line(self.bank, offset, name.as_ptr() as *const c_char, dir) + }; + + if ret == -1 { + Err(Error::OperationFailed("gpio-sim hog-line", IoError::last())) + } else { + Ok(()) + } + } +} + +impl Drop for SimBank { + fn drop(&mut self) { + unsafe { bindings::gpiosim_bank_unref(self.bank) } + } +} + +/// GPIO SIM +#[derive(Debug)] +pub(crate) struct Sim { + ctx: SimCtx, + dev: SimDev, + bank: SimBank, +} + +unsafe impl Send for Sim {} +unsafe impl Sync for Sim {} + +impl Sim { + pub(crate) fn new(ngpio: Option, 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(crate) fn chip_name(&self) -> &str { + self.bank.chip_name().unwrap() + } + + pub fn dev_path(&self) -> &str { + self.bank.dev_path().unwrap() + } + + pub(crate) fn val(&self, offset: u32) -> Result { + self.bank.val(offset) + } + + pub(crate) fn set_label(&self, label: &str) -> Result<()> { + self.bank.set_label(label) + } + + pub(crate) fn set_num_lines(&self, num: u64) -> Result<()> { + self.bank.set_num_lines(num) + } + + pub(crate) fn set_line_name(&self, offset: u32, name: &str) -> Result<()> { + self.bank.set_line_name(offset, name) + } + + pub(crate) fn set_pull(&self, offset: u32, pull: i32) -> Result<()> { + self.bank.set_pull(offset, pull) + } + + pub(crate) fn hog_line(&self, offset: u32, name: &str, dir: i32) -> Result<()> { + self.bank.hog_line(offset, name, dir) + } + + pub(crate) fn enable(&self) -> Result<()> { + self.dev.enable() + } + + pub(crate) fn disable(&self) -> Result<()> { + self.dev.disable() + } +} diff --git a/bindings/rust/tests/edge_event.rs b/bindings/rust/tests/edge_event.rs new file mode 100644 index 000000000000..1b05b225aab7 --- /dev/null +++ b/bindings/rust/tests/edge_event.rs @@ -0,0 +1,389 @@ +// 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 libc::EINVAL; + use std::sync::Arc; + use std::thread::{sleep, spawn}; + use std::time::Duration; + + use vmm_sys_util::errno::Error as IoError; + + use crate::common::*; + use libgpiod::{Direction, Edge, EdgeEventBuffer, Error as ChipError, LineEdgeEvent}; + use libgpiod_sys::{GPIOSIM_PULL_DOWN, GPIOSIM_PULL_UP}; + + const NGPIO: u64 = 8; + + mod buffer_settings { + use super::*; + + #[test] + fn default_capacity() { + assert_eq!(EdgeEventBuffer::new(0).unwrap().get_capacity(), 64); + } + + #[test] + fn user_defined_capacity() { + assert_eq!(EdgeEventBuffer::new(123).unwrap().get_capacity(), 123); + } + + #[test] + fn max_capacity() { + assert_eq!(EdgeEventBuffer::new(1024 * 2).unwrap().get_capacity(), 1024); + } + } + + mod failure { + use super::*; + + #[test] + fn wait_timeout() { + let mut config = TestConfig::new(NGPIO).unwrap(); + config.rconfig(Some(&[0])); + config.lconfig_edge(Some(Edge::Both)); + config.request_lines().unwrap(); + + // No events available + assert_eq!( + config + .request() + .wait_edge_event(Duration::from_millis(100)) + .unwrap_err(), + ChipError::OperationTimedOut + ); + } + + #[test] + fn dir_out_edge_failure() { + let mut config = TestConfig::new(NGPIO).unwrap(); + config.rconfig(Some(&[0])); + config.lconfig(Some(Direction::Output), None, None, Some(Edge::Both), None); + + assert_eq!( + config.request_lines().unwrap_err(), + ChipError::OperationFailed("Gpio LineRequest request-lines", IoError::new(EINVAL)) + ); + } + } + + mod verify { + use super::*; + + // Helpers to generate events + fn trigger_falling_and_rising_edge(sim: Arc, offset: u32) { + spawn(move || { + sleep(Duration::from_millis(30)); + sim.set_pull(offset, GPIOSIM_PULL_UP as i32).unwrap(); + + sleep(Duration::from_millis(30)); + sim.set_pull(offset, GPIOSIM_PULL_DOWN as i32).unwrap(); + }); + } + + fn trigger_rising_edge_events_on_two_offsets(sim: Arc, offset: [u32; 2]) { + spawn(move || { + sleep(Duration::from_millis(30)); + sim.set_pull(offset[0], GPIOSIM_PULL_UP as i32).unwrap(); + + sleep(Duration::from_millis(30)); + sim.set_pull(offset[1], GPIOSIM_PULL_UP as i32).unwrap(); + }); + } + + fn trigger_multiple_events(sim: Arc, offset: u32) { + sim.set_pull(offset, GPIOSIM_PULL_UP as i32).unwrap(); + sleep(Duration::from_millis(10)); + + sim.set_pull(offset, GPIOSIM_PULL_DOWN as i32).unwrap(); + sleep(Duration::from_millis(10)); + + sim.set_pull(offset, GPIOSIM_PULL_UP as i32).unwrap(); + sleep(Duration::from_millis(10)); + } + + #[test] + fn both_edges() { + const GPIO: u32 = 2; + let buf = EdgeEventBuffer::new(0).unwrap(); + let mut config = TestConfig::new(NGPIO).unwrap(); + config.rconfig(Some(&[GPIO])); + config.lconfig_edge(Some(Edge::Both)); + config.request_lines().unwrap(); + + // Generate events + trigger_falling_and_rising_edge(config.sim(), GPIO); + + // Rising event + config + .request() + .wait_edge_event(Duration::from_secs(1)) + .unwrap(); + + assert_eq!( + config + .request() + .read_edge_event(&buf, buf.get_capacity()) + .unwrap(), + 1 + ); + assert_eq!(buf.get_num_events(), 1); + + let event = buf.get_event(0).unwrap(); + let ts_rising = event.get_timestamp(); + assert_eq!(event.get_event_type().unwrap(), LineEdgeEvent::Rising); + assert_eq!(event.get_line_offset(), GPIO); + + // Falling event + config + .request() + .wait_edge_event(Duration::from_secs(1)) + .unwrap(); + + assert_eq!( + config + .request() + .read_edge_event(&buf, buf.get_capacity()) + .unwrap(), + 1 + ); + assert_eq!(buf.get_num_events(), 1); + + let event = buf.get_event(0).unwrap(); + let ts_falling = event.get_timestamp(); + assert_eq!(event.get_event_type().unwrap(), LineEdgeEvent::Falling); + assert_eq!(event.get_line_offset(), GPIO); + + // No events available + assert_eq!( + config + .request() + .wait_edge_event(Duration::from_millis(100)) + .unwrap_err(), + ChipError::OperationTimedOut + ); + + assert!(ts_falling > ts_rising); + } + + #[test] + fn rising_edge() { + const GPIO: u32 = 6; + let buf = EdgeEventBuffer::new(0).unwrap(); + let mut config = TestConfig::new(NGPIO).unwrap(); + config.rconfig(Some(&[GPIO])); + config.lconfig_edge(Some(Edge::Rising)); + config.request_lines().unwrap(); + + // Generate events + trigger_falling_and_rising_edge(config.sim(), GPIO); + + // Rising event + config + .request() + .wait_edge_event(Duration::from_secs(1)) + .unwrap(); + + assert_eq!( + config + .request() + .read_edge_event(&buf, buf.get_capacity()) + .unwrap(), + 1 + ); + assert_eq!(buf.get_num_events(), 1); + + let event = buf.get_event(0).unwrap(); + assert_eq!(event.get_event_type().unwrap(), LineEdgeEvent::Rising); + assert_eq!(event.get_line_offset(), GPIO); + + // No events available + assert_eq!( + config + .request() + .wait_edge_event(Duration::from_millis(100)) + .unwrap_err(), + ChipError::OperationTimedOut + ); + } + + #[test] + fn falling_edge() { + const GPIO: u32 = 7; + let buf = EdgeEventBuffer::new(0).unwrap(); + let mut config = TestConfig::new(NGPIO).unwrap(); + config.rconfig(Some(&[GPIO])); + config.lconfig_edge(Some(Edge::Falling)); + config.request_lines().unwrap(); + + // Generate events + trigger_falling_and_rising_edge(config.sim(), GPIO); + + // Falling event + config + .request() + .wait_edge_event(Duration::from_secs(1)) + .unwrap(); + + assert_eq!( + config + .request() + .read_edge_event(&buf, buf.get_capacity()) + .unwrap(), + 1 + ); + assert_eq!(buf.get_num_events(), 1); + + let event = buf.get_event(0).unwrap(); + assert_eq!(event.get_event_type().unwrap(), LineEdgeEvent::Falling); + assert_eq!(event.get_line_offset(), GPIO); + + // No events available + assert_eq!( + config + .request() + .wait_edge_event(Duration::from_millis(100)) + .unwrap_err(), + ChipError::OperationTimedOut + ); + } + + #[test] + fn edge_sequence() { + const GPIO: [u32; 2] = [0, 1]; + let mut config = TestConfig::new(NGPIO).unwrap(); + config.rconfig(Some(&GPIO)); + config.lconfig_edge(Some(Edge::Both)); + config.request_lines().unwrap(); + + // Generate events + trigger_rising_edge_events_on_two_offsets(config.sim(), GPIO); + + // Rising event GPIO 0 + let buf = EdgeEventBuffer::new(0).unwrap(); + config + .request() + .wait_edge_event(Duration::from_secs(1)) + .unwrap(); + + assert_eq!( + config + .request() + .read_edge_event(&buf, buf.get_capacity()) + .unwrap(), + 1 + ); + assert_eq!(buf.get_num_events(), 1); + + let event = buf.get_event(0).unwrap(); + assert_eq!(event.get_event_type().unwrap(), LineEdgeEvent::Rising); + assert_eq!(event.get_line_offset(), GPIO[0]); + assert_eq!(event.get_global_seqno(), 1); + assert_eq!(event.get_line_seqno(), 1); + + // Rising event GPIO 1 + config + .request() + .wait_edge_event(Duration::from_secs(1)) + .unwrap(); + + assert_eq!( + config + .request() + .read_edge_event(&buf, buf.get_capacity()) + .unwrap(), + 1 + ); + assert_eq!(buf.get_num_events(), 1); + + let event = buf.get_event(0).unwrap(); + assert_eq!(event.get_event_type().unwrap(), LineEdgeEvent::Rising); + assert_eq!(event.get_line_offset(), GPIO[1]); + assert_eq!(event.get_global_seqno(), 2); + assert_eq!(event.get_line_seqno(), 1); + + // No events available + assert_eq!( + config + .request() + .wait_edge_event(Duration::from_millis(100)) + .unwrap_err(), + ChipError::OperationTimedOut + ); + } + + #[test] + fn multiple_events() { + const GPIO: u32 = 1; + let buf = EdgeEventBuffer::new(0).unwrap(); + let mut config = TestConfig::new(NGPIO).unwrap(); + config.rconfig(Some(&[GPIO])); + config.lconfig_edge(Some(Edge::Both)); + config.request_lines().unwrap(); + + // Generate events + trigger_multiple_events(config.sim(), GPIO); + + // Read multiple events + config + .request() + .wait_edge_event(Duration::from_secs(1)) + .unwrap(); + + assert_eq!( + config + .request() + .read_edge_event(&buf, buf.get_capacity()) + .unwrap(), + 3 + ); + assert_eq!(buf.get_num_events(), 3); + + let mut global_seqno = 1; + let mut line_seqno = 1; + + // Verify sequence number of events + for i in 0..3 { + let event = buf.get_event(i).unwrap(); + assert_eq!(event.get_line_offset(), GPIO); + assert_eq!(event.get_global_seqno(), global_seqno); + assert_eq!(event.get_line_seqno(), line_seqno); + + global_seqno += 1; + line_seqno += 1; + } + } + + #[test] + fn over_capacity() { + const GPIO: u32 = 2; + let buf = EdgeEventBuffer::new(2).unwrap(); + let mut config = TestConfig::new(NGPIO).unwrap(); + config.rconfig(Some(&[GPIO])); + config.lconfig_edge(Some(Edge::Both)); + config.request_lines().unwrap(); + + // Generate events + trigger_multiple_events(config.sim(), GPIO); + + // Read multiple events + config + .request() + .wait_edge_event(Duration::from_secs(1)) + .unwrap(); + + assert_eq!( + config + .request() + .read_edge_event(&buf, buf.get_capacity()) + .unwrap(), + 2 + ); + assert_eq!(buf.get_num_events(), 2); + } + } +} diff --git a/bindings/rust/tests/info_event.rs b/bindings/rust/tests/info_event.rs new file mode 100644 index 000000000000..96d8385deadf --- /dev/null +++ b/bindings/rust/tests/info_event.rs @@ -0,0 +1,126 @@ +// 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 IoError; + + use crate::common::*; + use libgpiod::{Chip, Direction, Error as ChipError, Event, LineConfig, RequestConfig}; + + fn request_reconfigure_line(chip: Arc) { + spawn(move || { + sleep(Duration::from_millis(10)); + + let lconfig1 = LineConfig::new().unwrap(); + let rconfig = RequestConfig::new().unwrap(); + rconfig.set_offsets(&[7]); + + let request = chip.request_lines(&rconfig, &lconfig1).unwrap(); + + sleep(Duration::from_millis(10)); + + let mut lconfig2 = LineConfig::new().unwrap(); + lconfig2.set_direction_default(Direction::Output); + + request.reconfigure_lines(&lconfig2).unwrap(); + + sleep(Duration::from_millis(10)); + }); + } + + mod watch { + use super::*; + const NGPIO: u64 = 8; + const GPIO: u32 = 7; + + #[test] + fn failure() { + let sim = Sim::new(Some(NGPIO), None, true).unwrap(); + let chip = Chip::open(sim.dev_path()).unwrap(); + + assert_eq!( + chip.watch_line_info(NGPIO as u32).unwrap_err(), + ChipError::OperationFailed("Gpio LineInfo line-info", IoError::new(EINVAL)) + ); + + chip.watch_line_info(3).unwrap(); + + // No events available + assert_eq!( + chip.wait_info_event(Duration::from_millis(100)) + .unwrap_err(), + ChipError::OperationTimedOut + ); + } + + #[test] + fn verify() { + let sim = Sim::new(Some(NGPIO), None, true).unwrap(); + let chip = Chip::open(sim.dev_path()).unwrap(); + let info = chip.watch_line_info(GPIO).unwrap(); + + assert_eq!(info.get_offset(), GPIO); + } + + #[test] + fn reconfigure() { + let sim = Sim::new(Some(NGPIO), None, true).unwrap(); + let chip = Arc::new(Chip::open(sim.dev_path()).unwrap()); + let info = chip.watch_line_info(GPIO).unwrap(); + + assert_eq!(info.get_direction().unwrap(), Direction::Input); + + // Generate events + request_reconfigure_line(chip.clone()); + + // Line requested event + chip.wait_info_event(Duration::from_secs(1)).unwrap(); + let event = chip.read_info_event().unwrap(); + let ts_req = event.get_timestamp(); + + assert_eq!(event.get_event_type().unwrap(), Event::LineRequested); + assert_eq!( + event.line_info().unwrap().get_direction().unwrap(), + Direction::Input + ); + + // Line changed event + chip.wait_info_event(Duration::from_secs(1)).unwrap(); + let event = chip.read_info_event().unwrap(); + let ts_rec = event.get_timestamp(); + + assert_eq!(event.get_event_type().unwrap(), Event::LineConfigChanged); + assert_eq!( + event.line_info().unwrap().get_direction().unwrap(), + Direction::Output + ); + + // Line released event + chip.wait_info_event(Duration::from_secs(1)).unwrap(); + let event = chip.read_info_event().unwrap(); + let ts_rel = event.get_timestamp(); + + assert_eq!(event.get_event_type().unwrap(), Event::LineReleased); + + // No events available + assert_eq!( + chip.wait_info_event(Duration::from_millis(100)) + .unwrap_err(), + ChipError::OperationTimedOut + ); + + // Check timestamps are really monotonic. + assert!(ts_rel > ts_rec); + assert!(ts_rec > ts_req); + } + } +} diff --git a/bindings/rust/tests/line_config.rs b/bindings/rust/tests/line_config.rs new file mode 100644 index 000000000000..82879324a7f0 --- /dev/null +++ b/bindings/rust/tests/line_config.rs @@ -0,0 +1,187 @@ +// 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::{Bias, Direction, Drive, Edge, EventClock, LineConfig}; + + mod default { + use super::*; + + #[test] + fn verify() { + let lconfig = LineConfig::new().unwrap(); + + assert_eq!(lconfig.get_direction_default().unwrap(), Direction::AsIs); + assert_eq!(lconfig.get_edge_detection_default().unwrap(), Edge::None); + assert_eq!(lconfig.get_bias_default().unwrap(), Bias::AsIs); + assert_eq!(lconfig.get_drive_default().unwrap(), Drive::PushPull); + assert_eq!(lconfig.get_active_low_default(), false); + assert_eq!( + lconfig.get_debounce_period_default().unwrap(), + Duration::from_millis(0) + ); + assert_eq!( + lconfig.get_event_clock_default().unwrap(), + EventClock::Monotonic + ); + assert_eq!(lconfig.get_output_value_default().unwrap(), 0); + assert_eq!(lconfig.get_overrides().unwrap().len(), 0); + } + } + + mod overrides { + use super::*; + + #[test] + fn direction() { + const GPIO: u32 = 0; + let mut lconfig = LineConfig::new().unwrap(); + + lconfig.set_direction_default(Direction::AsIs); + lconfig.set_direction_override(Direction::Input, GPIO); + + assert_eq!(lconfig.direction_is_overridden(GPIO), true); + assert_eq!( + lconfig.get_direction_offset(GPIO).unwrap(), + Direction::Input + ); + + lconfig.clear_direction_override(GPIO); + assert_eq!(lconfig.direction_is_overridden(GPIO), false); + assert_eq!(lconfig.get_direction_offset(GPIO).unwrap(), Direction::AsIs); + } + + #[test] + fn edge_detection() { + const GPIO: u32 = 1; + let mut lconfig = LineConfig::new().unwrap(); + + lconfig.set_edge_detection_default(Edge::None); + lconfig.set_edge_detection_override(Edge::Both, GPIO); + + assert_eq!(lconfig.edge_detection_is_overridden(GPIO), true); + assert_eq!(lconfig.get_edge_detection_offset(GPIO).unwrap(), Edge::Both); + + lconfig.clear_edge_detection_override(GPIO); + assert_eq!(lconfig.edge_detection_is_overridden(GPIO), false); + assert_eq!(lconfig.get_edge_detection_offset(GPIO).unwrap(), Edge::None); + } + + #[test] + fn bias() { + const GPIO: u32 = 2; + let mut lconfig = LineConfig::new().unwrap(); + + lconfig.set_bias_default(Bias::AsIs); + lconfig.set_bias_override(Bias::PullDown, GPIO); + + assert_eq!(lconfig.bias_is_overridden(GPIO), true); + assert_eq!(lconfig.get_bias_offset(GPIO).unwrap(), Bias::PullDown); + + lconfig.clear_bias_override(GPIO); + assert_eq!(lconfig.bias_is_overridden(GPIO), false); + assert_eq!(lconfig.get_bias_offset(GPIO).unwrap(), Bias::AsIs); + } + + #[test] + fn drive() { + const GPIO: u32 = 3; + let mut lconfig = LineConfig::new().unwrap(); + + lconfig.set_drive_default(Drive::PushPull); + lconfig.set_drive_override(Drive::OpenDrain, GPIO); + + assert_eq!(lconfig.drive_is_overridden(GPIO), true); + assert_eq!(lconfig.get_drive_offset(GPIO).unwrap(), Drive::OpenDrain); + + lconfig.clear_drive_override(GPIO); + assert_eq!(lconfig.drive_is_overridden(GPIO), false); + assert_eq!(lconfig.get_drive_offset(GPIO).unwrap(), Drive::PushPull); + } + + #[test] + fn active_low() { + const GPIO: u32 = 4; + let mut lconfig = LineConfig::new().unwrap(); + + lconfig.set_active_low_default(false); + lconfig.set_active_low_override(true, GPIO); + + assert_eq!(lconfig.active_low_is_overridden(GPIO), true); + assert_eq!(lconfig.get_active_low_offset(GPIO), true); + + lconfig.clear_active_low_override(GPIO); + assert_eq!(lconfig.active_low_is_overridden(GPIO), false); + assert_eq!(lconfig.get_active_low_offset(GPIO), false); + } + + #[test] + fn debounce_period() { + const GPIO: u32 = 5; + let mut lconfig = LineConfig::new().unwrap(); + + lconfig.set_debounce_period_default(Duration::from_millis(5)); + lconfig.set_debounce_period_override(Duration::from_millis(3), GPIO); + + assert_eq!(lconfig.debounce_period_is_overridden(GPIO), true); + assert_eq!( + lconfig.get_debounce_period_offset(GPIO).unwrap(), + Duration::from_millis(3) + ); + + lconfig.clear_debounce_period_override(GPIO); + assert_eq!(lconfig.debounce_period_is_overridden(GPIO), false); + assert_eq!( + lconfig.get_debounce_period_offset(GPIO).unwrap(), + Duration::from_millis(5) + ); + } + + #[test] + fn event_clock() { + const GPIO: u32 = 6; + let mut lconfig = LineConfig::new().unwrap(); + + lconfig.set_event_clock_default(EventClock::Monotonic); + lconfig.set_event_clock_override(EventClock::Realtime, GPIO); + + assert_eq!(lconfig.event_clock_is_overridden(GPIO), true); + assert_eq!( + lconfig.get_event_clock_offset(GPIO).unwrap(), + EventClock::Realtime + ); + + lconfig.clear_event_clock_override(GPIO); + assert_eq!(lconfig.event_clock_is_overridden(GPIO), false); + assert_eq!( + lconfig.get_event_clock_offset(GPIO).unwrap(), + EventClock::Monotonic + ); + } + + #[test] + fn output_value() { + const GPIO: u32 = 0; + let mut lconfig = LineConfig::new().unwrap(); + + lconfig.set_output_value_default(0); + lconfig.set_output_value_override(1, GPIO); + lconfig.set_output_values(&[1, 2, 8], &[1, 1, 1]).unwrap(); + + for line in [0, 1, 2, 8] { + assert_eq!(lconfig.output_value_is_overridden(line), true); + assert_eq!(lconfig.get_output_value_offset(line).unwrap(), 1); + + lconfig.clear_output_value_override(line); + assert_eq!(lconfig.output_value_is_overridden(line), false); + assert_eq!(lconfig.get_output_value_offset(line).unwrap(), 0); + } + } + } +} diff --git a/bindings/rust/tests/line_info.rs b/bindings/rust/tests/line_info.rs new file mode 100644 index 000000000000..f6f8f592cc85 --- /dev/null +++ b/bindings/rust/tests/line_info.rs @@ -0,0 +1,90 @@ +// 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 IoError; + + use crate::common::*; + use libgpiod::{Bias, Chip, Direction, Drive, Edge, Error as ChipError, EventClock}; + use libgpiod_sys::{GPIOSIM_HOG_DIR_OUTPUT_HIGH, GPIOSIM_HOG_DIR_OUTPUT_LOW}; + + const NGPIO: u64 = 8; + + mod basic { + use super::*; + + #[test] + fn verify() { + const GPIO: u32 = 0; + const LABEL: &str = "foobar"; + let sim = Sim::new(Some(NGPIO), None, false).unwrap(); + sim.set_line_name(GPIO, LABEL).unwrap(); + sim.hog_line(GPIO, "hog", GPIOSIM_HOG_DIR_OUTPUT_HIGH as i32) + .unwrap(); + sim.enable().unwrap(); + + let chip = Chip::open(sim.dev_path()).unwrap(); + let info = chip.line_info(GPIO).unwrap(); + + assert_eq!(info.get_offset(), GPIO); + assert_eq!(info.get_name().unwrap(), LABEL); + assert_eq!(info.is_used(), true); + assert_eq!(info.get_consumer().unwrap(), "hog"); + assert_eq!(info.get_direction().unwrap(), Direction::Output); + assert_eq!(info.is_active_low(), false); + assert_eq!(info.get_bias().unwrap(), Bias::Unknown); + assert_eq!(info.get_drive().unwrap(), Drive::PushPull); + assert_eq!(info.get_edge_detection().unwrap(), Edge::None); + assert_eq!(info.get_event_clock().unwrap(), EventClock::Monotonic); + assert_eq!(info.is_debounced(), false); + assert_eq!(info.get_debounce_period(), Duration::from_millis(0)); + + assert_eq!( + chip.line_info(NGPIO as u32).unwrap_err(), + ChipError::OperationFailed("Gpio LineInfo line-info", IoError::new(EINVAL)) + ); + } + } + + mod properties { + use super::*; + + #[test] + fn verify() { + let sim = Sim::new(Some(NGPIO), None, false).unwrap(); + sim.set_line_name(1, "one").unwrap(); + sim.set_line_name(2, "two").unwrap(); + sim.set_line_name(4, "four").unwrap(); + sim.set_line_name(5, "five").unwrap(); + sim.hog_line(3, "hog3", GPIOSIM_HOG_DIR_OUTPUT_HIGH as i32) + .unwrap(); + sim.hog_line(4, "hog4", GPIOSIM_HOG_DIR_OUTPUT_LOW as i32) + .unwrap(); + sim.enable().unwrap(); + + let chip = Chip::open(sim.dev_path()).unwrap(); + chip.line_info(6).unwrap(); + + let info4 = chip.line_info(4).unwrap(); + assert_eq!(info4.get_offset(), 4); + assert_eq!(info4.get_name().unwrap(), "four"); + assert_eq!(info4.is_used(), true); + assert_eq!(info4.get_consumer().unwrap(), "hog4"); + assert_eq!(info4.get_direction().unwrap(), Direction::Output); + assert_eq!(info4.is_active_low(), false); + assert_eq!(info4.get_bias().unwrap(), Bias::Unknown); + assert_eq!(info4.get_drive().unwrap(), Drive::PushPull); + assert_eq!(info4.get_edge_detection().unwrap(), Edge::None); + assert_eq!(info4.get_event_clock().unwrap(), EventClock::Monotonic); + assert_eq!(info4.is_debounced(), false); + assert_eq!(info4.get_debounce_period(), Duration::from_millis(0)); + } + } +} diff --git a/bindings/rust/tests/line_request.rs b/bindings/rust/tests/line_request.rs new file mode 100644 index 000000000000..361ee6318d2e --- /dev/null +++ b/bindings/rust/tests/line_request.rs @@ -0,0 +1,234 @@ +// 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::{EBUSY, EINVAL}; + + use vmm_sys_util::errno::Error as IoError; + + use crate::common::*; + use libgpiod::{Bias, Direction, Error as ChipError, LineConfig}; + use libgpiod_sys::{ + GPIOSIM_PULL_DOWN, GPIOSIM_PULL_UP, GPIOSIM_VALUE_ACTIVE, GPIOSIM_VALUE_INACTIVE, + }; + + const NGPIO: u64 = 8; + + mod invalid_arguments { + use super::*; + + #[test] + fn no_offsets() { + let mut config = TestConfig::new(NGPIO).unwrap(); + config.rconfig(None); + config.lconfig_raw(); + + assert_eq!( + config.request_lines().unwrap_err(), + ChipError::OperationFailed("Gpio LineRequest request-lines", IoError::new(EINVAL)) + ); + } + + #[test] + fn duplicate_offsets() { + let mut config = TestConfig::new(NGPIO).unwrap(); + config.rconfig(Some(&[2, 0, 0, 4])); + config.lconfig_raw(); + + assert_eq!( + config.request_lines().unwrap_err(), + ChipError::OperationFailed("Gpio LineRequest request-lines", IoError::new(EBUSY)) + ); + } + + #[test] + fn out_of_bound_offsets() { + let mut config = TestConfig::new(NGPIO).unwrap(); + config.rconfig(Some(&[2, 0, 8, 4])); + config.lconfig_raw(); + + assert_eq!( + config.request_lines().unwrap_err(), + ChipError::OperationFailed("Gpio LineRequest request-lines", IoError::new(EINVAL)) + ); + } + } + + mod verify { + use super::*; + + #[test] + fn custom_consumer() { + const GPIO: u32 = 2; + const CONSUMER: &str = "foobar"; + let mut config = TestConfig::new(NGPIO).unwrap(); + config.rconfig_consumer(Some(&[GPIO]), Some(CONSUMER)); + config.lconfig_raw(); + config.request_lines().unwrap(); + + let info = config.chip().line_info(GPIO).unwrap(); + + assert_eq!(info.is_used(), true); + assert_eq!(info.get_consumer().unwrap(), CONSUMER); + } + + #[test] + fn empty_consumer() { + const GPIO: u32 = 2; + let mut config = TestConfig::new(NGPIO).unwrap(); + config.rconfig(Some(&[GPIO])); + config.lconfig_raw(); + config.request_lines().unwrap(); + + let info = config.chip().line_info(GPIO).unwrap(); + + assert_eq!(info.is_used(), true); + assert_eq!(info.get_consumer().unwrap(), "?"); + } + + #[test] + fn read_values() { + let offsets = [7, 1, 0, 6, 2]; + let pulls = [ + GPIOSIM_PULL_UP, + GPIOSIM_PULL_UP, + GPIOSIM_PULL_DOWN, + GPIOSIM_PULL_UP, + GPIOSIM_PULL_DOWN, + ]; + let mut config = TestConfig::new(NGPIO).unwrap(); + config.set_pull(&offsets, &pulls); + config.rconfig(Some(&offsets)); + config.lconfig(Some(Direction::Input), None, None, None, None); + config.request_lines().unwrap(); + + let request = config.request(); + + // Buffer is smaller + let mut values: Vec = vec![0; 4]; + assert_eq!( + request.get_values(&mut values).unwrap_err(), + ChipError::OperationFailed( + "Gpio LineRequest array size mismatch", + IoError::new(EINVAL), + ) + ); + + // Buffer is larger + let mut values: Vec = vec![0; 6]; + assert_eq!( + request.get_values(&mut values).unwrap_err(), + ChipError::OperationFailed( + "Gpio LineRequest array size mismatch", + IoError::new(EINVAL), + ) + ); + + // Single values read properly + assert_eq!(request.get_value(7).unwrap(), 1); + + // Values read properly + let mut values: Vec = vec![0; 5]; + request.get_values(&mut values).unwrap(); + for i in 0..values.len() { + assert_eq!( + values[i], + match pulls[i] { + GPIOSIM_PULL_DOWN => 0, + _ => 1, + } + ); + } + + // Subset of values read properly + let mut values: Vec = vec![0; 3]; + request.get_values_subset(&[2, 0, 6], &mut values).unwrap(); + assert_eq!(values[0], 0); + assert_eq!(values[1], 0); + assert_eq!(values[2], 1); + + // Value read properly after reconfigure + let mut lconfig = LineConfig::new().unwrap(); + lconfig.set_active_low_default(true); + request.reconfigure_lines(&lconfig).unwrap(); + assert_eq!(request.get_value(7).unwrap(), 0); + } + + #[test] + fn set_output_values() { + let offsets = [0, 1, 3, 4]; + let mut config = TestConfig::new(NGPIO).unwrap(); + config.rconfig(Some(&offsets)); + config.lconfig(Some(Direction::Output), Some(1), Some((4, 0)), None, None); + config.request_lines().unwrap(); + + assert_eq!(config.sim().val(0).unwrap(), GPIOSIM_VALUE_ACTIVE); + assert_eq!(config.sim().val(1).unwrap(), GPIOSIM_VALUE_ACTIVE); + assert_eq!(config.sim().val(3).unwrap(), GPIOSIM_VALUE_ACTIVE); + + // Overriden + assert_eq!(config.sim().val(4).unwrap(), GPIOSIM_VALUE_INACTIVE); + + // Default + assert_eq!(config.sim().val(2).unwrap(), GPIOSIM_VALUE_INACTIVE); + } + + #[test] + fn reconfigure_output_values() { + let offsets = [0, 1, 3, 4]; + let mut config = TestConfig::new(NGPIO).unwrap(); + config.rconfig(Some(&offsets)); + config.lconfig(Some(Direction::Output), Some(0), None, None, None); + config.request_lines().unwrap(); + let request = config.request(); + + // Set single value + request.set_value(1, 1).unwrap(); + assert_eq!(config.sim().val(0).unwrap(), GPIOSIM_VALUE_INACTIVE); + assert_eq!(config.sim().val(1).unwrap(), GPIOSIM_VALUE_ACTIVE); + assert_eq!(config.sim().val(3).unwrap(), GPIOSIM_VALUE_INACTIVE); + assert_eq!(config.sim().val(4).unwrap(), GPIOSIM_VALUE_INACTIVE); + request.set_value(1, 0).unwrap(); + assert_eq!(config.sim().val(1).unwrap(), GPIOSIM_VALUE_INACTIVE); + + // Set values of subset + request.set_values_subset(&[4, 3], &[1, 1]).unwrap(); + assert_eq!(config.sim().val(0).unwrap(), GPIOSIM_VALUE_INACTIVE); + assert_eq!(config.sim().val(1).unwrap(), GPIOSIM_VALUE_INACTIVE); + assert_eq!(config.sim().val(3).unwrap(), GPIOSIM_VALUE_ACTIVE); + assert_eq!(config.sim().val(4).unwrap(), GPIOSIM_VALUE_ACTIVE); + request.set_values_subset(&[4, 3], &[0, 0]).unwrap(); + assert_eq!(config.sim().val(3).unwrap(), GPIOSIM_VALUE_INACTIVE); + assert_eq!(config.sim().val(4).unwrap(), GPIOSIM_VALUE_INACTIVE); + + // Set all values + request.set_values(&[1, 0, 1, 0]).unwrap(); + assert_eq!(config.sim().val(0).unwrap(), GPIOSIM_VALUE_ACTIVE); + assert_eq!(config.sim().val(1).unwrap(), GPIOSIM_VALUE_INACTIVE); + assert_eq!(config.sim().val(3).unwrap(), GPIOSIM_VALUE_ACTIVE); + assert_eq!(config.sim().val(4).unwrap(), GPIOSIM_VALUE_INACTIVE); + request.set_values(&[0, 0, 0, 0]).unwrap(); + assert_eq!(config.sim().val(0).unwrap(), GPIOSIM_VALUE_INACTIVE); + assert_eq!(config.sim().val(1).unwrap(), GPIOSIM_VALUE_INACTIVE); + assert_eq!(config.sim().val(3).unwrap(), GPIOSIM_VALUE_INACTIVE); + assert_eq!(config.sim().val(4).unwrap(), GPIOSIM_VALUE_INACTIVE); + } + + #[test] + fn set_bias() { + let offsets = [3]; + let mut config = TestConfig::new(NGPIO).unwrap(); + config.rconfig(Some(&offsets)); + config.lconfig(Some(Direction::Input), None, None, None, Some(Bias::PullUp)); + config.request_lines().unwrap(); + config.request(); + + // Set single value + assert_eq!(config.sim().val(3).unwrap(), GPIOSIM_VALUE_ACTIVE); + } + } +} diff --git a/bindings/rust/tests/request_config.rs b/bindings/rust/tests/request_config.rs new file mode 100644 index 000000000000..e914ca8ec887 --- /dev/null +++ b/bindings/rust/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 IoError; + + use libgpiod::{Error as ChipError, RequestConfig}; + + mod verify { + use super::*; + + #[test] + fn default() { + let rconfig = RequestConfig::new().unwrap(); + + assert_eq!(rconfig.get_offsets().len(), 0); + assert_eq!(rconfig.get_event_buffer_size(), 0); + assert_eq!( + rconfig.get_consumer().unwrap_err(), + ChipError::OperationFailed("Gpio RequestConfig get-consumer", IoError::new(0)) + ); + } + + #[test] + fn initialized() { + let offsets = [0, 1, 2, 3]; + const CONSUMER: &str = "foobar"; + let rconfig = RequestConfig::new().unwrap(); + rconfig.set_consumer(CONSUMER); + rconfig.set_offsets(&offsets); + rconfig.set_event_buffer_size(64); + + assert_eq!(rconfig.get_offsets(), offsets); + assert_eq!(rconfig.get_event_buffer_size(), 64); + assert_eq!(rconfig.get_consumer().unwrap(), CONSUMER); + } + } +} From patchwork Fri Jul 8 11:35:01 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 588760 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 816EBC43334 for ; Fri, 8 Jul 2022 11:35:28 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237684AbiGHLf1 (ORCPT ); Fri, 8 Jul 2022 07:35:27 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33700 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237731AbiGHLf0 (ORCPT ); Fri, 8 Jul 2022 07:35:26 -0400 Received: from mail-pf1-x42a.google.com (mail-pf1-x42a.google.com [IPv6:2607:f8b0:4864:20::42a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4585F88F26 for ; Fri, 8 Jul 2022 04:35:26 -0700 (PDT) Received: by mail-pf1-x42a.google.com with SMTP id d10so9529112pfd.9 for ; Fri, 08 Jul 2022 04:35: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=vqIR+uSXHOhVHHUZbf27VLh2KEaBPzoLiCQTSRtyybk=; b=aiYFrymodhm5n+hXkpdhYbA2btJ7X8ZQUnfFiVOWZAU8vnTr2cxkQCMSVJaLxLwfZr S6pqcE7OuUp+9xjF1pozU/os+O/BVBLMX6jsvKyKNAObpr3t+z9wXk4VkaZq0ItA9Ibv nOL6XaHOp+v2MVsD1rKf99HC4CgLgEzgbaxDSDrg3Gvi8Tid5lsLZV60R+HLwDYRDxDj B1sN82aK9RKQxvoAxcpF4+HW7dLCJ3zuxEhgXBaE6dvOf9MHWRRC4G/i3I9DtIhmLdC6 8ETfW3nvTPfkS205i/feCa/9xhZmUPzOFMkTl4v+OZVaXSb0uK21UrjYL4czFCyR/ba3 rXTw== 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=uR/rAQt0iGpqh5sJ5yK6CjSrOx5VJFHGZofzq1tE3iNdejMl2YcNV9UUVgpKLyQ4Kt V6S13r4POwMmi5C1dqMClVdTXPf8BpEGgBZgkEWczxRBRtpdkqAS/XeT2+/M1IM2Bwv1 +UA0QRaoLvQWQuvgvhyXNMQaDG3kNfyPifg1+2fUk0zLIELKZpumie4nmYY73IcX7o9O 3xTzcdYU9kwvp+nBeERiKVO8xrZC6xNuuEelF2U/cKlgCSrHwhcoQxFgN0ye9cu1B4iH PtUssojTgZTtELRHB8krlAmMSv+vB16oGJT9ybgx2zlncw474c+wI4UDxxFJu95UQRl1 cZOg== X-Gm-Message-State: AJIora+w9cYjIoUhRscAXEPeV0BtJE53bU4tOQqmUi4WkNZMZuKD4+n+ btygRq2syFpWoJN6GudnawEgpA== X-Google-Smtp-Source: AGRyM1sE068N0U8uzbQrICPesY45Z7SQRbHSvJq/S7RpJrWzX2qJLlNMTBHoNmqgbWXYES6JGBiuDg== X-Received: by 2002:a63:a1d:0:b0:412:9d39:3af with SMTP id 29-20020a630a1d000000b004129d3903afmr2820593pgk.45.1657280125800; Fri, 08 Jul 2022 04:35:25 -0700 (PDT) Received: from localhost ([122.171.18.80]) by smtp.gmail.com with ESMTPSA id g62-20020a625241000000b00528e84c3093sm2253663pfb.143.2022.07.08.04.35.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 08 Jul 2022 04:35:25 -0700 (PDT) From: Viresh Kumar To: Linus Walleij , Bartosz Golaszewski Cc: Viresh Kumar , Vincent Guittot , linux-gpio@vger.kernel.org, Kent Gibson , Miguel Ojeda , Wedson Almeida Filho , =?utf-8?q?Alex_Benn=C3=A9e?= , stratos-dev@op-lists.linaro.org, Gerard Ryan Subject: [PATCH V4 8/8] libgpiod: Integrate building of rust bindings with make Date: Fri, 8 Jul 2022 17:05:01 +0530 Message-Id: <584910baf342bee3511361c3e486ad4f3e5437f2.1657279685.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