From patchwork Mon Nov 7 09:57:27 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 622480 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 ADEB5C4332F for ; Mon, 7 Nov 2022 09:57:46 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231892AbiKGJ5p (ORCPT ); Mon, 7 Nov 2022 04:57:45 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49190 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231304AbiKGJ5p (ORCPT ); Mon, 7 Nov 2022 04:57:45 -0500 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 4F56F17598 for ; Mon, 7 Nov 2022 01:57:44 -0800 (PST) Received: by mail-pl1-x62f.google.com with SMTP id io19so10581877plb.8 for ; Mon, 07 Nov 2022 01:57:44 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=6MAKuN26Xb3OfwOkrTuAtD8LOXrUtseWf36A0bwTJig=; b=ov1QyMc6idnr+ujIHDiwbI97cLwZCB1dMSQClL36IdguAVuJXr6DU04nVNn3AgG39U bgmST7XZfZ1wtpl5Kqm3Sf/p2h3rqG0rCa4yyDDSBmau4OoU1soUeFI0k0gAS0c4qLRr cU4UTSofTvdhHdR/E/mr5mgz5Sve3UY5bPgQSSbGpCfSDxfP+lMuX/GbpQF6BZfQd8DG VeJDK2UClC14lApoRZzrhlr6A4f8BXfvxz6uZnJ30M5BupYfYHfIunXqQzcRAg3nO7Lx RrlmoyHlMjRCj1xiUaF2OXi9N8D59ZvBW4v2YP8uh8zVJgUGdpOH7LE60ROntpFKOQA4 aubA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=6MAKuN26Xb3OfwOkrTuAtD8LOXrUtseWf36A0bwTJig=; b=peJ3mYDu56FJ7qcakM5nxEN5VgpMieTp4K+0IiEi2JYJchz7F4FzFS9ohdj7nbn4zP ofhCDfoPytzHQtOSLow+TeS82gmT7cDd2A2q1YXdZH9rD9kf4Opj7megl+aHF9MwGAKk U9NJ4dooLdrCEtjSHCPAQcDw/KcUAOahlNEi1ZypYWc1KD+OiO0QMEDi+J0/pj+n2fBw iAQQSL4A4tNXrLA2SjBQEV6K9+0LJlkNk0WU0Pe2AAfFIoGS80AZ5BcotqDRFV8d2VG+ 1JFjNvCM9LC8YtZUOSietZbrN390uoHgoK3dBFHyDg4XYc+iD7BrFo46nalhieiTsE2M BC6w== X-Gm-Message-State: ACrzQf2p8rDqUiphiqdgmgrTCFkbdMJX3ppJZ+5yQY662uuxeXQkjSbz 8I9GzyR5Hr+RzG2vv1Rpj9jftQ== X-Google-Smtp-Source: AMsMyM5GUGLhsy2NbagBYXJ6iqorkiNBFEZ4GmVf3fTu5Ns3iPXqEEgo3BQqxWD9BOwRw8DoCT7vkg== X-Received: by 2002:a17:90a:ce89:b0:213:167c:81e1 with SMTP id g9-20020a17090ace8900b00213167c81e1mr68419438pju.38.1667815063801; Mon, 07 Nov 2022 01:57:43 -0800 (PST) Received: from localhost ([122.172.84.80]) by smtp.gmail.com with ESMTPSA id a32-20020a634d20000000b0046fe658a903sm3793350pgb.94.2022.11.07.01.57.43 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 07 Nov 2022 01:57:43 -0800 (PST) From: Viresh Kumar To: Linus Walleij , Bartosz Golaszewski , Kent Gibson Cc: Viresh Kumar , Vincent Guittot , linux-gpio@vger.kernel.org, Miguel Ojeda , Wedson Almeida Filho , =?utf-8?q?Alex_Benn=C3=A9e?= , stratos-dev@op-lists.linaro.org, Gerard Ryan , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , y86-dev Subject: [libgpiod][PATCH V9 1/8] bindings: rust: Add libgpiod-sys rust crate Date: Mon, 7 Nov 2022 15:27:27 +0530 Message-Id: <2a943ded955e634fc76022dd3bccdb0e8f8b4313.1667815011.git.viresh.kumar@linaro.org> X-Mailer: git-send-email 2.31.1.272.g89b43f80a514 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org This adds libgpiod-sys rust crate, which provides FFI (foreign function interface) bindings for libgpiod APIs. Signed-off-by: Viresh Kumar --- .gitignore | 5 ++++ bindings/rust/Cargo.toml | 5 ++++ bindings/rust/libgpiod-sys/Cargo.toml | 20 +++++++++++++ bindings/rust/libgpiod-sys/build.rs | 41 +++++++++++++++++++++++++++ bindings/rust/libgpiod-sys/src/lib.rs | 13 +++++++++ 5 files changed, 84 insertions(+) create mode 100644 bindings/rust/Cargo.toml create mode 100644 bindings/rust/libgpiod-sys/Cargo.toml create mode 100644 bindings/rust/libgpiod-sys/build.rs create mode 100644 bindings/rust/libgpiod-sys/src/lib.rs diff --git a/.gitignore b/.gitignore index 6c08415b390d..9f2fcf440c5d 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,8 @@ stamp-h1 # profiling *.gcda *.gcno + +# Added by cargo + +target +Cargo.lock diff --git a/bindings/rust/Cargo.toml b/bindings/rust/Cargo.toml new file mode 100644 index 000000000000..c7bbcc798920 --- /dev/null +++ b/bindings/rust/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +members = [ + "libgpiod-sys" +] diff --git a/bindings/rust/libgpiod-sys/Cargo.toml b/bindings/rust/libgpiod-sys/Cargo.toml new file mode 100644 index 000000000000..47d0c2a7311c --- /dev/null +++ b/bindings/rust/libgpiod-sys/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "libgpiod-sys" +version = "0.1.0" +authors = ["Viresh Kumar "] +description = "libgpiod public header bindings" +repository = "https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git" +categories = ["external-ffi-bindings", "os::linux-apis"] +rust-version = "1.56" +keywords = ["libgpiod", "gpio"] +license = "Apache-2.0 OR BSD-3-Clause" +edition = "2021" + +[dependencies] + +[features] +generate = [ "bindgen" ] + +[build-dependencies] +bindgen = { version = "0.59.1", optional = true } +cc = "1.0.46" diff --git a/bindings/rust/libgpiod-sys/build.rs b/bindings/rust/libgpiod-sys/build.rs new file mode 100644 index 000000000000..98863686c7af --- /dev/null +++ b/bindings/rust/libgpiod-sys/build.rs @@ -0,0 +1,41 @@ +#[cfg(feature = "generate")] +extern crate bindgen; +#[cfg(feature = "generate")] +use std::env; +#[cfg(feature = "generate")] +use std::path::PathBuf; + +#[cfg(feature = "generate")] +fn generate_bindings() { + // Tell cargo to invalidate the built crate whenever following files change + println!("cargo:rerun-if-changed=../../../include/gpiod.h"); + + // The bindgen::Builder is the main entry point + // to bindgen, and lets you build up options for + // the resulting bindings. + let bindings = bindgen::Builder::default() + // The input header we would like to generate + // bindings for. + .header("../../../include/gpiod.h") + // Tell cargo to invalidate the built crate whenever any of the + // included header files changed. + .parse_callbacks(Box::new(bindgen::CargoCallbacks)) + // Finish the builder and generate the bindings. + .generate() + // Unwrap the Result and panic on failure. + .expect("Unable to generate bindings"); + + // Write the bindings to the $OUT_DIR/bindings.rs file. + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Couldn't write bindings!"); +} + +fn main() { + #[cfg(feature = "generate")] + generate_bindings(); + + println!("cargo:rustc-link-search=./../../lib/.libs/"); + println!("cargo:rustc-link-lib=static=gpiod"); +} diff --git a/bindings/rust/libgpiod-sys/src/lib.rs b/bindings/rust/libgpiod-sys/src/lib.rs new file mode 100644 index 000000000000..a1d1db19afe3 --- /dev/null +++ b/bindings/rust/libgpiod-sys/src/lib.rs @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause + +#[allow(non_camel_case_types, non_upper_case_globals)] +#[cfg_attr(test, allow(deref_nullptr, non_snake_case))] + +mod bindings_raw { + #[cfg(feature = "generate")] + include!(concat!(env!("OUT_DIR"), "/bindings.rs")); + + #[cfg(not(feature = "generate"))] + include!("bindings.rs"); +} +pub use bindings_raw::*; From patchwork Mon Nov 7 09:57:28 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 623161 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 1BF72C433FE for ; Mon, 7 Nov 2022 09:58:00 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231374AbiKGJ56 (ORCPT ); Mon, 7 Nov 2022 04:57:58 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49234 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231149AbiKGJ5w (ORCPT ); Mon, 7 Nov 2022 04:57:52 -0500 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 31CF515824 for ; Mon, 7 Nov 2022 01:57:49 -0800 (PST) Received: by mail-pj1-x1029.google.com with SMTP id o7so10079823pjj.1 for ; Mon, 07 Nov 2022 01:57:49 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=soDZB83rgaPQbou8FEqRgdIJsK37GDQRLU3LyNYl0PQ=; b=Csmbej8WEoJzIY9a5QBvtxGhMo+n/qzkH3a7+h30/EG0W/hoScnfbgJN6jJksYwYbe g+0Yfzm7HccOrddwhgSo8/gj1HoDKG/P5vp0CKPPHi3J5lu55HcoQ1+xoGbdXwWDMQiH 4IKmVv2qEOuJfj/i6dsqACxlQP5zB48OcvlXB1PYaC/XI0w505/F+RmemTI9LBkaFj54 d1klUlMuAUqu/CwgSVplT+sev/zngi2qqZQFYAfajiz8IIsI9oGbTWfuKISeuVGPk5Uk TLt5aoeoZjGL2Kpir8V1UroWIgWLJB4r7iYtr2f8ZIW5N3fPcboqwtSrL8pttbebT0CS 8DpA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=soDZB83rgaPQbou8FEqRgdIJsK37GDQRLU3LyNYl0PQ=; b=7ctuc4vfAMZ/9xbZ+irk4ggxwBNOuV7Ac94SScGcuJ7cd3DVTvcLepq05pNxeA5E3f npCx1sl3PVcYPOKtqgVVWCLSH3H9+1K4Rf1qL/EcbSYXFN+Rg3+Wm3Nq7JH94BshnDK2 Otq0gJPUVj1xzg3pnSwTJa+pwQtCUU1xHcmeQiQgZxK0H1FIAX99azp10kImsfL7o4uP gwimfiTmIfuLqE/n3EprxLxbMv0UEdmpkdI21DwV5pJ6S/cj3x6nh1CrGS+6ZlVkVukw mvB9I9DxqxHN56IijQb+kWwb9koGPo8VG+ypPXEr8OYBN1sC9Au6AMtjurEo7wYlBaOP cBMw== X-Gm-Message-State: ACrzQf3qyqGsjDRpI0riTPoqhBuUx6zxAI0Lx++P6PU3EpzdMEA0QfE4 YVYBXxVjASxvDxblCzYF5wR/sw== X-Google-Smtp-Source: AMsMyM5MsmaDJpAEePUeSZRZnT1+pMj8ySsWLpjxqoqSbCBX1pT33UMZ7ZpPJIIqoK9915RnwiEYYg== X-Received: by 2002:a17:90a:f507:b0:211:e623:da5b with SMTP id cs7-20020a17090af50700b00211e623da5bmr69069203pjb.37.1667815067781; Mon, 07 Nov 2022 01:57:47 -0800 (PST) Received: from localhost ([122.172.84.80]) by smtp.gmail.com with ESMTPSA id s5-20020a17090a440500b0020b7de675a4sm3868347pjg.41.2022.11.07.01.57.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 07 Nov 2022 01:57:47 -0800 (PST) From: Viresh Kumar To: Linus Walleij , Bartosz Golaszewski , Kent Gibson Cc: Viresh Kumar , Vincent Guittot , linux-gpio@vger.kernel.org, Miguel Ojeda , Wedson Almeida Filho , =?utf-8?q?Alex_Benn=C3=A9e?= , stratos-dev@op-lists.linaro.org, Gerard Ryan , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , y86-dev Subject: [libgpiod][PATCH V9 2/8] bindings: rust: Add pre generated bindings for libgpiod-sys Date: Mon, 7 Nov 2022 15:27:28 +0530 Message-Id: X-Mailer: git-send-email 2.31.1.272.g89b43f80a514 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org This adds a copy of pre generated bindings and adds the suggested way of updating those in README. Signed-off-by: Viresh Kumar --- bindings/rust/libgpiod-sys/README.md | 11 + bindings/rust/libgpiod-sys/src/bindings.rs | 1173 ++++++++++++++++++++ 2 files changed, 1184 insertions(+) create mode 100644 bindings/rust/libgpiod-sys/README.md create mode 100644 bindings/rust/libgpiod-sys/src/bindings.rs diff --git a/bindings/rust/libgpiod-sys/README.md b/bindings/rust/libgpiod-sys/README.md new file mode 100644 index 000000000000..ecf75b31c41e --- /dev/null +++ b/bindings/rust/libgpiod-sys/README.md @@ -0,0 +1,11 @@ +# Generated libgpiod-sys Rust FFI bindings +Automatically generated Rust FFI bindings via + [bindgen](https://github.com/rust-lang/rust-bindgen). + +## Updating bindings +1. Clone the source from + +2. run `cd libgpiod/bindings/rust/libgpiod-sys/` +2. run `cargo build --features generate` +3. Copy the bindings 'cp ../target/debug/build/libgpiod-sys-###/out/bindings.rs src/bindings.rs' +4. Commit changes in `src/bindings.rs` diff --git a/bindings/rust/libgpiod-sys/src/bindings.rs b/bindings/rust/libgpiod-sys/src/bindings.rs new file mode 100644 index 000000000000..fc2ffa02c3c4 --- /dev/null +++ b/bindings/rust/libgpiod-sys/src/bindings.rs @@ -0,0 +1,1173 @@ +/* automatically generated by rust-bindgen 0.59.2 */ + +pub const true_: u32 = 1; +pub const false_: u32 = 0; +pub const __bool_true_false_are_defined: u32 = 1; +pub const _STDINT_H: u32 = 1; +pub const _FEATURES_H: u32 = 1; +pub const _DEFAULT_SOURCE: u32 = 1; +pub const __GLIBC_USE_ISOC2X: u32 = 0; +pub const __USE_ISOC11: u32 = 1; +pub const __USE_ISOC99: u32 = 1; +pub const __USE_ISOC95: u32 = 1; +pub const __USE_POSIX_IMPLICITLY: u32 = 1; +pub const _POSIX_SOURCE: u32 = 1; +pub const _POSIX_C_SOURCE: u32 = 200809; +pub const __USE_POSIX: u32 = 1; +pub const __USE_POSIX2: u32 = 1; +pub const __USE_POSIX199309: u32 = 1; +pub const __USE_POSIX199506: u32 = 1; +pub const __USE_XOPEN2K: u32 = 1; +pub const __USE_XOPEN2K8: u32 = 1; +pub const _ATFILE_SOURCE: u32 = 1; +pub const __USE_MISC: u32 = 1; +pub const __USE_ATFILE: u32 = 1; +pub const __USE_FORTIFY_LEVEL: u32 = 0; +pub const __GLIBC_USE_DEPRECATED_GETS: u32 = 0; +pub const __GLIBC_USE_DEPRECATED_SCANF: u32 = 0; +pub const _STDC_PREDEF_H: u32 = 1; +pub const __STDC_IEC_559__: u32 = 1; +pub const __STDC_IEC_559_COMPLEX__: u32 = 1; +pub const __STDC_ISO_10646__: u32 = 201706; +pub const __GNU_LIBRARY__: u32 = 6; +pub const __GLIBC__: u32 = 2; +pub const __GLIBC_MINOR__: u32 = 31; +pub const _SYS_CDEFS_H: u32 = 1; +pub const __glibc_c99_flexarr_available: u32 = 1; +pub const __WORDSIZE: u32 = 64; +pub const __WORDSIZE_TIME64_COMPAT32: u32 = 1; +pub const __SYSCALL_WORDSIZE: u32 = 64; +pub const __LONG_DOUBLE_USES_FLOAT128: u32 = 0; +pub const __HAVE_GENERIC_SELECTION: u32 = 1; +pub const __GLIBC_USE_LIB_EXT2: u32 = 0; +pub const __GLIBC_USE_IEC_60559_BFP_EXT: u32 = 0; +pub const __GLIBC_USE_IEC_60559_BFP_EXT_C2X: u32 = 0; +pub const __GLIBC_USE_IEC_60559_FUNCS_EXT: u32 = 0; +pub const __GLIBC_USE_IEC_60559_FUNCS_EXT_C2X: u32 = 0; +pub const __GLIBC_USE_IEC_60559_TYPES_EXT: u32 = 0; +pub const _BITS_TYPES_H: u32 = 1; +pub const __TIMESIZE: u32 = 64; +pub const _BITS_TYPESIZES_H: u32 = 1; +pub const __OFF_T_MATCHES_OFF64_T: u32 = 1; +pub const __INO_T_MATCHES_INO64_T: u32 = 1; +pub const __RLIM_T_MATCHES_RLIM64_T: u32 = 1; +pub const __STATFS_MATCHES_STATFS64: u32 = 1; +pub const __FD_SETSIZE: u32 = 1024; +pub const _BITS_TIME64_H: u32 = 1; +pub const _BITS_WCHAR_H: u32 = 1; +pub const _BITS_STDINT_INTN_H: u32 = 1; +pub const _BITS_STDINT_UINTN_H: u32 = 1; +pub const INT8_MIN: i32 = -128; +pub const INT16_MIN: i32 = -32768; +pub const INT32_MIN: i32 = -2147483648; +pub const INT8_MAX: u32 = 127; +pub const INT16_MAX: u32 = 32767; +pub const INT32_MAX: u32 = 2147483647; +pub const UINT8_MAX: u32 = 255; +pub const UINT16_MAX: u32 = 65535; +pub const UINT32_MAX: u32 = 4294967295; +pub const INT_LEAST8_MIN: i32 = -128; +pub const INT_LEAST16_MIN: i32 = -32768; +pub const INT_LEAST32_MIN: i32 = -2147483648; +pub const INT_LEAST8_MAX: u32 = 127; +pub const INT_LEAST16_MAX: u32 = 32767; +pub const INT_LEAST32_MAX: u32 = 2147483647; +pub const UINT_LEAST8_MAX: u32 = 255; +pub const UINT_LEAST16_MAX: u32 = 65535; +pub const UINT_LEAST32_MAX: u32 = 4294967295; +pub const INT_FAST8_MIN: i32 = -128; +pub const INT_FAST16_MIN: i64 = -9223372036854775808; +pub const INT_FAST32_MIN: i64 = -9223372036854775808; +pub const INT_FAST8_MAX: u32 = 127; +pub const INT_FAST16_MAX: u64 = 9223372036854775807; +pub const INT_FAST32_MAX: u64 = 9223372036854775807; +pub const UINT_FAST8_MAX: u32 = 255; +pub const UINT_FAST16_MAX: i32 = -1; +pub const UINT_FAST32_MAX: i32 = -1; +pub const INTPTR_MIN: i64 = -9223372036854775808; +pub const INTPTR_MAX: u64 = 9223372036854775807; +pub const UINTPTR_MAX: i32 = -1; +pub const PTRDIFF_MIN: i64 = -9223372036854775808; +pub const PTRDIFF_MAX: u64 = 9223372036854775807; +pub const SIG_ATOMIC_MIN: i32 = -2147483648; +pub const SIG_ATOMIC_MAX: u32 = 2147483647; +pub const SIZE_MAX: i32 = -1; +pub const WINT_MIN: u32 = 0; +pub const WINT_MAX: u32 = 4294967295; +pub type size_t = ::std::os::raw::c_ulong; +pub type wchar_t = ::std::os::raw::c_int; +#[repr(C)] +#[repr(align(16))] +#[derive(Debug, Copy, Clone)] +pub struct max_align_t { + pub __clang_max_align_nonce1: ::std::os::raw::c_longlong, + pub __bindgen_padding_0: u64, + pub __clang_max_align_nonce2: u128, +} +#[test] +fn bindgen_test_layout_max_align_t() { + assert_eq!( + ::std::mem::size_of::(), + 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_settings { + _unused: [u8; 0], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct gpiod_line_config { + _unused: [u8; 0], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct gpiod_request_config { + _unused: [u8; 0], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct gpiod_line_request { + _unused: [u8; 0], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct gpiod_info_event { + _unused: [u8; 0], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct gpiod_edge_event { + _unused: [u8; 0], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct gpiod_edge_event_buffer { + _unused: [u8; 0], +} +extern "C" { + #[doc = " @brief Open a chip by path."] + #[doc = " @param path Path to the gpiochip device file."] + #[doc = " @return GPIO chip request or NULL if an error occurred."] + pub fn gpiod_chip_open(path: *const ::std::os::raw::c_char) -> *mut gpiod_chip; +} +extern "C" { + #[doc = " @brief Close the chip and release all associated resources."] + #[doc = " @param chip Chip to close."] + pub fn gpiod_chip_close(chip: *mut gpiod_chip); +} +extern "C" { + #[doc = " @brief Get information about the chip."] + #[doc = " @param chip GPIO chip object."] + #[doc = " @return New GPIO chip info object or NULL if an error occurred. The returned"] + #[doc = " object must be freed by the caller using ::gpiod_chip_info_free."] + pub fn gpiod_chip_get_info(chip: *mut gpiod_chip) -> *mut gpiod_chip_info; +} +extern "C" { + #[doc = " @brief Get the path used to open the chip."] + #[doc = " @param chip GPIO chip object."] + #[doc = " @return Path to the file passed as argument to ::gpiod_chip_open."] + pub fn gpiod_chip_get_path(chip: *mut gpiod_chip) -> *const ::std::os::raw::c_char; +} +extern "C" { + #[doc = " @brief Get a snapshot of information about a line."] + #[doc = " @param chip GPIO chip object."] + #[doc = " @param offset The offset of the GPIO line."] + #[doc = " @return New GPIO line info object or NULL if an error occurred. The returned"] + #[doc = "\t object must be freed by the caller using ::gpiod_line_info_free."] + pub fn gpiod_chip_get_line_info( + chip: *mut gpiod_chip, + offset: ::std::os::raw::c_uint, + ) -> *mut gpiod_line_info; +} +extern "C" { + #[doc = " @brief Get a snapshot of the status of a line and start watching it for"] + #[doc = "\t future changes."] + #[doc = " @param chip GPIO chip object."] + #[doc = " @param offset The offset of the GPIO line."] + #[doc = " @return New GPIO line info object or NULL if an error occurred. The returned"] + #[doc = "\t object must be freed by the caller using ::gpiod_line_info_free."] + #[doc = " @note Line status does not include the line value. To monitor the line"] + #[doc = "\t value the line must be requested as an input with edge detection set."] + pub fn gpiod_chip_watch_line_info( + chip: *mut gpiod_chip, + offset: ::std::os::raw::c_uint, + ) -> *mut gpiod_line_info; +} +extern "C" { + #[doc = " @brief Stop watching a line for status changes."] + #[doc = " @param chip GPIO chip object."] + #[doc = " @param offset The offset of the line to stop watching."] + #[doc = " @return 0 on success, -1 on failure."] + pub fn gpiod_chip_unwatch_line_info( + chip: *mut gpiod_chip, + offset: ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Get the file descriptor associated with the chip."] + #[doc = " @param chip GPIO chip object."] + #[doc = " @return File descriptor number for the chip."] + #[doc = "\t This function never fails."] + #[doc = "\t The returned file descriptor must not be closed by the caller."] + #[doc = "\t Call ::gpiod_chip_close to close the file descriptor."] + pub fn gpiod_chip_get_fd(chip: *mut gpiod_chip) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Wait for line status change events on any of the watched lines"] + #[doc = "\t on the chip."] + #[doc = " @param chip GPIO chip object."] + #[doc = " @param timeout_ns Wait time limit in nanoseconds. If set to 0, the function"] + #[doc = "\t\t returns immediatelly. If set to a negative number, the"] + #[doc = "\t\t function blocks indefinitely until an event becomes"] + #[doc = "\t\t available."] + #[doc = " @return 0 if wait timed out, -1 if an error occurred, 1 if an event is"] + #[doc = "\t pending."] + pub fn gpiod_chip_wait_info_event( + chip: *mut gpiod_chip, + timeout_ns: i64, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Read a single line status change event from the chip."] + #[doc = " @param chip GPIO chip object."] + #[doc = " @return Newly read watch event object or NULL on error. The event must be"] + #[doc = "\t freed by the caller using ::gpiod_info_event_free."] + #[doc = " @note If no events are pending, this function will block."] + pub fn gpiod_chip_read_info_event(chip: *mut gpiod_chip) -> *mut gpiod_info_event; +} +extern "C" { + #[doc = " @brief Map a line's name to its offset within the chip."] + #[doc = " @param chip GPIO chip object."] + #[doc = " @param name Name of the GPIO line to map."] + #[doc = " @return Offset of the line within the chip or -1 on error."] + #[doc = " @note If a line with given name is not exposed by the chip, the function"] + #[doc = " sets errno to ENOENT."] + pub fn gpiod_chip_get_line_offset_from_name( + chip: *mut gpiod_chip, + name: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Request a set of lines for exclusive usage."] + #[doc = " @param chip GPIO chip object."] + #[doc = " @param req_cfg Request config object. Can be NULL for default settings."] + #[doc = " @param line_cfg Line config object."] + #[doc = " @return New line request object or NULL if an error occurred. The request"] + #[doc = "\t must be released by the caller using ::gpiod_line_request_release."] + pub fn gpiod_chip_request_lines( + chip: *mut gpiod_chip, + req_cfg: *mut gpiod_request_config, + line_cfg: *mut gpiod_line_config, + ) -> *mut gpiod_line_request; +} +extern "C" { + #[doc = " @brief Free a chip info object and release all associated resources."] + #[doc = " @param info GPIO chip info object to free."] + pub fn gpiod_chip_info_free(info: *mut gpiod_chip_info); +} +extern "C" { + #[doc = " @brief Get the name of the chip as represented in the kernel."] + #[doc = " @param info GPIO chip info object."] + #[doc = " @return Pointer to a human-readable string containing the chip name."] + pub fn gpiod_chip_info_get_name(info: *mut gpiod_chip_info) -> *const ::std::os::raw::c_char; +} +extern "C" { + #[doc = " @brief Get the label of the chip as represented in the kernel."] + #[doc = " @param info GPIO chip info object."] + #[doc = " @return Pointer to a human-readable string containing the chip label."] + pub fn gpiod_chip_info_get_label(info: *mut gpiod_chip_info) -> *const ::std::os::raw::c_char; +} +extern "C" { + #[doc = " @brief Get the number of lines exposed by the chip."] + #[doc = " @param info GPIO chip info object."] + #[doc = " @return Number of GPIO lines."] + pub fn gpiod_chip_info_get_num_lines(info: *mut gpiod_chip_info) -> size_t; +} +pub const GPIOD_LINE_VALUE_INACTIVE: ::std::os::raw::c_uint = 0; +pub const GPIOD_LINE_VALUE_ACTIVE: ::std::os::raw::c_uint = 1; +#[doc = " @brief Logical line state."] +pub type _bindgen_ty_1 = ::std::os::raw::c_uint; +pub const GPIOD_LINE_DIRECTION_AS_IS: ::std::os::raw::c_uint = 1; +pub const GPIOD_LINE_DIRECTION_INPUT: ::std::os::raw::c_uint = 2; +pub const GPIOD_LINE_DIRECTION_OUTPUT: ::std::os::raw::c_uint = 3; +#[doc = " @brief Direction settings."] +pub type _bindgen_ty_2 = ::std::os::raw::c_uint; +pub const GPIOD_LINE_EDGE_NONE: ::std::os::raw::c_uint = 1; +pub const GPIOD_LINE_EDGE_RISING: ::std::os::raw::c_uint = 2; +pub const GPIOD_LINE_EDGE_FALLING: ::std::os::raw::c_uint = 3; +pub const GPIOD_LINE_EDGE_BOTH: ::std::os::raw::c_uint = 4; +#[doc = " @brief Edge detection settings."] +pub type _bindgen_ty_3 = ::std::os::raw::c_uint; +pub const GPIOD_LINE_BIAS_AS_IS: ::std::os::raw::c_uint = 1; +pub const GPIOD_LINE_BIAS_UNKNOWN: ::std::os::raw::c_uint = 2; +pub const GPIOD_LINE_BIAS_DISABLED: ::std::os::raw::c_uint = 3; +pub const GPIOD_LINE_BIAS_PULL_UP: ::std::os::raw::c_uint = 4; +pub const GPIOD_LINE_BIAS_PULL_DOWN: ::std::os::raw::c_uint = 5; +#[doc = " @brief Internal bias settings."] +pub type _bindgen_ty_4 = ::std::os::raw::c_uint; +pub const GPIOD_LINE_DRIVE_PUSH_PULL: ::std::os::raw::c_uint = 1; +pub const GPIOD_LINE_DRIVE_OPEN_DRAIN: ::std::os::raw::c_uint = 2; +pub const GPIOD_LINE_DRIVE_OPEN_SOURCE: ::std::os::raw::c_uint = 3; +#[doc = " @brief Drive settings."] +pub type _bindgen_ty_5 = ::std::os::raw::c_uint; +pub const GPIOD_LINE_EVENT_CLOCK_MONOTONIC: ::std::os::raw::c_uint = 1; +pub const GPIOD_LINE_EVENT_CLOCK_REALTIME: ::std::os::raw::c_uint = 2; +pub const GPIOD_LINE_EVENT_CLOCK_HTE: ::std::os::raw::c_uint = 3; +#[doc = " @brief Event clock settings."] +pub type _bindgen_ty_6 = ::std::os::raw::c_uint; +extern "C" { + #[doc = " @brief Free a line info object and release all associated resources."] + #[doc = " @param info GPIO line info object to free."] + pub fn gpiod_line_info_free(info: *mut gpiod_line_info); +} +extern "C" { + #[doc = " @brief Copy a line info object."] + #[doc = " @param info Line info to copy."] + #[doc = " @return Copy of the line info or NULL on error. The returned object must"] + #[doc = "\t be freed by the caller using :gpiod_line_info_free."] + pub fn gpiod_line_info_copy(info: *mut gpiod_line_info) -> *mut gpiod_line_info; +} +extern "C" { + #[doc = " @brief Get the offset of the line."] + #[doc = " @param info GPIO line info object."] + #[doc = " @return Offset of the line within the parent chip."] + #[doc = ""] + #[doc = " The offset uniquely identifies the line on the chip."] + #[doc = " The combination of the chip and offset uniquely identifies the line within"] + #[doc = " the system."] + pub fn gpiod_line_info_get_offset(info: *mut gpiod_line_info) -> ::std::os::raw::c_uint; +} +extern "C" { + #[doc = " @brief Get the name of the line."] + #[doc = " @param info GPIO line info object."] + #[doc = " @return Name of the GPIO line as it is represented in the kernel."] + #[doc = "\t This function returns a pointer to a null-terminated string"] + #[doc = "\t or NULL if the line is unnamed."] + pub fn gpiod_line_info_get_name(info: *mut gpiod_line_info) -> *const ::std::os::raw::c_char; +} +extern "C" { + #[doc = " @brief Check if the line is in use."] + #[doc = " @param info GPIO line object."] + #[doc = " @return True if the line is in use, false otherwise."] + #[doc = ""] + #[doc = " The exact reason a line is busy cannot be determined from user space."] + #[doc = " It may have been requested by another process or hogged by the kernel."] + #[doc = " It only matters that the line is used and can't be requested until"] + #[doc = " released by the existing consumer."] + pub fn gpiod_line_info_is_used(info: *mut gpiod_line_info) -> bool; +} +extern "C" { + #[doc = " @brief Get the name of the consumer of the line."] + #[doc = " @param info GPIO line info object."] + #[doc = " @return Name of the GPIO consumer as it is represented in the kernel."] + #[doc = "\t This function returns a pointer to a null-terminated string"] + #[doc = "\t or NULL if the consumer name is not set."] + pub fn gpiod_line_info_get_consumer( + info: *mut gpiod_line_info, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + #[doc = " @brief Get the direction setting of the line."] + #[doc = " @param info GPIO line info object."] + #[doc = " @return Returns ::GPIOD_LINE_DIRECTION_INPUT or"] + #[doc = "\t ::GPIOD_LINE_DIRECTION_OUTPUT."] + pub fn gpiod_line_info_get_direction(info: *mut gpiod_line_info) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Get the edge detection setting of the line."] + #[doc = " @param info GPIO line info object."] + #[doc = " @return Returns ::GPIOD_LINE_EDGE_NONE, ::GPIOD_LINE_EDGE_RISING,"] + #[doc = "\t ::GPIOD_LINE_EDGE_FALLING or ::GPIOD_LINE_EDGE_BOTH."] + pub fn gpiod_line_info_get_edge_detection(info: *mut gpiod_line_info) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Get the bias setting of the line."] + #[doc = " @param info GPIO line object."] + #[doc = " @return Returns ::GPIOD_LINE_BIAS_PULL_UP, ::GPIOD_LINE_BIAS_PULL_DOWN,"] + #[doc = "\t ::GPIOD_LINE_BIAS_DISABLED or ::GPIOD_LINE_BIAS_UNKNOWN."] + pub fn gpiod_line_info_get_bias(info: *mut gpiod_line_info) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Get the drive setting of the line."] + #[doc = " @param info GPIO line info object."] + #[doc = " @return Returns ::GPIOD_LINE_DRIVE_PUSH_PULL, ::GPIOD_LINE_DRIVE_OPEN_DRAIN"] + #[doc = "\t or ::GPIOD_LINE_DRIVE_OPEN_SOURCE."] + pub fn gpiod_line_info_get_drive(info: *mut gpiod_line_info) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Check if the logical value of the line is inverted compared to the"] + #[doc = "\t physical."] + #[doc = " @param info GPIO line object."] + #[doc = " @return True if the line is \"active-low\", false otherwise."] + pub fn gpiod_line_info_is_active_low(info: *mut gpiod_line_info) -> bool; +} +extern "C" { + #[doc = " @brief Check if the line is debounced (either by hardware or by the kernel"] + #[doc = "\t software debouncer)."] + #[doc = " @param info GPIO line info object."] + #[doc = " @return True if the line is debounced, false otherwise."] + pub fn gpiod_line_info_is_debounced(info: *mut gpiod_line_info) -> bool; +} +extern "C" { + #[doc = " @brief Get the debounce period of the line, in microseconds."] + #[doc = " @param info GPIO line info object."] + #[doc = " @return Debounce period in microseconds."] + #[doc = "\t 0 if the line is not debounced."] + pub fn gpiod_line_info_get_debounce_period_us( + info: *mut gpiod_line_info, + ) -> ::std::os::raw::c_ulong; +} +extern "C" { + #[doc = " @brief Get the event clock setting used for edge event timestamps for the"] + #[doc = "\t line."] + #[doc = " @param info GPIO line info object."] + #[doc = " @return Returns ::GPIOD_LINE_EVENT_CLOCK_MONOTONIC or"] + #[doc = "\t ::GPIOD_LINE_EVENT_CLOCK_REALTIME."] + pub fn gpiod_line_info_get_event_clock(info: *mut gpiod_line_info) -> ::std::os::raw::c_int; +} +pub const GPIOD_INFO_EVENT_LINE_REQUESTED: ::std::os::raw::c_uint = 1; +pub const GPIOD_INFO_EVENT_LINE_RELEASED: ::std::os::raw::c_uint = 2; +pub const GPIOD_INFO_EVENT_LINE_CONFIG_CHANGED: ::std::os::raw::c_uint = 3; +#[doc = " @brief Line status change event types."] +pub type _bindgen_ty_7 = ::std::os::raw::c_uint; +extern "C" { + #[doc = " @brief Free the info event object and release all associated resources."] + #[doc = " @param event Info event to free."] + pub fn gpiod_info_event_free(event: *mut gpiod_info_event); +} +extern "C" { + #[doc = " @brief Get the event type of the status change event."] + #[doc = " @param event Line status watch event."] + #[doc = " @return One of ::GPIOD_INFO_EVENT_LINE_REQUESTED,"] + #[doc = "\t ::GPIOD_INFO_EVENT_LINE_RELEASED or"] + #[doc = "\t ::GPIOD_INFO_EVENT_LINE_CONFIG_CHANGED."] + pub fn gpiod_info_event_get_event_type(event: *mut gpiod_info_event) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Get the timestamp of the event."] + #[doc = " @param event Line status watch event."] + #[doc = " @return Timestamp in nanoseconds, read from the monotonic clock."] + pub fn gpiod_info_event_get_timestamp_ns(event: *mut gpiod_info_event) -> u64; +} +extern "C" { + #[doc = " @brief Get the snapshot of line-info associated with the event."] + #[doc = " @param event Line info event object."] + #[doc = " @return Returns a pointer to the line-info object associated with the event"] + #[doc = "\t whose lifetime is tied to the event object. It must not be freed by"] + #[doc = "\t the caller."] + pub fn gpiod_info_event_get_line_info(event: *mut gpiod_info_event) -> *mut gpiod_line_info; +} +extern "C" { + #[doc = " @brief Create a new line settings object."] + #[doc = " @return New line settings object or NULL on error."] + pub fn gpiod_line_settings_new() -> *mut gpiod_line_settings; +} +extern "C" { + #[doc = " @brief Free the line settings object and release all associated resources."] + #[doc = " @param settings Line settings object."] + pub fn gpiod_line_settings_free(settings: *mut gpiod_line_settings); +} +extern "C" { + #[doc = " @brief Reset the line settings object to its default values."] + #[doc = " @param settings Line settings object."] + pub fn gpiod_line_settings_reset(settings: *mut gpiod_line_settings); +} +extern "C" { + #[doc = " @brief Copy the line settings object."] + #[doc = " @param settings Line settings object to copy."] + #[doc = " @return New line settings object that must be freed using"] + #[doc = " ::gpiod_line_settings_free or NULL on failure."] + pub fn gpiod_line_settings_copy(settings: *mut gpiod_line_settings) + -> *mut gpiod_line_settings; +} +extern "C" { + #[doc = " @brief Set direction."] + #[doc = " @param settings Line settings object."] + #[doc = " @param direction New direction."] + #[doc = " @return 0 on success, -1 on error."] + pub fn gpiod_line_settings_set_direction( + settings: *mut gpiod_line_settings, + direction: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Get direction."] + #[doc = " @param settings Line settings object."] + #[doc = " @return Current direction."] + pub fn gpiod_line_settings_get_direction( + settings: *mut gpiod_line_settings, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Set edge detection."] + #[doc = " @param settings Line settings object."] + #[doc = " @param edge New edge detection setting."] + #[doc = " @return 0 on success, -1 on failure."] + pub fn gpiod_line_settings_set_edge_detection( + settings: *mut gpiod_line_settings, + edge: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Get edge detection."] + #[doc = " @param settings Line settings object."] + #[doc = " @return Current edge detection setting."] + pub fn gpiod_line_settings_get_edge_detection( + settings: *mut gpiod_line_settings, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Set bias."] + #[doc = " @param settings Line settings object."] + #[doc = " @param bias New bias."] + #[doc = " @return 0 on success, -1 on failure."] + pub fn gpiod_line_settings_set_bias( + settings: *mut gpiod_line_settings, + bias: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Get bias."] + #[doc = " @param settings Line settings object."] + #[doc = " @return Current bias setting."] + pub fn gpiod_line_settings_get_bias( + settings: *mut gpiod_line_settings, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Set drive."] + #[doc = " @param settings Line settings object."] + #[doc = " @param drive New drive setting."] + #[doc = " @return 0 on success, -1 on failure."] + pub fn gpiod_line_settings_set_drive( + settings: *mut gpiod_line_settings, + drive: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Get drive."] + #[doc = " @param settings Line settings object."] + #[doc = " @return Current drive setting."] + pub fn gpiod_line_settings_get_drive( + settings: *mut gpiod_line_settings, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Set active-low setting."] + #[doc = " @param settings Line settings object."] + #[doc = " @param active_low New active-low setting."] + pub fn gpiod_line_settings_set_active_low(settings: *mut gpiod_line_settings, active_low: bool); +} +extern "C" { + #[doc = " @brief Get active-low setting."] + #[doc = " @param settings Line settings object."] + #[doc = " @return True if active-low is enabled, false otherwise."] + pub fn gpiod_line_settings_get_active_low(settings: *mut gpiod_line_settings) -> bool; +} +extern "C" { + #[doc = " @brief Set debounce period."] + #[doc = " @param settings Line settings object."] + #[doc = " @param period New debounce period in microseconds."] + pub fn gpiod_line_settings_set_debounce_period_us( + settings: *mut gpiod_line_settings, + period: ::std::os::raw::c_ulong, + ); +} +extern "C" { + #[doc = " @brief Get debounce period."] + #[doc = " @param settings Line settings object."] + #[doc = " @return Current debounce period in microseconds."] + pub fn gpiod_line_settings_get_debounce_period_us( + settings: *mut gpiod_line_settings, + ) -> ::std::os::raw::c_ulong; +} +extern "C" { + #[doc = " @brief Set event clock."] + #[doc = " @param settings Line settings object."] + #[doc = " @param event_clock New event clock."] + #[doc = " @return 0 on success, -1 on failure."] + pub fn gpiod_line_settings_set_event_clock( + settings: *mut gpiod_line_settings, + event_clock: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Get event clock setting."] + #[doc = " @param settings Line settings object."] + #[doc = " @return Current event clock setting."] + pub fn gpiod_line_settings_get_event_clock( + settings: *mut gpiod_line_settings, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Set the output value."] + #[doc = " @param settings Line settings object."] + #[doc = " @param value New output value."] + #[doc = " @return 0 on success, -1 on failure."] + pub fn gpiod_line_settings_set_output_value( + settings: *mut gpiod_line_settings, + value: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Get the output value."] + #[doc = " @param settings Line settings object."] + #[doc = " @return Current output value."] + pub fn gpiod_line_settings_get_output_value( + settings: *mut gpiod_line_settings, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Create a new line config object."] + #[doc = " @return New line config object or NULL on error."] + pub fn gpiod_line_config_new() -> *mut gpiod_line_config; +} +extern "C" { + #[doc = " @brief Free the line config object and release all associated resources."] + #[doc = " @param config Line config object to free."] + pub fn gpiod_line_config_free(config: *mut gpiod_line_config); +} +extern "C" { + #[doc = " @brief Reset the line config object."] + #[doc = " @param config Line config object to free."] + #[doc = ""] + #[doc = " Resets the entire configuration stored in the object. This is useful if"] + #[doc = " the user wants to reuse the object without reallocating it."] + pub fn gpiod_line_config_reset(config: *mut gpiod_line_config); +} +extern "C" { + #[doc = " @brief Add line settings for a set of offsets."] + #[doc = " @param config Line config object."] + #[doc = " @param offsets Array of offsets for which to apply the settings."] + #[doc = " @param num_offsets Number of offsets stored in the offsets array."] + #[doc = " @param settings Line settings to apply."] + #[doc = " @return 0 on success, -1 on failure."] + pub fn gpiod_line_config_add_line_settings( + config: *mut gpiod_line_config, + offsets: *const ::std::os::raw::c_uint, + num_offsets: size_t, + settings: *mut gpiod_line_settings, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Get line settings for offset."] + #[doc = " @param config Line config object."] + #[doc = " @param offset Offset for which to get line settings."] + #[doc = " @return New line settings object (must be freed by the caller) or NULL on"] + #[doc = " error."] + pub fn gpiod_line_config_get_line_settings( + config: *mut gpiod_line_config, + offset: ::std::os::raw::c_uint, + ) -> *mut gpiod_line_settings; +} +extern "C" { + #[doc = " @brief Get configured offsets."] + #[doc = " @param config Line config object."] + #[doc = " @param num_offsets Pointer to a variable in which the number of line offsets"] + #[doc = " will be stored."] + #[doc = " @param offsets Pointer to a pointer which will be set to point to an array"] + #[doc = " containing the configured offsets. The array will be allocated"] + #[doc = " using malloc() and must be freed using free()."] + #[doc = " @return 0 on success, -1 on failure."] + pub fn gpiod_line_config_get_offsets( + config: *mut gpiod_line_config, + num_offsets: *mut size_t, + offsets: *mut *mut ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Create a new request config object."] + #[doc = " @return New request config object or NULL on error."] + pub fn gpiod_request_config_new() -> *mut gpiod_request_config; +} +extern "C" { + #[doc = " @brief Free the request config object and release all associated resources."] + #[doc = " @param config Line config object."] + pub fn gpiod_request_config_free(config: *mut gpiod_request_config); +} +extern "C" { + #[doc = " @brief Set the consumer name for the request."] + #[doc = " @param config Request config object."] + #[doc = " @param consumer Consumer name."] + #[doc = " @note If the consumer string is too long, it will be truncated to the max"] + #[doc = " accepted length."] + pub fn gpiod_request_config_set_consumer( + config: *mut gpiod_request_config, + consumer: *const ::std::os::raw::c_char, + ); +} +extern "C" { + #[doc = " @brief Get the consumer name configured in the request config."] + #[doc = " @param config Request config object."] + #[doc = " @return Consumer name stored in the request config."] + pub fn gpiod_request_config_get_consumer( + config: *mut gpiod_request_config, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + #[doc = " @brief Set the size of the kernel event buffer for the request."] + #[doc = " @param config Request config object."] + #[doc = " @param event_buffer_size New event buffer size."] + #[doc = " @note The kernel may adjust the value if it's too high. If set to 0, the"] + #[doc = " default value will be used."] + #[doc = " @note The kernel buffer is distinct from and independent of the user space"] + #[doc = "\t buffer (::gpiod_edge_event_buffer_new)."] + pub fn gpiod_request_config_set_event_buffer_size( + config: *mut gpiod_request_config, + event_buffer_size: size_t, + ); +} +extern "C" { + #[doc = " @brief Get the edge event buffer size for the request config."] + #[doc = " @param config Request config object."] + #[doc = " @return Edge event buffer size setting from the request config."] + pub fn gpiod_request_config_get_event_buffer_size(config: *mut gpiod_request_config) -> size_t; +} +extern "C" { + #[doc = " @brief Release the requested lines and free all associated resources."] + #[doc = " @param request Line request object to release."] + pub fn gpiod_line_request_release(request: *mut gpiod_line_request); +} +extern "C" { + #[doc = " @brief Get the number of lines in the request."] + #[doc = " @param request Line request object."] + #[doc = " @return Number of requested lines."] + pub fn gpiod_line_request_get_num_lines(request: *mut gpiod_line_request) -> size_t; +} +extern "C" { + #[doc = " @brief Get the offsets of the lines in the request."] + #[doc = " @param request Line request object."] + #[doc = " @param offsets Array to store offsets. Must be sized to hold the number of"] + #[doc = "\t\t lines returned by ::gpiod_line_request_get_num_lines."] + pub fn gpiod_line_request_get_offsets( + request: *mut gpiod_line_request, + offsets: *mut ::std::os::raw::c_uint, + ); +} +extern "C" { + #[doc = " @brief Get the value of a single requested line."] + #[doc = " @param request Line request object."] + #[doc = " @param offset The offset of the line of which the value should be read."] + #[doc = " @return Returns 1 or 0 on success and -1 on error."] + pub fn gpiod_line_request_get_value( + request: *mut gpiod_line_request, + offset: ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Get the values of a subset of requested lines."] + #[doc = " @param request GPIO line request."] + #[doc = " @param num_values Number of lines for which to read values."] + #[doc = " @param offsets Array of offsets identifying the subset of requested lines"] + #[doc = "\t\t from which to read values."] + #[doc = " @param values Array in which the values will be stored. Must be sized"] + #[doc = "\t\t to hold \\p num_values entries. Each value is associated with the"] + #[doc = "\t\t line identified by the corresponding entry in \\p offsets."] + #[doc = " @return 0 on success, -1 on failure."] + pub fn gpiod_line_request_get_values_subset( + request: *mut gpiod_line_request, + num_values: size_t, + offsets: *const ::std::os::raw::c_uint, + values: *mut ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Get the values of all requested lines."] + #[doc = " @param request GPIO line request."] + #[doc = " @param values Array in which the values will be stored. Must be sized to"] + #[doc = "\t\t hold the number of lines returned by"] + #[doc = "\t\t ::gpiod_line_request_get_num_lines."] + #[doc = "\t\t Each value is associated with the line identified by the"] + #[doc = "\t\t corresponding entry in the offset array returned by"] + #[doc = "\t\t ::gpiod_line_request_get_offsets."] + #[doc = " @return 0 on success, -1 on failure."] + pub fn gpiod_line_request_get_values( + request: *mut gpiod_line_request, + values: *mut ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Set the value of a single requested line."] + #[doc = " @param request Line request object."] + #[doc = " @param offset The offset of the line for which the value should be set."] + #[doc = " @param value Value to set."] + pub fn gpiod_line_request_set_value( + request: *mut gpiod_line_request, + offset: ::std::os::raw::c_uint, + value: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Set the values of a subset of requested lines."] + #[doc = " @param request GPIO line request."] + #[doc = " @param num_values Number of lines for which to set values."] + #[doc = " @param offsets Array of offsets, containing the number of entries specified"] + #[doc = "\t\t by \\p num_values, identifying the requested lines for"] + #[doc = "\t\t which to set values."] + #[doc = " @param values Array of values to set, containing the number of entries"] + #[doc = "\t\t specified by \\p num_values. Each value is associated with the"] + #[doc = "\t\t line identified by the corresponding entry in \\p offsets."] + #[doc = " @return 0 on success, -1 on failure."] + pub fn gpiod_line_request_set_values_subset( + request: *mut gpiod_line_request, + num_values: size_t, + offsets: *const ::std::os::raw::c_uint, + values: *const ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Set the values of all lines associated with a request."] + #[doc = " @param request GPIO line request."] + #[doc = " @param values Array containing the values to set. Must be sized to"] + #[doc = "\t\t contain the number of lines returned by"] + #[doc = "\t\t ::gpiod_line_request_get_num_lines."] + #[doc = "\t\t Each value is associated with the line identified by the"] + #[doc = "\t\t corresponding entry in the offset array returned by"] + #[doc = "\t\t ::gpiod_line_request_get_offsets."] + pub fn gpiod_line_request_set_values( + request: *mut gpiod_line_request, + values: *const ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Update the configuration of lines associated with a line request."] + #[doc = " @param request GPIO line request."] + #[doc = " @param config New line config to apply."] + #[doc = " @return 0 on success, -1 on failure."] + #[doc = " @note The new line configuration completely replaces the old."] + #[doc = " @note Any requested lines without overrides are configured to the requested"] + #[doc = "\t defaults."] + #[doc = " @note Any configured overrides for lines that have not been requested"] + #[doc = "\t are silently ignored."] + pub fn gpiod_line_request_reconfigure_lines( + request: *mut gpiod_line_request, + config: *mut gpiod_line_config, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Get the file descriptor associated with a line request."] + #[doc = " @param request GPIO line request."] + #[doc = " @return The file descriptor associated with the request."] + #[doc = "\t This function never fails."] + #[doc = "\t The returned file descriptor must not be closed by the caller."] + #[doc = "\t Call ::gpiod_line_request_release to close the file."] + pub fn gpiod_line_request_get_fd(request: *mut gpiod_line_request) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Wait for edge events on any of the requested lines."] + #[doc = " @param request GPIO line request."] + #[doc = " @param timeout_ns Wait time limit in nanoseconds. If set to 0, the function"] + #[doc = "\t\t returns immediatelly. If set to a negative number, the"] + #[doc = "\t\t function blocks indefinitely until an event becomes"] + #[doc = "\t\t available."] + #[doc = " @return 0 if wait timed out, -1 if an error occurred, 1 if an event is"] + #[doc = "\t pending."] + #[doc = "q"] + #[doc = " Lines must have edge detection set for edge events to be emitted."] + #[doc = " By default edge detection is disabled."] + pub fn gpiod_line_request_wait_edge_event( + request: *mut gpiod_line_request, + timeout_ns: i64, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Read a number of edge events from a line request."] + #[doc = " @param request GPIO line request."] + #[doc = " @param buffer Edge event buffer, sized to hold at least \\p max_events."] + #[doc = " @param max_events Maximum number of events to read."] + #[doc = " @return On success returns the number of events read from the file"] + #[doc = "\t descriptor, on failure return -1."] + #[doc = " @note This function will block if no event was queued for the line request."] + #[doc = " @note Any exising events in the buffer are overwritten. This is not an"] + #[doc = " append operation."] + pub fn gpiod_line_request_read_edge_event( + request: *mut gpiod_line_request, + buffer: *mut gpiod_edge_event_buffer, + max_events: size_t, + ) -> ::std::os::raw::c_int; +} +pub const GPIOD_EDGE_EVENT_RISING_EDGE: ::std::os::raw::c_uint = 1; +pub const GPIOD_EDGE_EVENT_FALLING_EDGE: ::std::os::raw::c_uint = 2; +#[doc = " @brief Event types."] +pub type _bindgen_ty_8 = ::std::os::raw::c_uint; +extern "C" { + #[doc = " @brief Free the edge event object."] + #[doc = " @param event Edge event object to free."] + pub fn gpiod_edge_event_free(event: *mut gpiod_edge_event); +} +extern "C" { + #[doc = " @brief Copy the edge event object."] + #[doc = " @param event Edge event to copy."] + #[doc = " @return Copy of the edge event or NULL on error. The returned object must"] + #[doc = "\t be freed by the caller using ::gpiod_edge_event_free."] + pub fn gpiod_edge_event_copy(event: *mut gpiod_edge_event) -> *mut gpiod_edge_event; +} +extern "C" { + #[doc = " @brief Get the event type."] + #[doc = " @param event GPIO edge event."] + #[doc = " @return The event type (::GPIOD_EDGE_EVENT_RISING_EDGE or"] + #[doc = "\t ::GPIOD_EDGE_EVENT_FALLING_EDGE)."] + pub fn gpiod_edge_event_get_event_type(event: *mut gpiod_edge_event) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Get the timestamp of the event."] + #[doc = " @param event GPIO edge event."] + #[doc = " @return Timestamp in nanoseconds."] + #[doc = " @note The source clock for the timestamp depends on the event_clock"] + #[doc = "\t setting for the line."] + pub fn gpiod_edge_event_get_timestamp_ns(event: *mut gpiod_edge_event) -> u64; +} +extern "C" { + #[doc = " @brief Get the offset of the line which triggered the event."] + #[doc = " @param event GPIO edge event."] + #[doc = " @return Line offset."] + pub fn gpiod_edge_event_get_line_offset(event: *mut gpiod_edge_event) + -> ::std::os::raw::c_uint; +} +extern "C" { + #[doc = " @brief Get the global sequence number of the event."] + #[doc = " @param event GPIO edge event."] + #[doc = " @return Sequence number of the event in the series of events for all lines"] + #[doc = "\t in the associated line request."] + pub fn gpiod_edge_event_get_global_seqno( + event: *mut gpiod_edge_event, + ) -> ::std::os::raw::c_ulong; +} +extern "C" { + #[doc = " @brief Get the event sequence number specific to the line."] + #[doc = " @param event GPIO edge event."] + #[doc = " @return Sequence number of the event in the series of events only for this"] + #[doc = "\t line within the lifetime of the associated line request."] + pub fn gpiod_edge_event_get_line_seqno(event: *mut gpiod_edge_event) + -> ::std::os::raw::c_ulong; +} +extern "C" { + #[doc = " @brief Create a new edge event buffer."] + #[doc = " @param capacity Number of events the buffer can store (min = 1, max = 1024)."] + #[doc = " @return New edge event buffer or NULL on error."] + #[doc = " @note If capacity equals 0, it will be set to a default value of 64. If"] + #[doc = "\t capacity is larger than 1024, it will be limited to 1024."] + #[doc = " @note The user space buffer is independent of the kernel buffer"] + #[doc = "\t (::gpiod_request_config_set_event_buffer_size). As the user space"] + #[doc = "\t buffer is filled from the kernel buffer, there is no benefit making"] + #[doc = "\t the user space buffer larger than the kernel buffer."] + #[doc = "\t The default kernel buffer size for each request is 16*num_lines."] + pub fn gpiod_edge_event_buffer_new(capacity: size_t) -> *mut gpiod_edge_event_buffer; +} +extern "C" { + #[doc = " @brief Get the capacity (the max number of events that can be stored) of"] + #[doc = "\t the event buffer."] + #[doc = " @param buffer Edge event buffer."] + #[doc = " @return The capacity of the buffer."] + pub fn gpiod_edge_event_buffer_get_capacity(buffer: *mut gpiod_edge_event_buffer) -> size_t; +} +extern "C" { + #[doc = " @brief Free the edge event buffer and release all associated resources."] + #[doc = " @param buffer Edge event buffer to free."] + pub fn gpiod_edge_event_buffer_free(buffer: *mut gpiod_edge_event_buffer); +} +extern "C" { + #[doc = " @brief Get an event stored in the buffer."] + #[doc = " @param buffer Edge event buffer."] + #[doc = " @param index Index of the event in the buffer."] + #[doc = " @return Pointer to an event stored in the buffer. The lifetime of the"] + #[doc = "\t event is tied to the buffer object. Users must not free the event"] + #[doc = "\t returned by this function."] + pub fn gpiod_edge_event_buffer_get_event( + buffer: *mut gpiod_edge_event_buffer, + index: ::std::os::raw::c_ulong, + ) -> *mut gpiod_edge_event; +} +extern "C" { + #[doc = " @brief Get the number of events a buffer has stored."] + #[doc = " @param buffer Edge event buffer."] + #[doc = " @return Number of events stored in the buffer."] + pub fn gpiod_edge_event_buffer_get_num_events(buffer: *mut gpiod_edge_event_buffer) -> size_t; +} +extern "C" { + #[doc = " @brief Check if the file pointed to by path is a GPIO chip character device."] + #[doc = " @param path Path to check."] + #[doc = " @return True if the file exists and is either a GPIO chip character device"] + #[doc = "\t or a symbolic link to one."] + pub fn gpiod_is_gpiochip_device(path: *const ::std::os::raw::c_char) -> bool; +} +extern "C" { + #[doc = " @brief Get the API version of the library as a human-readable string."] + #[doc = " @return Human-readable string containing the library version."] + pub fn gpiod_version_string() -> *const ::std::os::raw::c_char; +} From patchwork Mon Nov 7 09:57:29 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 622479 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 C1527C4332F for ; Mon, 7 Nov 2022 09:57:59 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231360AbiKGJ55 (ORCPT ); Mon, 7 Nov 2022 04:57:57 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49236 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231374AbiKGJ5w (ORCPT ); Mon, 7 Nov 2022 04:57:52 -0500 Received: from mail-pl1-x629.google.com (mail-pl1-x629.google.com [IPv6:2607:f8b0:4864:20::629]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 206AA140DB for ; Mon, 7 Nov 2022 01:57:51 -0800 (PST) Received: by mail-pl1-x629.google.com with SMTP id y4so10599639plb.2 for ; Mon, 07 Nov 2022 01:57:51 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=qn8Xzx0J5SmBLZSJl8oM9eRA8S+vaZoL1zr2yzYWrvg=; b=SYlWpJ+vNlAaY76QEctkxwLYVeoywbQYWWy8GQHaRylZwIcaCCXyveqxevA17e3Bg2 6gnFkKKNRh+gAbhsOiZE7Ju0uC347tezfmtRq626J72cj/BS9oIYEbIzuzqT2Bjn22mX wixpsx7nJnRkFX7onW0/2Tmxe85Wn8QccNCAQZk4XngeJ0GfpX7wpXm8P1ukUwn6PP/e OW8wioT4iPV+BrXwTaOQDpjpleLtTLAtKzjj8r3an7sExvBDw0XLSM9sfSeANAtdCM09 4hcyqhNb4GLpiJ31Tf4FE3QTFGx93WKqwc9bQNrRpkMDLkggV4anJJjp5GEdjonvcVVA wuFQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=qn8Xzx0J5SmBLZSJl8oM9eRA8S+vaZoL1zr2yzYWrvg=; b=lljZHwhMFlTd4kDImSxWGEiUjOJ0/u52DAt6gw0npvW8BEL9aD6uSoBDp12dgfO0to SgcAGYaQxpLkt9mMygM+w6rDgyRyWlaUgzTVerSSZZ+O7KQMKhpw8D7uWgwjzchJ5M1n bGsEYL5fTPm1nIYwDhbOVGet1gN4usRvJ0gywxLvBJpKolavsc1AEKAaTpFwlmgFkjV/ 1BbIrruzoQlBJP1nv5efkzlAPGiNuD1eMB1y8BEPQTAKRdjvuPrWt2Ru49/i+aJBlDVy 2Mn7ANwYYBQcQ1JdTbyfNv29a9o40r17aa8IJEKu7NyO244KqdP9geQOkU8New8VH3rc ZJ8Q== X-Gm-Message-State: ACrzQf1XBCZ0DC+qtq76NF0bZtgWqd3ZNZN+jjnv+emnVFa+Ti+YMDeb 1kiMcT6HXkrvcScnABO6/T+FhQ== X-Google-Smtp-Source: AMsMyM7jkrn+Tp9CgfGbGw3ZSMvXmrnsezjo5afX5G2el+CbqA3kb09awVufBrzP4ukxuuXtlU2nAg== X-Received: by 2002:a17:903:3293:b0:186:867b:e24 with SMTP id jh19-20020a170903329300b00186867b0e24mr49323167plb.127.1667815070352; Mon, 07 Nov 2022 01:57:50 -0800 (PST) Received: from localhost ([122.172.84.80]) by smtp.gmail.com with ESMTPSA id p67-20020a625b46000000b005625d6d2999sm4047259pfb.187.2022.11.07.01.57.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 07 Nov 2022 01:57:50 -0800 (PST) From: Viresh Kumar To: Linus Walleij , Bartosz Golaszewski , Kent Gibson Cc: Viresh Kumar , Vincent Guittot , linux-gpio@vger.kernel.org, Miguel Ojeda , Wedson Almeida Filho , =?utf-8?q?Alex_Benn=C3=A9e?= , stratos-dev@op-lists.linaro.org, Gerard Ryan , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , y86-dev Subject: [libgpiod][PATCH V9 3/8] bindings: rust: Add gpiosim crate Date: Mon, 7 Nov 2022 15:27:29 +0530 Message-Id: <48a122d51e85768a36e77b8316cb86e3bbcd25a8.1667815011.git.viresh.kumar@linaro.org> X-Mailer: git-send-email 2.31.1.272.g89b43f80a514 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org This adds gpiosim rust crate, which provides helpers to emulate GPIO chips. Signed-off-by: Viresh Kumar --- bindings/rust/Cargo.toml | 1 + bindings/rust/gpiosim/Cargo.toml | 22 ++ bindings/rust/gpiosim/build.rs | 43 ++++ bindings/rust/gpiosim/src/lib.rs | 79 ++++++++ bindings/rust/gpiosim/src/sim.rs | 331 +++++++++++++++++++++++++++++++ 5 files changed, 476 insertions(+) create mode 100644 bindings/rust/gpiosim/Cargo.toml create mode 100644 bindings/rust/gpiosim/build.rs create mode 100644 bindings/rust/gpiosim/src/lib.rs create mode 100644 bindings/rust/gpiosim/src/sim.rs diff --git a/bindings/rust/Cargo.toml b/bindings/rust/Cargo.toml index c7bbcc798920..b9eea6b3a5ea 100644 --- a/bindings/rust/Cargo.toml +++ b/bindings/rust/Cargo.toml @@ -1,5 +1,6 @@ [workspace] members = [ + "gpiosim", "libgpiod-sys" ] diff --git a/bindings/rust/gpiosim/Cargo.toml b/bindings/rust/gpiosim/Cargo.toml new file mode 100644 index 000000000000..f8f7e7f79b77 --- /dev/null +++ b/bindings/rust/gpiosim/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "gpiosim" +version = "0.1.0" +authors = ["Viresh Kumar "] +description = "gpiosim header bindings" +repository = "https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git" +categories = ["external-ffi-bindings", "os::linux-apis"] +rust-version = "1.56" +keywords = ["libgpiod", "gpio", "gpiosim"] +license = "Apache-2.0 OR BSD-3-Clause" +edition = "2021" + +[dependencies] +errno = "0.2.8" +libgpiod = { path = "../libgpiod" } + +[features] +generate = [ "bindgen" ] + +[build-dependencies] +bindgen = { version = "0.59.1", optional = true } +cc = "1.0.46" diff --git a/bindings/rust/gpiosim/build.rs b/bindings/rust/gpiosim/build.rs new file mode 100644 index 000000000000..460fb8c092c3 --- /dev/null +++ b/bindings/rust/gpiosim/build.rs @@ -0,0 +1,43 @@ +#[cfg(feature = "generate")] +extern crate bindgen; +#[cfg(feature = "generate")] +use std::env; +#[cfg(feature = "generate")] +use std::path::PathBuf; + +#[cfg(feature = "generate")] +fn generate_bindings() { + // Tell cargo to invalidate the built crate whenever following files change + println!("cargo:rerun-if-changed=../../../tests/gpiosim/gpiosim.h"); + + // The bindgen::Builder is the main entry point + // to bindgen, and lets you build up options for + // the resulting bindings. + let bindings = bindgen::Builder::default() + // The input header we would like to generate + // bindings for. + .header("../../../tests/gpiosim/gpiosim.h") + // Tell cargo to invalidate the built crate whenever any of the + // included header files changed. + .parse_callbacks(Box::new(bindgen::CargoCallbacks)) + // Finish the builder and generate the bindings. + .generate() + // Unwrap the Result and panic on failure. + .expect("Unable to generate bindings"); + + // Write the bindings to the $OUT_DIR/bindings.rs file. + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Couldn't write bindings!"); +} + +fn main() { + #[cfg(feature = "generate")] + generate_bindings(); + + println!("cargo:rustc-link-lib=kmod"); + println!("cargo:rustc-link-lib=mount"); + println!("cargo:rustc-link-search=./../../tests/gpiosim/.libs/"); + println!("cargo:rustc-link-lib=static=gpiosim"); +} diff --git a/bindings/rust/gpiosim/src/lib.rs b/bindings/rust/gpiosim/src/lib.rs new file mode 100644 index 000000000000..e76870e4a094 --- /dev/null +++ b/bindings/rust/gpiosim/src/lib.rs @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use libgpiod::{Error, Result}; + +#[allow(non_camel_case_types, non_upper_case_globals)] +#[cfg_attr(test, allow(deref_nullptr, non_snake_case))] +#[allow(dead_code)] +mod bindings_raw { + #[cfg(feature = "generate")] + include!(concat!(env!("OUT_DIR"), "/bindings.rs")); + + #[cfg(not(feature = "generate"))] + include!("bindings.rs"); +} +use bindings_raw::*; + +mod sim; +pub use sim::*; + +/// Value settings. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum Value { + /// Active + Active, + /// Inactive + InActive, +} + +impl Value { + pub(crate) fn new(val: u32) -> Result { + match val { + GPIOSIM_VALUE_INACTIVE => Ok(Value::InActive), + GPIOSIM_VALUE_ACTIVE => Ok(Value::Active), + _ => Err(Error::InvalidEnumValue("Value", val as u32)), + } + } +} + +/// Direction settings. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum Direction { + /// Direction is input - for reading the value of an externally driven GPIO line. + Input, + /// Direction is output - for driving the GPIO line, value is high. + OutputHigh, + /// Direction is output - for driving the GPIO line, value is low. + OutputLow, +} + +impl Direction { + fn val(self) -> i32 { + (match self { + Direction::Input => GPIOSIM_HOG_DIR_INPUT, + Direction::OutputHigh => GPIOSIM_HOG_DIR_OUTPUT_HIGH, + Direction::OutputLow => GPIOSIM_HOG_DIR_OUTPUT_LOW, + }) as i32 + } +} + +/// Internal pull settings. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum Pull { + /// The internal pull-up is enabled. + Up, + /// The internal pull-down is enabled. + Down, +} + +impl Pull { + fn val(self) -> i32 { + (match self { + Pull::Up => GPIOSIM_PULL_UP, + Pull::Down => GPIOSIM_PULL_DOWN, + }) as i32 + } +} diff --git a/bindings/rust/gpiosim/src/sim.rs b/bindings/rust/gpiosim/src/sim.rs new file mode 100644 index 000000000000..6cafda0d90e2 --- /dev/null +++ b/bindings/rust/gpiosim/src/sim.rs @@ -0,0 +1,331 @@ +// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use std::ffi::{CStr, CString}; +use std::os::raw::c_char; +use std::path::PathBuf; +use std::str; + +use libgpiod::{line::Offset, Error, OperationType, Result}; + +use crate::*; + +/// Sim Ctx +#[derive(Debug)] +struct SimCtx { + ctx: *mut gpiosim_ctx, +} + +// Safe as the pointer is guaranteed to be valid and the associated resource +// won't be freed until the object is dropped. +unsafe impl Send for SimCtx {} + +impl SimCtx { + fn new() -> Result { + // SAFETY: `gpiosim_ctx` returned by gpiosim is guaranteed to live + // as long as the `struct SimCtx`. + let ctx = unsafe { gpiosim_ctx_new() }; + if ctx.is_null() { + return Err(Error::OperationFailed( + OperationType::SimCtxNew, + errno::errno(), + )); + } + + Ok(Self { ctx }) + } +} + +impl Drop for SimCtx { + fn drop(&mut self) { + // SAFETY: `gpiosim_ctx` is guaranteed to be valid here. + unsafe { gpiosim_ctx_unref(self.ctx) } + } +} + +/// Sim Dev +#[derive(Debug)] +struct SimDev { + dev: *mut gpiosim_dev, +} + +// Safe as the pointer is guaranteed to be valid and the associated resource +// won't be freed until the object is dropped. +unsafe impl Send for SimDev {} + +impl SimDev { + fn new(ctx: &SimCtx) -> Result { + // SAFETY: `gpiosim_dev` returned by gpiosim is guaranteed to live + // as long as the `struct SimDev`. + let dev = unsafe { gpiosim_dev_new(ctx.ctx) }; + if dev.is_null() { + return Err(Error::OperationFailed( + OperationType::SimDevNew, + errno::errno(), + )); + } + + Ok(Self { dev }) + } + + fn enable(&self) -> Result<()> { + // SAFETY: `gpiosim_dev` is guaranteed to be valid here. + let ret = unsafe { gpiosim_dev_enable(self.dev) }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::SimDevEnable, + errno::errno(), + )) + } else { + Ok(()) + } + } + + fn disable(&self) -> Result<()> { + // SAFETY: `gpiosim_dev` is guaranteed to be valid here. + let ret = unsafe { gpiosim_dev_disable(self.dev) }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::SimDevDisable, + errno::errno(), + )) + } else { + Ok(()) + } + } +} + +impl Drop for SimDev { + fn drop(&mut self) { + // SAFETY: `gpiosim_dev` is guaranteed to be valid here. + unsafe { gpiosim_dev_unref(self.dev) } + } +} + +/// Sim Bank +#[derive(Debug)] +struct SimBank { + bank: *mut gpiosim_bank, +} + +// Safe as the pointer is guaranteed to be valid and the associated resource +// won't be freed until the object is dropped. +unsafe impl Send for SimBank {} + +impl SimBank { + fn new(dev: &SimDev) -> Result { + // SAFETY: `gpiosim_bank` returned by gpiosim is guaranteed to live + // as long as the `struct SimBank`. + let bank = unsafe { gpiosim_bank_new(dev.dev) }; + if bank.is_null() { + return Err(Error::OperationFailed( + OperationType::SimBankNew, + errno::errno(), + )); + } + + Ok(Self { bank }) + } + + fn chip_name(&self) -> Result<&str> { + // SAFETY: The string returned by gpiosim is guaranteed to live as long + // as the `struct SimBank`. + let name = unsafe { gpiosim_bank_get_chip_name(self.bank) }; + + // SAFETY: The string is guaranteed to be valid here. + unsafe { CStr::from_ptr(name) } + .to_str() + .map_err(Error::StringNotUtf8) + } + + fn dev_path(&self) -> Result { + // SAFETY: The string returned by gpiosim is guaranteed to live as long + // as the `struct SimBank`. + let path = unsafe { gpiosim_bank_get_dev_path(self.bank) }; + + // SAFETY: The string is guaranteed to be valid here. + let path = unsafe { CStr::from_ptr(path) } + .to_str() + .map_err(Error::StringNotUtf8)?; + + Ok(PathBuf::from(path)) + } + + fn val(&self, offset: Offset) -> Result { + // SAFETY: `gpiosim_bank` is guaranteed to be valid here. + let ret = unsafe { gpiosim_bank_get_value(self.bank, offset) }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::SimBankGetVal, + errno::errno(), + )) + } else { + Value::new(ret as u32) + } + } + + fn set_label(&self, label: &str) -> Result<()> { + let label = CString::new(label).map_err(|_| Error::InvalidString)?; + + // SAFETY: `gpiosim_bank` is guaranteed to be valid here. + let ret = unsafe { gpiosim_bank_set_label(self.bank, label.as_ptr() as *const c_char) }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::SimBankSetLabel, + errno::errno(), + )) + } else { + Ok(()) + } + } + + fn set_num_lines(&self, num: usize) -> Result<()> { + // SAFETY: `gpiosim_bank` is guaranteed to be valid here. + let ret = unsafe { gpiosim_bank_set_num_lines(self.bank, num.try_into().unwrap()) }; + if ret == -1 { + Err(Error::OperationFailed( + OperationType::SimBankSetNumLines, + errno::errno(), + )) + } else { + Ok(()) + } + } + + fn set_line_name(&self, offset: Offset, name: &str) -> Result<()> { + let name = CString::new(name).map_err(|_| Error::InvalidString)?; + + // SAFETY: `gpiosim_bank` is guaranteed to be valid here. + let ret = unsafe { + gpiosim_bank_set_line_name(self.bank, offset, name.as_ptr() as *const c_char) + }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::SimBankSetLineName, + errno::errno(), + )) + } else { + Ok(()) + } + } + + fn set_pull(&self, offset: Offset, pull: Pull) -> Result<()> { + // SAFETY: `gpiosim_bank` is guaranteed to be valid here. + let ret = unsafe { gpiosim_bank_set_pull(self.bank, offset, pull.val()) }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::SimBankSetPull, + errno::errno(), + )) + } else { + Ok(()) + } + } + + fn hog_line(&self, offset: Offset, name: &str, dir: Direction) -> Result<()> { + let name = CString::new(name).map_err(|_| Error::InvalidString)?; + + // SAFETY: `gpiosim_bank` is guaranteed to be valid here. + let ret = unsafe { + gpiosim_bank_hog_line(self.bank, offset, name.as_ptr() as *const c_char, dir.val()) + }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::SimBankHogLine, + errno::errno(), + )) + } else { + Ok(()) + } + } +} + +impl Drop for SimBank { + fn drop(&mut self) { + // SAFETY: `gpiosim_bank` is guaranteed to be valid here. + unsafe { gpiosim_bank_unref(self.bank) } + } +} + +/// GPIO SIM +#[derive(Debug)] +pub struct Sim { + _ctx: SimCtx, + dev: SimDev, + bank: SimBank, +} + +impl Sim { + pub fn new(ngpio: Option, label: Option<&str>, enable: bool) -> Result { + let ctx = SimCtx::new()?; + let dev = SimDev::new(&ctx)?; + let bank = SimBank::new(&dev)?; + + if let Some(ngpio) = ngpio { + bank.set_num_lines(ngpio)?; + } + + if let Some(label) = label { + bank.set_label(label)?; + } + + if enable { + dev.enable()?; + } + + Ok(Self { + _ctx: ctx, + dev, + bank, + }) + } + + pub fn chip_name(&self) -> &str { + self.bank.chip_name().unwrap() + } + + pub fn dev_path(&self) -> PathBuf { + self.bank.dev_path().unwrap() + } + + pub fn val(&self, offset: Offset) -> Result { + self.bank.val(offset) + } + + pub fn set_label(&self, label: &str) -> Result<()> { + self.bank.set_label(label) + } + + pub fn set_num_lines(&self, num: usize) -> Result<()> { + self.bank.set_num_lines(num) + } + + pub fn set_line_name(&self, offset: Offset, name: &str) -> Result<()> { + self.bank.set_line_name(offset, name) + } + + pub fn set_pull(&self, offset: Offset, pull: Pull) -> Result<()> { + self.bank.set_pull(offset, pull) + } + + pub fn hog_line(&self, offset: Offset, name: &str, dir: Direction) -> Result<()> { + self.bank.hog_line(offset, name, dir) + } + + pub fn enable(&self) -> Result<()> { + self.dev.enable() + } + + pub fn disable(&self) -> Result<()> { + self.dev.disable() + } +} From patchwork Mon Nov 7 09:57:30 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 622478 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 BE507C43217 for ; Mon, 7 Nov 2022 09:58:00 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231627AbiKGJ6A (ORCPT ); Mon, 7 Nov 2022 04:58:00 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49254 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231526AbiKGJ5z (ORCPT ); Mon, 7 Nov 2022 04:57:55 -0500 Received: from mail-pj1-x1034.google.com (mail-pj1-x1034.google.com [IPv6:2607:f8b0:4864:20::1034]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 308962D2 for ; Mon, 7 Nov 2022 01:57:54 -0800 (PST) Received: by mail-pj1-x1034.google.com with SMTP id q1-20020a17090a750100b002139ec1e999so9807599pjk.1 for ; Mon, 07 Nov 2022 01:57:54 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=SjJiMlrs/ZeYBiv0yhVqMLyEDnGsJrY4JvzsUIgfAw4=; b=ohdUh0hDtYqxVE2XKYDDZMWmWatoHpRuACZbr2VawYqyC+ENVdKZoXz5B/G8td55Qi V9CcPGcoHmTVhBqQSquL19rZ1mIsByZ83kTrobjBbpHEbcWuc57DPRdJoLcdwBLNAy7/ 0z3FdiAZWODLuk+spIFQppnQFMTIDMfvD5/OF6rYKp4TdFqVOxfv12ZLbCfjOMUDpPQC 9r/d0woWfVNc/KeBH2ZJGdmosEroM6Hs1B0407xaNwcKpeWce11wZxMPkz2fv+4ry44x BrlAS8mMByft8/98wSKN4xz5ArN0VeYoBFuXwxYf4uXeXWvAG1Gw1V/6VFcIM2hfGgGa VvZA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=SjJiMlrs/ZeYBiv0yhVqMLyEDnGsJrY4JvzsUIgfAw4=; b=tQzFh3wxKRh502iP3uZecYpGn3iAjttflIAi0q5rZzHpXbF8liI6m1nViUI8dMLeql fY1qitNYolxy3ISZjkwqDTMBCR0kY5L3c7xwBLClIIsB2Pd2mwKdNc5k9UMfZFE4SyyR dwVRrnTAXcj9CPtpHdpb13E1egpRObtSyLFL9k9TygPFlyCwP1ZolBVskzwzON4xv2wb svqmdmm6THhYvZJyAztVFEKNRjdsfpLuAVIVlwXv7QEQCLi7spLnURQ7rKdEsxU9UDJC ZzJzkWsZIAFDfTNuSFHCQb3f+2pLmTnnF/IKRxrdc40HWuF5oDbUj0H3B3UUUu8NW8OG F0cg== X-Gm-Message-State: ACrzQf3xQjpoKVST4asuRA/0PfY814t8zOYzsx2pq+VyxEwku6B6sIsi W8Ex3MbVhI8SAf1O8sa4mV4DgA== X-Google-Smtp-Source: AMsMyM5RlZpYFTKlCM8qDFO0SNunz3ajlFT7Kf0s9rYvs/vfaRkwJTvELzuMF7KzfjRpi7ipaGth8Q== X-Received: by 2002:a17:90b:153:b0:213:b853:5db1 with SMTP id em19-20020a17090b015300b00213b8535db1mr47900043pjb.168.1667815073699; Mon, 07 Nov 2022 01:57:53 -0800 (PST) Received: from localhost ([122.172.84.80]) by smtp.gmail.com with ESMTPSA id h5-20020aa79f45000000b0056b9df2a15esm4091141pfr.62.2022.11.07.01.57.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 07 Nov 2022 01:57:53 -0800 (PST) From: Viresh Kumar To: Linus Walleij , Bartosz Golaszewski , Kent Gibson Cc: Viresh Kumar , Vincent Guittot , linux-gpio@vger.kernel.org, Miguel Ojeda , Wedson Almeida Filho , =?utf-8?q?Alex_Benn=C3=A9e?= , stratos-dev@op-lists.linaro.org, Gerard Ryan , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , y86-dev Subject: [libgpiod][PATCH V9 4/8] bindings: rust: Add pre generated bindings for gpiosim Date: Mon, 7 Nov 2022 15:27:30 +0530 Message-Id: <401d9417d895b8b1b19ca577c84347d89f7e0fbd.1667815011.git.viresh.kumar@linaro.org> X-Mailer: git-send-email 2.31.1.272.g89b43f80a514 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org This adds a copy of pre generated bindings and adds the suggested way of updating those in README. Signed-off-by: Viresh Kumar --- bindings/rust/gpiosim/README.md | 11 ++ bindings/rust/gpiosim/src/bindings.rs | 180 ++++++++++++++++++++++++++ 2 files changed, 191 insertions(+) create mode 100644 bindings/rust/gpiosim/README.md create mode 100644 bindings/rust/gpiosim/src/bindings.rs diff --git a/bindings/rust/gpiosim/README.md b/bindings/rust/gpiosim/README.md new file mode 100644 index 000000000000..acb84543dbdc --- /dev/null +++ b/bindings/rust/gpiosim/README.md @@ -0,0 +1,11 @@ +# Generated gpiosim Rust FFI bindings +Automatically generated Rust FFI bindings via + [bindgen](https://github.com/rust-lang/rust-bindgen). + +## Updating bindings +1. Clone the source from + +2. run `cd libgpiod/bindings/rust/gpiosim/` +2. run `cargo build --features generate` +3. Copy the bindings 'cp ../target/debug/build/gpiosim-###/out/bindings.rs src/bindings.rs' +4. Commit changes in `src/bindings.rs` diff --git a/bindings/rust/gpiosim/src/bindings.rs b/bindings/rust/gpiosim/src/bindings.rs new file mode 100644 index 000000000000..bac347f1aab9 --- /dev/null +++ b/bindings/rust/gpiosim/src/bindings.rs @@ -0,0 +1,180 @@ +/* automatically generated by rust-bindgen 0.59.2 */ + +pub const true_: u32 = 1; +pub const false_: u32 = 0; +pub const __bool_true_false_are_defined: u32 = 1; +pub type size_t = ::std::os::raw::c_ulong; +pub type wchar_t = ::std::os::raw::c_int; +#[repr(C)] +#[repr(align(16))] +#[derive(Debug, Copy, Clone)] +pub struct max_align_t { + pub __clang_max_align_nonce1: ::std::os::raw::c_longlong, + pub __bindgen_padding_0: u64, + pub __clang_max_align_nonce2: u128, +} +#[test] +fn bindgen_test_layout_max_align_t() { + assert_eq!( + ::std::mem::size_of::(), + 32usize, + concat!("Size of: ", stringify!(max_align_t)) + ); + assert_eq!( + ::std::mem::align_of::(), + 16usize, + concat!("Alignment of ", stringify!(max_align_t)) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).__clang_max_align_nonce1 as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(max_align_t), + "::", + stringify!(__clang_max_align_nonce1) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).__clang_max_align_nonce2 as *const _ as usize + }, + 16usize, + concat!( + "Offset of field: ", + stringify!(max_align_t), + "::", + stringify!(__clang_max_align_nonce2) + ) + ); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct gpiosim_ctx { + _unused: [u8; 0], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct gpiosim_dev { + _unused: [u8; 0], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct gpiosim_bank { + _unused: [u8; 0], +} +pub const GPIOSIM_VALUE_INACTIVE: ::std::os::raw::c_uint = 0; +pub const GPIOSIM_VALUE_ACTIVE: ::std::os::raw::c_uint = 1; +pub type _bindgen_ty_1 = ::std::os::raw::c_uint; +pub const GPIOSIM_PULL_DOWN: ::std::os::raw::c_uint = 1; +pub const GPIOSIM_PULL_UP: ::std::os::raw::c_uint = 2; +pub type _bindgen_ty_2 = ::std::os::raw::c_uint; +pub const GPIOSIM_HOG_DIR_INPUT: ::std::os::raw::c_uint = 1; +pub const GPIOSIM_HOG_DIR_OUTPUT_HIGH: ::std::os::raw::c_uint = 2; +pub const GPIOSIM_HOG_DIR_OUTPUT_LOW: ::std::os::raw::c_uint = 3; +pub type _bindgen_ty_3 = ::std::os::raw::c_uint; +extern "C" { + pub fn gpiosim_ctx_new() -> *mut gpiosim_ctx; +} +extern "C" { + pub fn gpiosim_ctx_ref(ctx: *mut gpiosim_ctx) -> *mut gpiosim_ctx; +} +extern "C" { + pub fn gpiosim_ctx_unref(ctx: *mut gpiosim_ctx); +} +extern "C" { + pub fn gpiosim_dev_new(ctx: *mut gpiosim_ctx) -> *mut gpiosim_dev; +} +extern "C" { + pub fn gpiosim_dev_ref(dev: *mut gpiosim_dev) -> *mut gpiosim_dev; +} +extern "C" { + pub fn gpiosim_dev_unref(dev: *mut gpiosim_dev); +} +extern "C" { + pub fn gpiosim_dev_get_ctx(dev: *mut gpiosim_dev) -> *mut gpiosim_ctx; +} +extern "C" { + pub fn gpiosim_dev_get_name(dev: *mut gpiosim_dev) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn gpiosim_dev_enable(dev: *mut gpiosim_dev) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn gpiosim_dev_disable(dev: *mut gpiosim_dev) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn gpiosim_dev_is_live(dev: *mut gpiosim_dev) -> bool; +} +extern "C" { + pub fn gpiosim_bank_new(dev: *mut gpiosim_dev) -> *mut gpiosim_bank; +} +extern "C" { + pub fn gpiosim_bank_ref(bank: *mut gpiosim_bank) -> *mut gpiosim_bank; +} +extern "C" { + pub fn gpiosim_bank_unref(bank: *mut gpiosim_bank); +} +extern "C" { + pub fn gpiosim_bank_get_dev(bank: *mut gpiosim_bank) -> *mut gpiosim_dev; +} +extern "C" { + pub fn gpiosim_bank_get_chip_name(bank: *mut gpiosim_bank) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn gpiosim_bank_get_dev_path(bank: *mut gpiosim_bank) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn gpiosim_bank_set_label( + bank: *mut gpiosim_bank, + label: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn gpiosim_bank_set_num_lines( + bank: *mut gpiosim_bank, + num_lines: size_t, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn gpiosim_bank_set_line_name( + bank: *mut gpiosim_bank, + offset: ::std::os::raw::c_uint, + name: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn gpiosim_bank_hog_line( + bank: *mut gpiosim_bank, + offset: ::std::os::raw::c_uint, + name: *const ::std::os::raw::c_char, + direction: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn gpiosim_bank_clear_hog( + bank: *mut gpiosim_bank, + offset: ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn gpiosim_bank_get_value( + bank: *mut gpiosim_bank, + offset: ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn gpiosim_bank_get_pull( + bank: *mut gpiosim_bank, + offset: ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn gpiosim_bank_set_pull( + bank: *mut gpiosim_bank, + offset: ::std::os::raw::c_uint, + pull: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} From patchwork Mon Nov 7 09:57:31 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 622477 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 9D156C433FE for ; Mon, 7 Nov 2022 09:58:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231149AbiKGJ6E (ORCPT ); Mon, 7 Nov 2022 04:58:04 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49290 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231589AbiKGJ6C (ORCPT ); Mon, 7 Nov 2022 04:58:02 -0500 Received: from mail-pg1-x52f.google.com (mail-pg1-x52f.google.com [IPv6:2607:f8b0:4864:20::52f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 24B592D2 for ; Mon, 7 Nov 2022 01:57:59 -0800 (PST) Received: by mail-pg1-x52f.google.com with SMTP id 6so1868675pgm.6 for ; Mon, 07 Nov 2022 01:57:59 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=ktkT+1+Q/QiLpB8weu9Eag4DDbuBTsF2KGDqyh1dZsg=; b=gumM59ITyyJST9vASd68TFL0Mb1hD2yiSXiOrpSF++nyebAh/euyuBSZO3LgOYaD9k AFkRAghXGIg7IafKKisyhqV0dStuBHdJ8GayhTaVxiiuFsSG+onSCYa9yJv48sXNpRSQ Z6p5xCj0A/S+vI3oLWpzrW+56MFGE//H0T8Kgujsoh/ffkwkd+unvi+hhjwnfBS1r8ne UlbHOrorBRHa7Sld/I3Y5gxWNZDSAvpfn51Tf1d/+uW3xpofhPmyL+HPY+HkdA0kPnmp BhJA3DH3ajvqYV61AB6S39mpaYSGRQ/IeU8vGWp/riPY9JUtU7/+XEKKA5NX3WpnVQ6Y zksA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=ktkT+1+Q/QiLpB8weu9Eag4DDbuBTsF2KGDqyh1dZsg=; b=SoIkyOxdwMnVhg09hiDpl8apR/9+J48QPjLecHLVt5FV1SKmYxTANAg4Uz/cRn0pQl Pq1ryEntLjMp8ZzwS/6zL/gYfrsfGhwwKhYm3tJLf6EpGuBXJQ4gyOgLqmDn3aSgIldS apt7pLgtwZByrDKp8kVpxHhitQiBjwLIJu0yRT58Jw1sSbZfPCPXFoMeTDO9x7XKOeN2 1srZSj4cDBL9eLMMYBw3u/Vu43BLuhAJWialA+5550AqflVV1nO9umpk/Hj4KfLGQmwM Kz/nZmnpy6e/w6qphatWPjBB+TNfMi0aLufjuy4AxYpjyVKSTuIMJLWMuYhh+NikSAjW 39cA== X-Gm-Message-State: ACrzQf1ycY/YTNHPP4JlUGEizrFV5Ll/xehqKgQlku0ley5RXUKB3xZA qMUbp+0tHzsbyuAn2DTAvFXqxg== X-Google-Smtp-Source: AMsMyM49yawn1+WBvMpho+Yi+z3oTvZ7TZsDD/J35MDVcPgVxPYl1TI9M37s18QuN4ArxuOunL+KWw== X-Received: by 2002:a63:f47:0:b0:46a:e00c:24c7 with SMTP id 7-20020a630f47000000b0046ae00c24c7mr42790963pgp.239.1667815077257; Mon, 07 Nov 2022 01:57:57 -0800 (PST) Received: from localhost ([122.172.84.80]) by smtp.gmail.com with ESMTPSA id a15-20020aa78e8f000000b00565c8634e55sm4048848pfr.135.2022.11.07.01.57.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 07 Nov 2022 01:57:56 -0800 (PST) From: Viresh Kumar To: Linus Walleij , Bartosz Golaszewski , Kent Gibson Cc: Viresh Kumar , Vincent Guittot , linux-gpio@vger.kernel.org, Miguel Ojeda , Wedson Almeida Filho , =?utf-8?q?Alex_Benn=C3=A9e?= , stratos-dev@op-lists.linaro.org, Gerard Ryan , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , y86-dev Subject: [libgpiod][PATCH V9 5/8] bindings: rust: Add libgpiod crate Date: Mon, 7 Nov 2022 15:27:31 +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 | 1 + bindings/rust/libgpiod/Cargo.toml | 18 + bindings/rust/libgpiod/src/chip.rs | 310 ++++++++++++ bindings/rust/libgpiod/src/edge_event.rs | 93 ++++ bindings/rust/libgpiod/src/event_buffer.rs | 167 +++++++ bindings/rust/libgpiod/src/info_event.rs | 69 +++ bindings/rust/libgpiod/src/lib.rs | 479 +++++++++++++++++++ bindings/rust/libgpiod/src/line_config.rs | 135 ++++++ bindings/rust/libgpiod/src/line_info.rs | 162 +++++++ bindings/rust/libgpiod/src/line_request.rs | 227 +++++++++ bindings/rust/libgpiod/src/line_settings.rs | 297 ++++++++++++ bindings/rust/libgpiod/src/request_config.rs | 95 ++++ 12 files changed, 2053 insertions(+) create mode 100644 bindings/rust/libgpiod/Cargo.toml create mode 100644 bindings/rust/libgpiod/src/chip.rs create mode 100644 bindings/rust/libgpiod/src/edge_event.rs create mode 100644 bindings/rust/libgpiod/src/event_buffer.rs create mode 100644 bindings/rust/libgpiod/src/info_event.rs create mode 100644 bindings/rust/libgpiod/src/lib.rs create mode 100644 bindings/rust/libgpiod/src/line_config.rs create mode 100644 bindings/rust/libgpiod/src/line_info.rs create mode 100644 bindings/rust/libgpiod/src/line_request.rs create mode 100644 bindings/rust/libgpiod/src/line_settings.rs create mode 100644 bindings/rust/libgpiod/src/request_config.rs diff --git a/bindings/rust/Cargo.toml b/bindings/rust/Cargo.toml index b9eea6b3a5ea..4fdf4e06ff90 100644 --- a/bindings/rust/Cargo.toml +++ b/bindings/rust/Cargo.toml @@ -2,5 +2,6 @@ members = [ "gpiosim", + "libgpiod", "libgpiod-sys" ] diff --git a/bindings/rust/libgpiod/Cargo.toml b/bindings/rust/libgpiod/Cargo.toml new file mode 100644 index 000000000000..65ca079b5e15 --- /dev/null +++ b/bindings/rust/libgpiod/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "libgpiod" +version = "0.1.0" +authors = ["Viresh Kumar "] +description = "libgpiod wrappers" +repository = "https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git" +categories = ["api-bindings", "hardware-support", "embedded", "os::linux-apis"] +rust-version = "1.56" +keywords = ["libgpiod", "gpio"] +license = "Apache-2.0 OR BSD-3-Clause" +edition = "2021" + +[dependencies] +errno = "0.2.8" +intmap = "2.0.0" +libc = "0.2.39" +libgpiod-sys = { path = "../libgpiod-sys" } +thiserror = "1.0" diff --git a/bindings/rust/libgpiod/src/chip.rs b/bindings/rust/libgpiod/src/chip.rs new file mode 100644 index 000000000000..d51f3a57c871 --- /dev/null +++ b/bindings/rust/libgpiod/src/chip.rs @@ -0,0 +1,310 @@ +// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +pub mod info { + /// GPIO chip info event related definitions. + pub use crate::info_event::*; +} + +use std::cmp::Ordering; +use std::ffi::{CStr, CString}; +use std::os::{raw::c_char, unix::prelude::AsRawFd}; +use std::path::Path; +use std::str; +use std::sync::Arc; +use std::time::Duration; + +use super::{ + gpiod, + line::{self, Offset}, + request, Error, OperationType, Result, +}; + +#[derive(Debug, Eq, PartialEq)] +struct Internal { + chip: *mut gpiod::gpiod_chip, +} + +impl Internal { + /// Find a chip by path. + fn open>(path: &P) -> Result { + // Null-terminate the string + let path = path.as_ref().to_string_lossy() + "\0"; + + // SAFETY: The `gpiod_chip` returned by libgpiod is guaranteed to live as long + // as the `struct Internal`. + let chip = unsafe { gpiod::gpiod_chip_open(path.as_ptr() as *const c_char) }; + if chip.is_null() { + return Err(Error::OperationFailed( + OperationType::ChipOpen, + errno::errno(), + )); + } + + Ok(Self { chip }) + } +} + +impl Drop for Internal { + /// Close the chip and release all associated resources. + fn drop(&mut self) { + // SAFETY: `gpiod_chip` is guaranteed to be valid here. + unsafe { gpiod::gpiod_chip_close(self.chip) } + } +} + +/// GPIO chip +/// +/// A GPIO chip object is associated with an open file descriptor to the GPIO +/// character device. It exposes basic information about the chip and allows +/// callers to retrieve information about each line, watch lines for state +/// changes and make line requests. +#[derive(Debug, Eq, PartialEq)] +pub struct Chip { + ichip: Arc, +} + +// Safe as `Internal` won't be freed until the `Chip` is dropped. +unsafe impl Send for Chip {} + +impl Chip { + /// Find a chip by path. + pub fn open>(path: &P) -> Result { + let ichip = Arc::new(Internal::open(path)?); + + Ok(Self { ichip }) + } + + /// Get the chip name as represented in the kernel. + pub fn info(&self) -> Result { + Info::new(self.ichip.clone()) + } + + /// Get the path used to find the chip. + pub fn path(&self) -> Result<&str> { + // SAFETY: The string returned by libgpiod is guaranteed to live as long + // as the `struct Chip`. + let path = unsafe { gpiod::gpiod_chip_get_path(self.ichip.chip) }; + + // SAFETY: The string is guaranteed to be valid here by the C API. + unsafe { CStr::from_ptr(path) } + .to_str() + .map_err(Error::StringNotUtf8) + } + + /// Get a snapshot of information about the line. + pub fn line_info(&self, offset: Offset) -> Result { + // SAFETY: The `gpiod_line_info` returned by libgpiod is guaranteed to live as long + // as the `struct Info`. + let info = unsafe { gpiod::gpiod_chip_get_line_info(self.ichip.chip, offset) }; + + if info.is_null() { + return Err(Error::OperationFailed( + OperationType::ChipGetLineInfo, + errno::errno(), + )); + } + + line::Info::new(info) + } + + /// Get the current snapshot of information about the line at given offset and start watching + /// it for future changes. + pub fn watch_line_info(&self, offset: Offset) -> Result { + // SAFETY: `gpiod_line_info` is guaranteed to be valid here. + let info = unsafe { gpiod::gpiod_chip_watch_line_info(self.ichip.chip, offset) }; + + if info.is_null() { + return Err(Error::OperationFailed( + OperationType::ChipWatchLineInfo, + errno::errno(), + )); + } + + line::Info::new_watch(info) + } + + /// Stop watching a line + pub fn unwatch(&self, offset: Offset) { + // SAFETY: `gpiod_chip` is guaranteed to be valid here. + unsafe { + gpiod::gpiod_chip_unwatch_line_info(self.ichip.chip, offset); + } + } + + /// Wait for line status events on any of the watched lines on the chip. + pub fn wait_info_event(&self, timeout: Option) -> Result { + let timeout = match timeout { + Some(x) => x.as_nanos() as i64, + // Block indefinitely + None => -1, + }; + + // SAFETY: `gpiod_chip` is guaranteed to be valid here. + let ret = unsafe { gpiod::gpiod_chip_wait_info_event(self.ichip.chip, timeout) }; + + match ret { + -1 => Err(Error::OperationFailed( + OperationType::ChipWaitInfoEvent, + errno::errno(), + )), + 0 => Ok(false), + _ => Ok(true), + } + } + + /// Read a single line status change event from the chip. If no events are + /// pending, this function will block. + pub fn read_info_event(&self) -> Result { + // SAFETY: The `gpiod_info_event` returned by libgpiod is guaranteed to live as long + // as the `struct Event`. + let event = unsafe { gpiod::gpiod_chip_read_info_event(self.ichip.chip) }; + if event.is_null() { + return Err(Error::OperationFailed( + OperationType::ChipReadInfoEvent, + errno::errno(), + )); + } + + Ok(info::Event::new(event)) + } + + /// Map a GPIO line's name to its offset within the chip. + pub fn line_offset_from_name(&self, name: &str) -> Result { + let name = CString::new(name).map_err(|_| Error::InvalidString)?; + + // SAFETY: `gpiod_chip` is guaranteed to be valid here. + let ret = unsafe { + gpiod::gpiod_chip_get_line_offset_from_name( + self.ichip.chip, + name.as_ptr() as *const c_char, + ) + }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::ChipGetLineOffsetFromName, + errno::errno(), + )) + } else { + Ok(ret as u32) + } + } + + /// Request a set of lines for exclusive usage. + pub fn request_lines( + &self, + rconfig: &request::Config, + lconfig: &line::Config, + ) -> Result { + let request = unsafe { + // SAFETY: The `gpiod_line_request` returned by libgpiod is guaranteed to live as long + // as the `struct Request`. + gpiod::gpiod_chip_request_lines(self.ichip.chip, rconfig.config, lconfig.config) + }; + + if request.is_null() { + return Err(Error::OperationFailed( + OperationType::ChipRequestLines, + errno::errno(), + )); + } + + request::Request::new(request) + } +} + +impl AsRawFd for Chip { + /// 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. + fn as_raw_fd(&self) -> i32 { + // SAFETY: `gpiod_chip` is guaranteed to be valid here. + unsafe { gpiod::gpiod_chip_get_fd(self.ichip.chip) } + } +} + +/// GPIO chip Information +#[derive(Debug, Eq)] +pub struct Info { + info: *mut gpiod::gpiod_chip_info, +} + +impl Info { + /// Find a GPIO chip by path. + fn new(chip: Arc) -> Result { + // SAFETY: `gpiod_chip` is guaranteed to be valid here. + let info = unsafe { gpiod::gpiod_chip_get_info(chip.chip) }; + if info.is_null() { + return Err(Error::OperationFailed( + OperationType::ChipGetInfo, + errno::errno(), + )); + } + + Ok(Self { info }) + } + + /// Get the GPIO chip name as represented in the kernel. + pub fn name(&self) -> Result<&str> { + // SAFETY: The string returned by libgpiod is guaranteed to live as long + // as the `struct Chip`. + let name = unsafe { gpiod::gpiod_chip_info_get_name(self.info) }; + + // SAFETY: The string is guaranteed to be valid here by the C API. + unsafe { CStr::from_ptr(name) } + .to_str() + .map_err(Error::StringNotUtf8) + } + + /// Get the GPIO chip label as represented in the kernel. + pub fn label(&self) -> Result<&str> { + // SAFETY: The string returned by libgpiod is guaranteed to live as long + // as the `struct Chip`. + let label = unsafe { gpiod::gpiod_chip_info_get_label(self.info) }; + + // SAFETY: The string is guaranteed to be valid here by the C API. + unsafe { CStr::from_ptr(label) } + .to_str() + .map_err(Error::StringNotUtf8) + } + + /// Get the number of GPIO lines exposed by the chip. + pub fn num_lines(&self) -> usize { + // SAFETY: `gpiod_chip` is guaranteed to be valid here. + unsafe { gpiod::gpiod_chip_info_get_num_lines(self.info) as usize } + } +} + +impl PartialEq for Info { + fn eq(&self, other: &Self) -> bool { + self.name().unwrap().eq(other.name().unwrap()) + } +} + +impl PartialOrd for Info { + fn partial_cmp(&self, other: &Self) -> Option { + let name = match self.name() { + Ok(name) => name, + _ => return None, + }; + + let other_name = match other.name() { + Ok(name) => name, + _ => return None, + }; + + name.partial_cmp(other_name) + } +} + +impl Drop for Info { + /// Close the GPIO chip info and release all associated resources. + fn drop(&mut self) { + // SAFETY: `gpiod_chip` is guaranteed to be valid here. + unsafe { gpiod::gpiod_chip_info_free(self.info) } + } +} diff --git a/bindings/rust/libgpiod/src/edge_event.rs b/bindings/rust/libgpiod/src/edge_event.rs new file mode 100644 index 000000000000..4e167f3ee701 --- /dev/null +++ b/bindings/rust/libgpiod/src/edge_event.rs @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use std::time::Duration; + +use super::{ + gpiod, + line::{EdgeKind, Offset}, + Error, OperationType, Result, +}; + +/// Line edge events handling +/// +/// An edge event object contains information about a single line edge event. +/// It contains the event type, timestamp and the offset of the line on which +/// the event occurred as well as two sequence numbers (global for all lines +/// in the associated request and local for this line only). +/// +/// Edge events are stored into an edge-event buffer object to improve +/// performance and to limit the number of memory allocations when a large +/// number of events are being read. + +#[derive(Debug, Eq, PartialEq)] +pub struct Event(*mut gpiod::gpiod_edge_event); + +impl Event { + pub fn event_clone(event: &Event) -> Result { + // SAFETY: `gpiod_edge_event` is guaranteed to be valid here. + let event = unsafe { gpiod::gpiod_edge_event_copy(event.0) }; + if event.is_null() { + return Err(Error::OperationFailed( + OperationType::EdgeEventCopy, + errno::errno(), + )); + } + + Ok(Self(event)) + } + + /// Get the event type. + pub fn event_type(&self) -> Result { + // SAFETY: `gpiod_edge_event` is guaranteed to be valid here. + EdgeKind::new(unsafe { gpiod::gpiod_edge_event_get_event_type(self.0) } as u32) + } + + /// Get the timestamp of the event. + pub fn timestamp(&self) -> Duration { + // SAFETY: `gpiod_edge_event` is guaranteed to be valid here. + Duration::from_nanos(unsafe { gpiod::gpiod_edge_event_get_timestamp_ns(self.0) }) + } + + /// Get the offset of the line on which the event was triggered. + pub fn line_offset(&self) -> Offset { + // SAFETY: `gpiod_edge_event` is guaranteed to be valid here. + unsafe { gpiod::gpiod_edge_event_get_line_offset(self.0) } + } + + /// Get the global sequence number of the event. + /// + /// Returns sequence number of the event relative to all lines in the + /// associated line request. + pub fn global_seqno(&self) -> usize { + // SAFETY: `gpiod_edge_event` is guaranteed to be valid here. + unsafe { + gpiod::gpiod_edge_event_get_global_seqno(self.0) + .try_into() + .unwrap() + } + } + + /// Get the event sequence number specific to concerned line. + /// + /// Returns sequence number of the event relative to the line within the + /// lifetime of the associated line request. + pub fn line_seqno(&self) -> usize { + // SAFETY: `gpiod_edge_event` is guaranteed to be valid here. + unsafe { + gpiod::gpiod_edge_event_get_line_seqno(self.0) + .try_into() + .unwrap() + } + } +} + +impl Drop for Event { + /// Free the edge event. + fn drop(&mut self) { + // SAFETY: `gpiod_edge_event` is guaranteed to be valid here. + unsafe { gpiod::gpiod_edge_event_free(self.0) }; + } +} diff --git a/bindings/rust/libgpiod/src/event_buffer.rs b/bindings/rust/libgpiod/src/event_buffer.rs new file mode 100644 index 000000000000..8053b9aaaada --- /dev/null +++ b/bindings/rust/libgpiod/src/event_buffer.rs @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use std::os::raw::c_ulong; +use std::ptr; + +use super::{ + gpiod, + request::{Event, Request}, + Error, OperationType, Result, +}; + +/// Line edge events +/// +/// An iterator over the elements of type `Event`. + +pub struct Events<'a> { + buffer: &'a mut Buffer, + read_index: usize, + len: usize, +} + +impl<'a> Events<'a> { + pub fn new(buffer: &'a mut Buffer, len: usize) -> Self { + Self { + buffer, + read_index: 0, + len, + } + } + + /// Get the number of contained events in the snapshot, this doesn't change + /// on reading events from the iterator. + pub fn len(&self) -> usize { + self.len + } + + /// Check if buffer is empty. + pub fn is_empty(&self) -> bool { + self.len == 0 + } +} + +impl<'a> Iterator for Events<'a> { + type Item = Result<&'a Event>; + + fn nth(&mut self, n: usize) -> Option { + if self.read_index + n >= self.len { + return None; + } + + self.read_index += n + 1; + Some(self.buffer.event(self.read_index - 1)) + } + + fn next(&mut self) -> Option { + self.nth(0) + } +} + +/// Line edge events buffer +#[derive(Debug, Eq, PartialEq)] +pub struct Buffer { + pub(crate) buffer: *mut gpiod::gpiod_edge_event_buffer, + events: Vec<*mut gpiod::gpiod_edge_event>, +} + +impl Buffer { + /// Create a new edge event buffer. + /// + /// If capacity equals 0, it will be set to a default value of 64. If + /// capacity is larger than 1024, it will be limited to 1024. + pub fn new(capacity: usize) -> Result { + // SAFETY: The `gpiod_edge_event_buffer` returned by libgpiod is guaranteed to live as long + // as the `struct Buffer`. + let buffer = unsafe { gpiod::gpiod_edge_event_buffer_new(capacity as c_ulong) }; + if buffer.is_null() { + return Err(Error::OperationFailed( + OperationType::EdgeEventBufferNew, + errno::errno(), + )); + } + + // SAFETY: `gpiod_edge_event_buffer` is guaranteed to be valid here. + let capacity = unsafe { gpiod::gpiod_edge_event_buffer_get_capacity(buffer) as usize }; + + Ok(Self { + buffer, + events: vec![ptr::null_mut(); capacity], + }) + } + + /// Get the capacity of the event buffer. + pub fn capacity(&self) -> usize { + self.events.len() + } + + /// Get edge events from a line request. + /// + /// This function will block if no event was queued for the line. + pub fn read_edge_events(&mut self, request: &Request) -> Result { + for i in 0..self.events.len() { + self.events[i] = ptr::null_mut(); + } + + // SAFETY: `gpiod_line_request` is guaranteed to be valid here. + let ret = unsafe { + gpiod::gpiod_line_request_read_edge_event( + request.request, + self.buffer, + self.events.len().try_into().unwrap(), + ) + }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::LineRequestReadEdgeEvent, + errno::errno(), + )) + } else { + let ret = ret as usize; + + if ret > self.events.len() { + Err(Error::TooManyEvents(ret, self.events.len())) + } else { + Ok(Events::new(self, ret)) + } + } + } + + /// Read an event stored in the buffer. + fn event<'a>(&mut self, index: usize) -> Result<&'a Event> { + if self.events[index].is_null() { + // SAFETY: The `gpiod_edge_event` returned by libgpiod is guaranteed to live as long + // as the `struct Event`. + let event = unsafe { + gpiod::gpiod_edge_event_buffer_get_event(self.buffer, index.try_into().unwrap()) + }; + + if event.is_null() { + return Err(Error::OperationFailed( + OperationType::EdgeEventBufferGetEvent, + errno::errno(), + )); + } + + self.events[index] = event; + } + + Ok(unsafe { + // This will not lead to `drop(event)`. + (self.events.as_ptr().add(index) as *const Event) + .as_ref() + .unwrap() + }) + } +} + +impl Drop for Buffer { + /// Free the edge event buffer and release all associated resources. + fn drop(&mut self) { + // SAFETY: `gpiod_edge_event_buffer` is guaranteed to be valid here. + unsafe { gpiod::gpiod_edge_event_buffer_free(self.buffer) }; + } +} diff --git a/bindings/rust/libgpiod/src/info_event.rs b/bindings/rust/libgpiod/src/info_event.rs new file mode 100644 index 000000000000..09de4dd3458a --- /dev/null +++ b/bindings/rust/libgpiod/src/info_event.rs @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use std::time::Duration; + +use super::{ + gpiod, + line::{self, InfoChangeKind}, + Error, OperationType, Result, +}; + +/// Line status watch events +/// +/// Accessors for the info event objects allowing to monitor changes in GPIO +/// line state. +/// +/// Callers can be notified about changes in line's state using the interfaces +/// exposed by GPIO chips. Each info event contains information about the event +/// itself (timestamp, type) as well as a snapshot of line's state in the form +/// of a line-info object. + +#[derive(Debug, Eq, PartialEq)] +pub struct Event { + pub(crate) event: *mut gpiod::gpiod_info_event, +} + +impl Event { + /// Get a single chip's line's status change event. + pub(crate) fn new(event: *mut gpiod::gpiod_info_event) -> Self { + Self { event } + } + + /// Get the event type of the status change event. + pub fn event_type(&self) -> Result { + // SAFETY: `gpiod_info_event` is guaranteed to be valid here. + InfoChangeKind::new(unsafe { gpiod::gpiod_info_event_get_event_type(self.event) } as u32) + } + + /// Get the timestamp of the event, read from the monotonic clock. + pub fn timestamp(&self) -> Duration { + // SAFETY: `gpiod_info_event` is guaranteed to be valid here. + Duration::from_nanos(unsafe { gpiod::gpiod_info_event_get_timestamp_ns(self.event) }) + } + + /// Get the line-info object associated with the event. + pub fn line_info(&self) -> Result { + // SAFETY: `gpiod_line_info` is guaranteed to be valid here. + let info = unsafe { gpiod::gpiod_info_event_get_line_info(self.event) }; + + if info.is_null() { + return Err(Error::OperationFailed( + OperationType::InfoEventGetLineInfo, + errno::errno(), + )); + } + + line::Info::new_from_event(info) + } +} + +impl Drop for Event { + /// Free the info event object and release all associated resources. + fn drop(&mut self) { + // SAFETY: `gpiod_info_event` is guaranteed to be valid here. + unsafe { gpiod::gpiod_info_event_free(self.event) } + } +} diff --git a/bindings/rust/libgpiod/src/lib.rs b/bindings/rust/libgpiod/src/lib.rs new file mode 100644 index 000000000000..cddaa7ba36c7 --- /dev/null +++ b/bindings/rust/libgpiod/src/lib.rs @@ -0,0 +1,479 @@ +// SPDX-License-Identifier: Apache-2.0 OR 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. + +use std::ffi::CStr; +use std::fs; +use std::os::raw::c_char; +use std::path::Path; +use std::time::Duration; +use std::{fmt, str}; + +use intmap::IntMap; +use thiserror::Error as ThisError; + +use libgpiod_sys as gpiod; + +/// Operation types, used with OperationFailed() Error. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum OperationType { + ChipOpen, + ChipWaitInfoEvent, + ChipGetLine, + ChipGetLineInfo, + ChipGetLineOffsetFromName, + ChipGetInfo, + ChipReadInfoEvent, + ChipRequestLines, + ChipWatchLineInfo, + EdgeEventBufferGetEvent, + EdgeEventCopy, + EdgeEventBufferNew, + InfoEventGetLineInfo, + LineConfigNew, + LineConfigAddSettings, + LineConfigGetOffsets, + LineConfigGetSettings, + LineRequestReconfigLines, + LineRequestGetVal, + LineRequestGetValSubset, + LineRequestSetVal, + LineRequestSetValSubset, + LineRequestReadEdgeEvent, + LineRequestWaitEdgeEvent, + LineSettingsNew, + LineSettingsCopy, + LineSettingsGetOutVal, + LineSettingsSetDirection, + LineSettingsSetEdgeDetection, + LineSettingsSetBias, + LineSettingsSetDrive, + LineSettingsSetActiveLow, + LineSettingsSetDebouncePeriod, + LineSettingsSetEventClock, + LineSettingsSetOutputValue, + RequestConfigNew, + RequestConfigGetConsumer, + SimBankGetVal, + SimBankNew, + SimBankSetLabel, + SimBankSetNumLines, + SimBankSetLineName, + SimBankSetPull, + SimBankHogLine, + SimCtxNew, + SimDevNew, + SimDevEnable, + SimDevDisable, +} + +impl fmt::Display for OperationType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +/// Result of libgpiod operations. +pub type Result = std::result::Result; + +/// Error codes for libgpiod operations. +#[derive(Copy, Clone, Debug, Eq, PartialEq, ThisError)] +pub enum Error { + #[error("Failed to get {0}")] + NullString(&'static str), + #[error("String not utf8: {0:?}")] + StringNotUtf8(str::Utf8Error), + #[error("Invalid String")] + InvalidString, + #[error("Invalid enum {0} value: {1}")] + InvalidEnumValue(&'static str, u32), + #[error("Operation {0} Failed: {1}")] + OperationFailed(OperationType, errno::Errno), + #[error("Invalid Arguments")] + InvalidArguments, + #[error("Event count more than buffer capacity: {0} > {1}")] + TooManyEvents(usize, usize), + #[error("Std Io Error")] + IoError, +} + +mod info_event; + +/// GPIO chip related definitions. +pub mod chip; + +mod edge_event; +mod event_buffer; +mod line_request; +mod request_config; + +/// GPIO chip request related definitions. +pub mod request { + pub use crate::edge_event::*; + pub use crate::event_buffer::*; + pub use crate::line_request::*; + pub use crate::request_config::*; +} + +mod line_config; +mod line_info; +mod line_settings; + +/// GPIO chip line related definitions. +pub mod line { + pub use crate::line_config::*; + pub use crate::line_info::*; + pub use crate::line_settings::*; + + use super::*; + + /// Value settings. + #[derive(Copy, Clone, Debug, Eq, PartialEq)] + pub enum Value { + /// Active + Active, + /// Inactive + InActive, + } + + /// Maps offset to Value. + pub type ValueMap = IntMap; + + impl Value { + pub fn new(val: i32) -> Result { + Ok(match val { + 0 => Value::InActive, + 1 => Value::Active, + _ => return Err(Error::InvalidEnumValue("Value", val as u32)), + }) + } + + pub(crate) fn value(&self) -> i32 { + match self { + Value::Active => 1, + Value::InActive => 0, + } + } + } + + /// Offset type. + pub type Offset = u32; + + /// Direction settings. + #[derive(Copy, Clone, Debug, Eq, PartialEq)] + pub enum Direction { + /// Request the line(s), but don't change direction. + AsIs, + /// Direction is input - for reading the value of an externally driven GPIO line. + Input, + /// Direction is output - for driving the GPIO line. + Output, + } + + impl Direction { + pub(crate) fn new(dir: u32) -> Result { + Ok(match dir { + gpiod::GPIOD_LINE_DIRECTION_AS_IS => Direction::AsIs, + gpiod::GPIOD_LINE_DIRECTION_INPUT => Direction::Input, + gpiod::GPIOD_LINE_DIRECTION_OUTPUT => Direction::Output, + _ => return Err(Error::InvalidEnumValue("Direction", dir)), + }) + } + + pub(crate) fn gpiod_direction(&self) -> u32 { + match self { + Direction::AsIs => gpiod::GPIOD_LINE_DIRECTION_AS_IS, + Direction::Input => gpiod::GPIOD_LINE_DIRECTION_INPUT, + Direction::Output => gpiod::GPIOD_LINE_DIRECTION_OUTPUT, + } + } + } + + /// Internal bias settings. + #[derive(Copy, Clone, Debug, Eq, PartialEq)] + pub enum Bias { + /// The internal bias is disabled. + Disabled, + /// The internal pull-up bias is enabled. + PullUp, + /// The internal pull-down bias is enabled. + PullDown, + } + + impl Bias { + pub(crate) fn new(bias: u32) -> Result> { + Ok(match bias { + gpiod::GPIOD_LINE_BIAS_UNKNOWN => None, + gpiod::GPIOD_LINE_BIAS_AS_IS => None, + gpiod::GPIOD_LINE_BIAS_DISABLED => Some(Bias::Disabled), + gpiod::GPIOD_LINE_BIAS_PULL_UP => Some(Bias::PullUp), + gpiod::GPIOD_LINE_BIAS_PULL_DOWN => Some(Bias::PullDown), + _ => return Err(Error::InvalidEnumValue("Bias", bias)), + }) + } + + pub(crate) fn gpiod_bias(bias: Option) -> u32 { + match bias { + None => gpiod::GPIOD_LINE_BIAS_AS_IS, + Some(bias) => match bias { + Bias::Disabled => gpiod::GPIOD_LINE_BIAS_DISABLED, + Bias::PullUp => gpiod::GPIOD_LINE_BIAS_PULL_UP, + Bias::PullDown => gpiod::GPIOD_LINE_BIAS_PULL_DOWN, + }, + } + } + } + + /// Drive settings. + #[derive(Copy, Clone, Debug, Eq, PartialEq)] + pub enum Drive { + /// Drive setting is push-pull. + PushPull, + /// Line output is open-drain. + OpenDrain, + /// Line output is open-source. + OpenSource, + } + + impl Drive { + pub(crate) fn new(drive: u32) -> Result { + Ok(match drive { + gpiod::GPIOD_LINE_DRIVE_PUSH_PULL => Drive::PushPull, + gpiod::GPIOD_LINE_DRIVE_OPEN_DRAIN => Drive::OpenDrain, + gpiod::GPIOD_LINE_DRIVE_OPEN_SOURCE => Drive::OpenSource, + _ => return Err(Error::InvalidEnumValue("Drive", drive)), + }) + } + + pub(crate) fn gpiod_drive(&self) -> u32 { + match self { + Drive::PushPull => gpiod::GPIOD_LINE_DRIVE_PUSH_PULL, + Drive::OpenDrain => gpiod::GPIOD_LINE_DRIVE_OPEN_DRAIN, + Drive::OpenSource => gpiod::GPIOD_LINE_DRIVE_OPEN_SOURCE, + } + } + } + + /// Edge detection settings. + #[derive(Copy, Clone, Debug, Eq, PartialEq)] + pub enum Edge { + /// Line detects rising edge events. + Rising, + /// Line detects falling edge events. + Falling, + /// Line detects both rising and falling edge events. + Both, + } + + impl Edge { + pub(crate) fn new(edge: u32) -> Result> { + Ok(match edge { + gpiod::GPIOD_LINE_EDGE_NONE => None, + gpiod::GPIOD_LINE_EDGE_RISING => Some(Edge::Rising), + gpiod::GPIOD_LINE_EDGE_FALLING => Some(Edge::Falling), + gpiod::GPIOD_LINE_EDGE_BOTH => Some(Edge::Both), + _ => return Err(Error::InvalidEnumValue("Edge", edge)), + }) + } + + pub(crate) fn gpiod_edge(edge: Option) -> u32 { + match edge { + None => gpiod::GPIOD_LINE_EDGE_NONE, + Some(edge) => match edge { + Edge::Rising => gpiod::GPIOD_LINE_EDGE_RISING, + Edge::Falling => gpiod::GPIOD_LINE_EDGE_FALLING, + Edge::Both => gpiod::GPIOD_LINE_EDGE_BOTH, + }, + } + } + } + + /// Line setting kind. + #[derive(Copy, Clone, Debug, Eq, PartialEq)] + pub enum SettingKind { + /// Line direction. + Direction, + /// Bias. + Bias, + /// Drive. + Drive, + /// Edge detection. + EdgeDetection, + /// Active-low setting. + ActiveLow, + /// Debounce period. + DebouncePeriod, + /// Event clock type. + EventClock, + /// Output value. + OutputValue, + } + + /// Line settings. + #[derive(Copy, Clone, Debug, Eq, PartialEq)] + pub enum SettingVal { + /// Line direction. + Direction(Direction), + /// Bias. + Bias(Option), + /// Drive. + Drive(Drive), + /// Edge detection. + EdgeDetection(Option), + /// Active-low setting. + ActiveLow(bool), + /// Debounce period. + DebouncePeriod(Duration), + /// Event clock type. + EventClock(EventClock), + /// Output value. + OutputValue(Value), + } + + impl fmt::Display for SettingVal { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self) + } + } + + /// Event clock settings. + #[derive(Copy, Clone, Debug, Eq, PartialEq)] + pub enum EventClock { + /// Line uses the monotonic clock for edge event timestamps. + Monotonic, + /// Line uses the realtime clock for edge event timestamps. + Realtime, + /// Line uses the hardware timestamp engine clock for edge event timestamps. + HTE, + } + + impl EventClock { + pub(crate) fn new(clock: u32) -> Result { + Ok(match clock { + gpiod::GPIOD_LINE_EVENT_CLOCK_MONOTONIC => EventClock::Monotonic, + gpiod::GPIOD_LINE_EVENT_CLOCK_REALTIME => EventClock::Realtime, + gpiod::GPIOD_LINE_EVENT_CLOCK_HTE => EventClock::HTE, + _ => return Err(Error::InvalidEnumValue("Eventclock", clock)), + }) + } + + pub(crate) fn gpiod_clock(&self) -> u32 { + match self { + EventClock::Monotonic => gpiod::GPIOD_LINE_EVENT_CLOCK_MONOTONIC, + EventClock::Realtime => gpiod::GPIOD_LINE_EVENT_CLOCK_REALTIME, + EventClock::HTE => gpiod::GPIOD_LINE_EVENT_CLOCK_HTE, + } + } + } + + /// Line status change event types. + #[derive(Copy, Clone, Debug, Eq, PartialEq)] + pub enum InfoChangeKind { + /// Line has been requested. + LineRequested, + /// Previously requested line has been released. + LineReleased, + /// Line configuration has changed. + LineConfigChanged, + } + + impl InfoChangeKind { + pub(crate) fn new(kind: u32) -> Result { + Ok(match kind { + gpiod::GPIOD_INFO_EVENT_LINE_REQUESTED => InfoChangeKind::LineRequested, + gpiod::GPIOD_INFO_EVENT_LINE_RELEASED => InfoChangeKind::LineReleased, + gpiod::GPIOD_INFO_EVENT_LINE_CONFIG_CHANGED => InfoChangeKind::LineConfigChanged, + _ => return Err(Error::InvalidEnumValue("InfoChangeKind", kind)), + }) + } + } + + /// Edge event types. + #[derive(Copy, Clone, Debug, Eq, PartialEq)] + pub enum EdgeKind { + /// Rising edge event. + Rising, + /// Falling edge event. + Falling, + } + + impl EdgeKind { + pub(crate) fn new(kind: u32) -> Result { + Ok(match kind { + gpiod::GPIOD_EDGE_EVENT_RISING_EDGE => EdgeKind::Rising, + gpiod::GPIOD_EDGE_EVENT_FALLING_EDGE => EdgeKind::Falling, + _ => return Err(Error::InvalidEnumValue("EdgeEvent", kind)), + }) + } + } +} + +/// Various libgpiod-related functions. + +/// Check if the file pointed to by path is a GPIO chip character device. +/// +/// Returns true if the file exists and is a GPIO chip character device or a +/// symbolic link to it. +pub fn is_gpiochip_device>(path: &P) -> bool { + // Null-terminate the string + let path = path.as_ref().to_string_lossy() + "\0"; + + // SAFETY: libgpiod won't access the path reference once the call returns. + unsafe { gpiod::gpiod_is_gpiochip_device(path.as_ptr() as *const c_char) } +} + +/// GPIO devices. +/// +/// Returns a vector of unique available GPIO Chips. +/// +/// The chips are sorted in ascending order of the chip names. +pub fn gpiochip_devices>(path: &P) -> Result> { + let mut devices = Vec::new(); + + for entry in fs::read_dir(path).map_err(|_| Error::IoError)?.flatten() { + let path = entry.path(); + + if is_gpiochip_device(&path) { + let chip = chip::Chip::open(&path)?; + let info = chip.info()?; + + devices.push((chip, info)); + } + } + + devices.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap()); + devices.dedup_by(|a, b| a.1.eq(&b.1)); + + Ok(devices.into_iter().map(|a| a.0).collect()) +} + +/// Get the API version of the libgpiod library as a human-readable string. +pub fn libgpiod_version() -> Result<&'static str> { + // SAFETY: The string returned by libgpiod is guaranteed to live forever. + let version = unsafe { gpiod::gpiod_version_string() }; + + if version.is_null() { + return Err(Error::NullString("GPIO library version")); + } + + // SAFETY: The string is guaranteed to be valid here by the C API. + unsafe { CStr::from_ptr(version) } + .to_str() + .map_err(Error::StringNotUtf8) +} + +/// Get the API version of the libgpiod crate as a human-readable string. +pub fn crate_version() -> &'static str { + env!("CARGO_PKG_VERSION") +} diff --git a/bindings/rust/libgpiod/src/line_config.rs b/bindings/rust/libgpiod/src/line_config.rs new file mode 100644 index 000000000000..96acbf70e945 --- /dev/null +++ b/bindings/rust/libgpiod/src/line_config.rs @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use std::os::raw::{c_ulong, c_void}; +use std::slice; + +use super::{ + gpiod, + line::{Offset, Settings}, + Error, OperationType, Result, +}; + +/// Line configuration objects. +/// +/// The line-config object contains the configuration for lines that can be +/// used in two cases: +/// - when making a line request +/// - when reconfiguring a set of already requested lines. +/// +/// A new line-config object is empty. Using it in a request will lead to an +/// error. In order for a line-config to become useful, it needs to be assigned +/// at least one offset-to-settings mapping by calling +/// ::gpiod_line_config_add_line_settings. +/// +/// When calling ::gpiod_chip_request_lines, the library will request all +/// offsets that were assigned settings in the order that they were assigned. + +#[derive(Debug, Eq, PartialEq)] +pub struct Config { + pub(crate) config: *mut gpiod::gpiod_line_config, +} + +impl Config { + /// Create a new line config object. + pub fn new() -> Result { + // SAFETY: The `gpiod_line_config` returned by libgpiod is guaranteed to live as long + // as the `struct Config`. + let config = unsafe { gpiod::gpiod_line_config_new() }; + + if config.is_null() { + return Err(Error::OperationFailed( + OperationType::LineConfigNew, + errno::errno(), + )); + } + + Ok(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) { + // SAFETY: `gpiod_line_config` is guaranteed to be valid here. + unsafe { gpiod::gpiod_line_config_reset(self.config) } + } + + /// Add line settings for a set of offsets. + pub fn add_line_settings(&self, offsets: &[Offset], settings: Settings) -> Result<()> { + // SAFETY: `gpiod_line_config` is guaranteed to be valid here. + let ret = unsafe { + gpiod::gpiod_line_config_add_line_settings( + self.config, + offsets.as_ptr(), + offsets.len() as c_ulong, + settings.settings, + ) + }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::LineConfigAddSettings, + errno::errno(), + )) + } else { + Ok(()) + } + } + + /// Get line settings for offset. + pub fn line_settings(&self, offset: Offset) -> Result { + // SAFETY: `gpiod_line_config` is guaranteed to be valid here. + let settings = unsafe { gpiod::gpiod_line_config_get_line_settings(self.config, offset) }; + + if settings.is_null() { + return Err(Error::OperationFailed( + OperationType::LineConfigGetSettings, + errno::errno(), + )); + } + + Ok(Settings::new_with_settings(settings)) + } + + /// Get configured offsets. + pub fn offsets(&self) -> Result> { + let mut num: u64 = 0; + let mut ptr: *mut Offset = std::ptr::null_mut(); + + // SAFETY: The `ptr` array returned by libgpiod is guaranteed to live as long + // as it is not explicitly freed with `free()`. + let ret = unsafe { + gpiod::gpiod_line_config_get_offsets( + self.config, + &mut num as *mut _ as *mut _, + &mut ptr, + ) + }; + + if ret == -1 { + return Err(Error::OperationFailed( + OperationType::LineConfigGetOffsets, + errno::errno(), + )); + } + + // SAFETY: The `ptr` array returned by libgpiod is guaranteed to live as long + // as it is not explicitly freed with `free()`. + let offsets = unsafe { slice::from_raw_parts(ptr as *const Offset, num as usize).to_vec() }; + + // SAFETY: The `ptr` array is guaranteed to be valid here. + unsafe { libc::free(ptr as *mut c_void) }; + + Ok(offsets) + } +} + +impl Drop for Config { + /// Free the line config object and release all associated resources. + fn drop(&mut self) { + // SAFETY: `gpiod_line_config` is guaranteed to be valid here. + unsafe { gpiod::gpiod_line_config_free(self.config) } + } +} diff --git a/bindings/rust/libgpiod/src/line_info.rs b/bindings/rust/libgpiod/src/line_info.rs new file mode 100644 index 000000000000..412c9b835235 --- /dev/null +++ b/bindings/rust/libgpiod/src/line_info.rs @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use std::ffi::CStr; +use std::str; +use std::time::Duration; + +use super::{ + gpiod, + line::{Bias, Direction, Drive, Edge, EventClock, Offset}, + Error, Result, +}; + +/// Line info +/// +/// Exposes functions for retrieving kernel information about both requested and +/// free lines. Line info object contains an immutable snapshot of a line's status. +/// +/// The line info contains all the publicly available information about a +/// line, which does not include the line value. The line must be requested +/// to access the line value. + +#[derive(Debug, Eq, PartialEq)] +pub struct Info { + info: *mut gpiod::gpiod_line_info, + contained: bool, +} + +impl Info { + fn new_internal(info: *mut gpiod::gpiod_line_info, contained: bool) -> Result { + Ok(Self { info, contained }) + } + + /// Get a snapshot of information about the line. + pub(crate) fn new(info: *mut gpiod::gpiod_line_info) -> Result { + Info::new_internal(info, false) + } + + /// Get a snapshot of information about the line and start watching it for changes. + pub(crate) fn new_watch(info: *mut gpiod::gpiod_line_info) -> Result { + Info::new_internal(info, false) + } + + /// Get the Line info object associated with an event. + pub(crate) fn new_from_event(info: *mut gpiod::gpiod_line_info) -> Result { + Info::new_internal(info, true) + } + + /// Get the offset of the line within the GPIO chip. + /// + /// The offset uniquely identifies the line on the chip. The combination of the chip and offset + /// uniquely identifies the line within the system. + + pub fn offset(&self) -> Offset { + // SAFETY: `gpiod_line_info` is guaranteed to be valid here. + unsafe { gpiod::gpiod_line_info_get_offset(self.info) } + } + + /// Get GPIO line's name. + pub fn name(&self) -> Result<&str> { + // SAFETY: The string returned by libgpiod is guaranteed to live as long + // as the `struct Info`. + let name = unsafe { gpiod::gpiod_line_info_get_name(self.info) }; + if name.is_null() { + return Err(Error::NullString("GPIO line's name")); + } + + // SAFETY: The string is guaranteed to be valid here by the C API. + unsafe { CStr::from_ptr(name) } + .to_str() + .map_err(Error::StringNotUtf8) + } + + /// Returns True if the line is in use, false otherwise. + /// + /// The user space can't know exactly why a line is busy. It may have been + /// requested by another process or hogged by the kernel. It only matters that + /// the line is used and we can't request it. + pub fn is_used(&self) -> bool { + // SAFETY: `gpiod_line_info` is guaranteed to be valid here. + unsafe { gpiod::gpiod_line_info_is_used(self.info) } + } + + /// Get the GPIO line's consumer name. + pub fn consumer(&self) -> Result<&str> { + // SAFETY: The string returned by libgpiod is guaranteed to live as long + // as the `struct Info`. + let name = unsafe { gpiod::gpiod_line_info_get_consumer(self.info) }; + if name.is_null() { + return Err(Error::NullString("GPIO line's consumer name")); + } + + // SAFETY: The string is guaranteed to be valid here by the C API. + unsafe { CStr::from_ptr(name) } + .to_str() + .map_err(Error::StringNotUtf8) + } + + /// Get the GPIO line's direction. + pub fn direction(&self) -> Result { + // SAFETY: `gpiod_line_info` is guaranteed to be valid here. + Direction::new(unsafe { gpiod::gpiod_line_info_get_direction(self.info) } as u32) + } + + /// Returns true if the line is "active-low", false otherwise. + pub fn is_active_low(&self) -> bool { + // SAFETY: `gpiod_line_info` is guaranteed to be valid here. + unsafe { gpiod::gpiod_line_info_is_active_low(self.info) } + } + + /// Get the GPIO line's bias setting. + pub fn bias(&self) -> Result> { + // SAFETY: `gpiod_line_info` is guaranteed to be valid here. + Bias::new(unsafe { gpiod::gpiod_line_info_get_bias(self.info) } as u32) + } + + /// Get the GPIO line's drive setting. + pub fn drive(&self) -> Result { + // SAFETY: `gpiod_line_info` is guaranteed to be valid here. + Drive::new(unsafe { gpiod::gpiod_line_info_get_drive(self.info) } as u32) + } + + /// Get the current edge detection setting of the line. + pub fn edge_detection(&self) -> Result> { + // SAFETY: `gpiod_line_info` is guaranteed to be valid here. + Edge::new(unsafe { gpiod::gpiod_line_info_get_edge_detection(self.info) } as u32) + } + + /// Get the current event clock setting used for edge event timestamps. + pub fn event_clock(&self) -> Result { + // SAFETY: `gpiod_line_info` is guaranteed to be valid here. + EventClock::new(unsafe { gpiod::gpiod_line_info_get_event_clock(self.info) } as u32) + } + + /// Returns true if the line is debounced (either by hardware or by the + /// kernel software debouncer), false otherwise. + pub fn is_debounced(&self) -> bool { + // SAFETY: `gpiod_line_info` is guaranteed to be valid here. + unsafe { gpiod::gpiod_line_info_is_debounced(self.info) } + } + + /// Get the debounce period of the line. + pub fn debounce_period(&self) -> Duration { + // SAFETY: `gpiod_line_info` is guaranteed to be valid here. + Duration::from_micros(unsafe { + gpiod::gpiod_line_info_get_debounce_period_us(self.info) as u64 + }) + } +} + +impl Drop for Info { + fn drop(&mut self) { + // We must not free the Line info object created from `struct chip::Event` by calling + // libgpiod API. + if !self.contained { + // SAFETY: `gpiod_line_info` is guaranteed to be valid here. + unsafe { gpiod::gpiod_line_info_free(self.info) } + } + } +} diff --git a/bindings/rust/libgpiod/src/line_request.rs b/bindings/rust/libgpiod/src/line_request.rs new file mode 100644 index 000000000000..7101d098b6f5 --- /dev/null +++ b/bindings/rust/libgpiod/src/line_request.rs @@ -0,0 +1,227 @@ +// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use std::os::{raw::c_ulong, unix::prelude::AsRawFd}; +use std::time::Duration; + +use super::{ + gpiod, + line::{self, Offset, Value, ValueMap}, + request, Error, OperationType, Result, +}; + +/// Line request operations +/// +/// Allows interaction with a set of requested lines. +#[derive(Debug, Eq, PartialEq)] +pub struct Request { + pub(crate) request: *mut gpiod::gpiod_line_request, +} + +impl Request { + /// Request a set of lines for exclusive usage. + pub(crate) fn new(request: *mut gpiod::gpiod_line_request) -> Result { + Ok(Self { request }) + } + + /// Get the number of lines in the request. + pub fn num_lines(&self) -> usize { + // SAFETY: `gpiod_line_request` is guaranteed to be valid here. + unsafe { gpiod::gpiod_line_request_get_num_lines(self.request) as usize } + } + + /// Get the offsets of lines in the request. + pub fn offsets(&self) -> Vec { + let mut offsets = vec![0; self.num_lines() as usize]; + + // SAFETY: `gpiod_line_request` is guaranteed to be valid here. + unsafe { gpiod::gpiod_line_request_get_offsets(self.request, offsets.as_mut_ptr()) }; + offsets + } + + /// Get the value (0 or 1) of a single line associated with the request. + pub fn value(&self, offset: Offset) -> Result { + // SAFETY: `gpiod_line_request` is guaranteed to be valid here. + let value = unsafe { gpiod::gpiod_line_request_get_value(self.request, offset) }; + + if value != 0 && value != 1 { + Err(Error::OperationFailed( + OperationType::LineRequestGetVal, + errno::errno(), + )) + } else { + Value::new(value) + } + } + + /// Get values of a subset of lines associated with the request. + pub fn values_subset(&self, offsets: &[Offset]) -> Result { + let mut values = vec![0; offsets.len()]; + + // SAFETY: `gpiod_line_request` is guaranteed to be valid here. + let ret = unsafe { + gpiod::gpiod_line_request_get_values_subset( + self.request, + offsets.len() as c_ulong, + offsets.as_ptr(), + values.as_mut_ptr(), + ) + }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::LineRequestGetValSubset, + errno::errno(), + )) + } else { + let mut map = ValueMap::new(); + + for (i, val) in values.iter().enumerate() { + map.insert(offsets[i].into(), Value::new(*val)?); + } + + Ok(map) + } + } + + /// Get values of all lines associated with the request. + pub fn values(&self) -> Result { + self.values_subset(&self.offsets()) + } + + /// Set the value of a single line associated with the request. + pub fn set_value(&self, offset: Offset, value: Value) -> Result<()> { + // SAFETY: `gpiod_line_request` is guaranteed to be valid here. + let ret = + unsafe { gpiod::gpiod_line_request_set_value(self.request, offset, value.value()) }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::LineRequestSetVal, + errno::errno(), + )) + } else { + Ok(()) + } + } + + /// Get values of a subset of lines associated with the request. + pub fn set_values_subset(&self, map: ValueMap) -> Result<()> { + let mut offsets = Vec::new(); + let mut values = Vec::new(); + + for (offset, value) in map { + offsets.push(offset as u32); + values.push(value.value()); + } + + // SAFETY: `gpiod_line_request` is guaranteed to be valid here. + let ret = unsafe { + gpiod::gpiod_line_request_set_values_subset( + self.request, + offsets.len() as c_ulong, + offsets.as_ptr(), + values.as_ptr(), + ) + }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::LineRequestSetValSubset, + errno::errno(), + )) + } else { + Ok(()) + } + } + + /// Get values of all lines associated with the request. + pub fn set_values(&self, values: &[Value]) -> Result<()> { + if values.len() != self.num_lines() as usize { + return Err(Error::InvalidArguments); + } + + let mut new_values = Vec::new(); + for value in values { + new_values.push(value.value()); + } + + // SAFETY: `gpiod_line_request` is guaranteed to be valid here. + let ret = + unsafe { gpiod::gpiod_line_request_set_values(self.request, new_values.as_ptr()) }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::LineRequestSetVal, + errno::errno(), + )) + } else { + Ok(()) + } + } + + /// Update the configuration of lines associated with the line request. + pub fn reconfigure_lines(&self, lconfig: &line::Config) -> Result<()> { + // SAFETY: `gpiod_line_request` is guaranteed to be valid here. + let ret = + unsafe { gpiod::gpiod_line_request_reconfigure_lines(self.request, lconfig.config) }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::LineRequestReconfigLines, + errno::errno(), + )) + } else { + Ok(()) + } + } + + /// Wait for edge events on any of the lines associated with the request. + pub fn wait_edge_event(&self, timeout: Option) -> Result { + let timeout = match timeout { + Some(x) => x.as_nanos() as i64, + // Block indefinitely + None => -1, + }; + + // SAFETY: `gpiod_line_request` is guaranteed to be valid here. + let ret = unsafe { gpiod::gpiod_line_request_wait_edge_event(self.request, timeout) }; + + match ret { + -1 => Err(Error::OperationFailed( + OperationType::LineRequestWaitEdgeEvent, + errno::errno(), + )), + 0 => Ok(false), + _ => Ok(true), + } + } + + /// Get a number of edge events from a line request. + /// + /// This function will block if no event was queued for the line. + pub fn read_edge_events<'a>( + &'a self, + buffer: &'a mut request::Buffer, + ) -> Result { + buffer.read_edge_events(self) + } +} + +impl AsRawFd for Request { + /// Get the file descriptor associated with the line request. + fn as_raw_fd(&self) -> i32 { + // SAFETY: `gpiod_line_request` is guaranteed to be valid here. + unsafe { gpiod::gpiod_line_request_get_fd(self.request) } + } +} + +impl Drop for Request { + /// Release the requested lines and free all associated resources. + fn drop(&mut self) { + // SAFETY: `gpiod_line_request` is guaranteed to be valid here. + unsafe { gpiod::gpiod_line_request_release(self.request) } + } +} diff --git a/bindings/rust/libgpiod/src/line_settings.rs b/bindings/rust/libgpiod/src/line_settings.rs new file mode 100644 index 000000000000..50242d2c841a --- /dev/null +++ b/bindings/rust/libgpiod/src/line_settings.rs @@ -0,0 +1,297 @@ +// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use std::time::Duration; + +use super::{ + gpiod, + line::{Bias, Direction, Drive, Edge, EventClock, SettingKind, SettingVal, Value}, + Error, OperationType, Result, +}; + +/// Line settings objects. +/// +/// Line settings object contains a set of line properties that can be used +/// when requesting lines or reconfiguring an existing request. +/// +/// Mutators in general can only fail if the new property value is invalid. The +/// return values can be safely ignored - the object remains valid even after +/// a mutator fails and simply uses the sane default appropriate for given +/// property. + +#[derive(Debug, Eq, PartialEq)] +pub struct Settings { + pub(crate) settings: *mut gpiod::gpiod_line_settings, +} + +impl Settings { + /// Create a new line settings object. + pub fn new() -> Result { + // SAFETY: The `gpiod_line_settings` returned by libgpiod is guaranteed to live as long + // as the `struct Settings`. + let settings = unsafe { gpiod::gpiod_line_settings_new() }; + + if settings.is_null() { + return Err(Error::OperationFailed( + OperationType::LineSettingsNew, + errno::errno(), + )); + } + + Ok(Self { settings }) + } + + pub fn new_with_settings(settings: *mut gpiod::gpiod_line_settings) -> Self { + Self { settings } + } + + /// Resets the line settings object to its default values. + pub fn reset(&mut self) { + // SAFETY: `gpiod_line_settings` is guaranteed to be valid here. + unsafe { gpiod::gpiod_line_settings_reset(self.settings) } + } + + /// Makes copy of the settings object. + pub fn settings_clone(&self) -> Result { + // SAFETY: `gpiod_line_settings` is guaranteed to be valid here. + let settings = unsafe { gpiod::gpiod_line_settings_copy(self.settings) }; + if settings.is_null() { + return Err(Error::OperationFailed( + OperationType::LineSettingsCopy, + errno::errno(), + )); + } + + Ok(Self { settings }) + } + + /// Set line prop setting. + pub fn set_prop(&mut self, props: &[SettingVal]) -> Result<&mut Self> { + for property in props { + match property { + SettingVal::Direction(prop) => self.set_direction(*prop)?, + SettingVal::EdgeDetection(prop) => self.set_edge_detection(*prop)?, + SettingVal::Bias(prop) => self.set_bias(*prop)?, + SettingVal::Drive(prop) => self.set_drive(*prop)?, + SettingVal::ActiveLow(prop) => self.set_active_low(*prop), + SettingVal::DebouncePeriod(prop) => self.set_debounce_period(*prop), + SettingVal::EventClock(prop) => self.set_event_clock(*prop)?, + SettingVal::OutputValue(prop) => self.set_output_value(*prop)?, + }; + } + + Ok(self) + } + + /// Get the line prop setting. + pub fn prop(&self, property: SettingKind) -> Result { + Ok(match property { + SettingKind::Direction => SettingVal::Direction(self.direction()?), + SettingKind::EdgeDetection => SettingVal::EdgeDetection(self.edge_detection()?), + SettingKind::Bias => SettingVal::Bias(self.bias()?), + SettingKind::Drive => SettingVal::Drive(self.drive()?), + SettingKind::ActiveLow => SettingVal::ActiveLow(self.active_low()), + SettingKind::DebouncePeriod => SettingVal::DebouncePeriod(self.debounce_period()?), + SettingKind::EventClock => SettingVal::EventClock(self.event_clock()?), + SettingKind::OutputValue => SettingVal::OutputValue(self.output_value()?), + }) + } + + /// Set the line direction. + pub fn set_direction(&mut self, direction: Direction) -> Result<&mut Self> { + // SAFETY: `gpiod_line_settings` is guaranteed to be valid here. + let ret = unsafe { + gpiod::gpiod_line_settings_set_direction( + self.settings, + direction.gpiod_direction() as i32, + ) + }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::LineSettingsSetDirection, + errno::errno(), + )) + } else { + Ok(self) + } + } + + /// Get the direction setting. + pub fn direction(&self) -> Result { + // SAFETY: `gpiod_line_settings` is guaranteed to be valid here. + Direction::new(unsafe { gpiod::gpiod_line_settings_get_direction(self.settings) } as u32) + } + + /// Set the edge event detection setting. + pub fn set_edge_detection(&mut self, edge: Option) -> Result<&mut Self> { + // SAFETY: `gpiod_line_settings` is guaranteed to be valid here. + let ret = unsafe { + gpiod::gpiod_line_settings_set_edge_detection( + self.settings, + Edge::gpiod_edge(edge) as i32, + ) + }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::LineSettingsSetEdgeDetection, + errno::errno(), + )) + } else { + Ok(self) + } + } + + /// Get the edge event detection setting. + pub fn edge_detection(&self) -> Result> { + // SAFETY: `gpiod_line_settings` is guaranteed to be valid here. + Edge::new(unsafe { gpiod::gpiod_line_settings_get_edge_detection(self.settings) } as u32) + } + + /// Set the bias setting. + pub fn set_bias(&mut self, bias: Option) -> Result<&mut Self> { + // SAFETY: `gpiod_line_settings` is guaranteed to be valid here. + let ret = unsafe { + gpiod::gpiod_line_settings_set_bias(self.settings, Bias::gpiod_bias(bias) as i32) + }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::LineSettingsSetBias, + errno::errno(), + )) + } else { + Ok(self) + } + } + + /// Get the bias setting. + pub fn bias(&self) -> Result> { + // SAFETY: `gpiod_line_settings` is guaranteed to be valid here. + Bias::new(unsafe { gpiod::gpiod_line_settings_get_bias(self.settings) } as u32) + } + + /// Set the drive setting. + pub fn set_drive(&mut self, drive: Drive) -> Result<&mut Self> { + // SAFETY: `gpiod_line_settings` is guaranteed to be valid here. + let ret = unsafe { + gpiod::gpiod_line_settings_set_drive(self.settings, drive.gpiod_drive() as i32) + }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::LineSettingsSetDrive, + errno::errno(), + )) + } else { + Ok(self) + } + } + + /// Get the drive setting. + pub fn drive(&self) -> Result { + // SAFETY: `gpiod_line_settings` is guaranteed to be valid here. + Drive::new(unsafe { gpiod::gpiod_line_settings_get_drive(self.settings) } as u32) + } + + /// Set active-low setting. + pub fn set_active_low(&mut self, active_low: bool) -> &mut Self { + // SAFETY: `gpiod_line_settings` is guaranteed to be valid here. + unsafe { + gpiod::gpiod_line_settings_set_active_low(self.settings, active_low); + } + self + } + + /// Check the active-low setting. + pub fn active_low(&self) -> bool { + // SAFETY: `gpiod_line_settings` is guaranteed to be valid here. + unsafe { gpiod::gpiod_line_settings_get_active_low(self.settings) } + } + + /// Set the debounce period setting. + pub fn set_debounce_period(&mut self, period: Duration) -> &mut Self { + // SAFETY: `gpiod_line_settings` is guaranteed to be valid here. + unsafe { + gpiod::gpiod_line_settings_set_debounce_period_us( + self.settings, + period.as_micros().try_into().unwrap(), + ); + } + + self + } + + /// Get the debounce period. + pub fn debounce_period(&self) -> Result { + // SAFETY: `gpiod_line_settings` is guaranteed to be valid here. + Ok(Duration::from_micros(unsafe { + gpiod::gpiod_line_settings_get_debounce_period_us(self.settings) as u64 + })) + } + + /// Set the event clock setting. + pub fn set_event_clock(&mut self, clock: EventClock) -> Result<&mut Self> { + // SAFETY: `gpiod_line_settings` is guaranteed to be valid here. + let ret = unsafe { + gpiod::gpiod_line_settings_set_event_clock(self.settings, clock.gpiod_clock() as i32) + }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::LineSettingsSetEventClock, + errno::errno(), + )) + } else { + Ok(self) + } + } + + /// Get the event clock setting. + pub fn event_clock(&self) -> Result { + // SAFETY: `gpiod_line_settings` is guaranteed to be valid here. + EventClock::new(unsafe { gpiod::gpiod_line_settings_get_event_clock(self.settings) } as u32) + } + + /// Set the output value setting. + pub fn set_output_value(&mut self, value: Value) -> Result<&mut Self> { + // SAFETY: `gpiod_line_settings` is guaranteed to be valid here. + let ret = + unsafe { gpiod::gpiod_line_settings_set_output_value(self.settings, value.value()) }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::LineSettingsSetOutputValue, + errno::errno(), + )) + } else { + Ok(self) + } + } + + /// Get the output value, 0 or 1. + pub fn output_value(&self) -> Result { + // SAFETY: `gpiod_line_settings` is guaranteed to be valid here. + let value = unsafe { gpiod::gpiod_line_settings_get_output_value(self.settings) }; + + if value != 0 && value != 1 { + Err(Error::OperationFailed( + OperationType::LineSettingsGetOutVal, + errno::errno(), + )) + } else { + Value::new(value) + } + } +} + +impl Drop for Settings { + /// Free the line settings object and release all associated resources. + fn drop(&mut self) { + // SAFETY: `gpiod_line_settings` is guaranteed to be valid here. + unsafe { gpiod::gpiod_line_settings_free(self.settings) } + } +} diff --git a/bindings/rust/libgpiod/src/request_config.rs b/bindings/rust/libgpiod/src/request_config.rs new file mode 100644 index 000000000000..4b5247548254 --- /dev/null +++ b/bindings/rust/libgpiod/src/request_config.rs @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use std::ffi::{CStr, CString}; +use std::os::raw::{c_char, c_ulong}; +use std::str; + +use super::{gpiod, Error, OperationType, Result}; + +/// Request configuration objects +/// +/// Request config objects are used to pass a set of options to the kernel at +/// the time of the line request. The mutators don't return error values. If the +/// values are invalid, in general they are silently adjusted to acceptable +/// ranges. + +#[derive(Debug, Eq, PartialEq)] +pub struct Config { + pub(crate) config: *mut gpiod::gpiod_request_config, +} + +impl Config { + /// Create a new request config object. + pub fn new() -> Result { + // SAFETY: The `gpiod_request_config` returned by libgpiod is guaranteed to live as long + // as the `struct Config`. + let config = unsafe { gpiod::gpiod_request_config_new() }; + if config.is_null() { + return Err(Error::OperationFailed( + OperationType::RequestConfigNew, + errno::errno(), + )); + } + + Ok(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) -> Result<()> { + let consumer = CString::new(consumer).map_err(|_| Error::InvalidString)?; + + // SAFETY: `gpiod_request_config` is guaranteed to be valid here. + unsafe { + gpiod::gpiod_request_config_set_consumer( + self.config, + consumer.as_ptr() as *const c_char, + ) + } + + Ok(()) + } + + /// Get the consumer name configured in the request config. + pub fn consumer(&self) -> Result<&str> { + // SAFETY: The string returned by libgpiod is guaranteed to live as long + // as the `struct Config`. + let consumer = unsafe { gpiod::gpiod_request_config_get_consumer(self.config) }; + if consumer.is_null() { + return Err(Error::OperationFailed( + OperationType::RequestConfigGetConsumer, + errno::errno(), + )); + } + + // SAFETY: The string is guaranteed to be valid here by the C API. + unsafe { CStr::from_ptr(consumer) } + .to_str() + .map_err(Error::StringNotUtf8) + } + + /// Set the size of the kernel event buffer for the request. + pub fn set_event_buffer_size(&self, size: usize) { + // SAFETY: `gpiod_request_config` is guaranteed to be valid here. + unsafe { gpiod::gpiod_request_config_set_event_buffer_size(self.config, size as c_ulong) } + } + + /// Get the edge event buffer size setting for the request config. + pub fn event_buffer_size(&self) -> usize { + // SAFETY: `gpiod_request_config` is guaranteed to be valid here. + unsafe { gpiod::gpiod_request_config_get_event_buffer_size(self.config) as usize } + } +} + +impl Drop for Config { + /// Free the request config object and release all associated resources. + fn drop(&mut self) { + // SAFETY: `gpiod_request_config` is guaranteed to be valid here. + unsafe { gpiod::gpiod_request_config_free(self.config) } + } +} From patchwork Mon Nov 7 09:57:32 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 623160 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 15A12C4332F for ; Mon, 7 Nov 2022 09:58:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231679AbiKGJ6E (ORCPT ); Mon, 7 Nov 2022 04:58:04 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49286 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231149AbiKGJ6C (ORCPT ); Mon, 7 Nov 2022 04:58:02 -0500 Received: from mail-pg1-x534.google.com (mail-pg1-x534.google.com [IPv6:2607:f8b0:4864:20::534]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9E82B25C2 for ; Mon, 7 Nov 2022 01:58:00 -0800 (PST) Received: by mail-pg1-x534.google.com with SMTP id o13so694042pgu.7 for ; Mon, 07 Nov 2022 01:58:00 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=sBTGaKJPENfs1XtcdM0uFma9isOAobp+kmrZKLTRIg0=; b=siuOMyj6DZUq9Saf5sx1gxnve6EblBiMBEtIEMsF3fPQ9OosqNwQS1+uzZs7zTuUaL oORzOM/oUlz7bkF76Mn/VBckoKHe/qtdBMJezMjmqD9E9oRr7NKYB2xQwe0EDZHroFJK +nOYL0/NHQJFx/kPq/bvrzDJc7GkCJmrJWkhOcLGj4rscWVhShf5Wy8CRGVgW4NvGJyC 7GCSlvM3WYsCMGXNP/06b9NkUYasITDJDnWk1GGV0PbWibfWtnJWj2nVA2VuZg4vKSPr la6kYUd0VzMp6kztzmQ1wjjW6tcHg5JaF7zCUV2ey4h0RwNuYMgYwtfbBXeKw1rVO1UA 3h2A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=sBTGaKJPENfs1XtcdM0uFma9isOAobp+kmrZKLTRIg0=; b=vYzm9HHLSRHrHiH/xKozye3Y8EhHOOxhprFnHuQWcOTvjLd4B0yZud0qJT7GfbEz00 TqK4jYqReTcM0O/fgaFqYfWRsqjYRG3ng9I2efQPqjqsfB2cB4H9bxwCReweuiNMP/f/ pze9Z/IMvzEOGvTKBarc59laFU267blCSScBfnKx1L/ZsbC3N4Czn4W1Ze3dXcCu3leT Rrs2RsHQ4mnnncH7+gQ8fRPCG1IDSPs0waFarirZBLOCyb1mJaQXbTCHfosLehnm36Mt 5Kq7IJECOqbNUv3InhYuP0qKoF9nBvZx4fN95mT4lcC9spYb3h0wj88QOfC2QNkl4WWx xSng== X-Gm-Message-State: ACrzQf3qkNfSwZ6PAkeSKKwzKXYZ9X6Hgwbu/LVqoVtqu4o9JELPj08u wOUIUtqwQfbvQKiHje7plCQ5gA== X-Google-Smtp-Source: AMsMyM5DLIhgZEBsvbc6+for+7siftbL1O7zTIJKE7x5YJ5LfybQ6r6rAlMkYLg6Ys3C2ux8yMH2EQ== X-Received: by 2002:aa7:8c15:0:b0:56b:ead2:3950 with SMTP id c21-20020aa78c15000000b0056bead23950mr50156240pfd.77.1667815080001; Mon, 07 Nov 2022 01:58:00 -0800 (PST) Received: from localhost ([122.172.84.80]) by smtp.gmail.com with ESMTPSA id z6-20020a623306000000b0056328e516f4sm4220990pfz.148.2022.11.07.01.57.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 07 Nov 2022 01:57:59 -0800 (PST) From: Viresh Kumar To: Linus Walleij , Bartosz Golaszewski , Kent Gibson Cc: Viresh Kumar , Vincent Guittot , linux-gpio@vger.kernel.org, Miguel Ojeda , Wedson Almeida Filho , =?utf-8?q?Alex_Benn=C3=A9e?= , stratos-dev@op-lists.linaro.org, Gerard Ryan , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , y86-dev Subject: [libgpiod][PATCH V9 6/8] bindings: rust: Add examples to libgpiod crate Date: Mon, 7 Nov 2022 15:27:32 +0530 Message-Id: <43c0d28fc12bbfb5a0feba0d30542a4ca2d4bad6.1667815011.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 examples for the usage of the rust bindings, quite similar to the ones in cxx bindings. Signed-off-by: Viresh Kumar --- .../rust/libgpiod/examples/gpio_events.rs | 89 ++++++++++++ .../examples/gpio_threaded_info_events.rs | 133 ++++++++++++++++++ bindings/rust/libgpiod/examples/gpiodetect.rs | 31 ++++ bindings/rust/libgpiod/examples/gpiofind.rs | 37 +++++ bindings/rust/libgpiod/examples/gpioget.rs | 46 ++++++ bindings/rust/libgpiod/examples/gpioinfo.rs | 98 +++++++++++++ bindings/rust/libgpiod/examples/gpiomon.rs | 75 ++++++++++ bindings/rust/libgpiod/examples/gpioset.rs | 64 +++++++++ bindings/rust/libgpiod/examples/gpiowatch.rs | 54 +++++++ 9 files changed, 627 insertions(+) create mode 100644 bindings/rust/libgpiod/examples/gpio_events.rs create mode 100644 bindings/rust/libgpiod/examples/gpio_threaded_info_events.rs create mode 100644 bindings/rust/libgpiod/examples/gpiodetect.rs create mode 100644 bindings/rust/libgpiod/examples/gpiofind.rs create mode 100644 bindings/rust/libgpiod/examples/gpioget.rs create mode 100644 bindings/rust/libgpiod/examples/gpioinfo.rs create mode 100644 bindings/rust/libgpiod/examples/gpiomon.rs create mode 100644 bindings/rust/libgpiod/examples/gpioset.rs create mode 100644 bindings/rust/libgpiod/examples/gpiowatch.rs diff --git a/bindings/rust/libgpiod/examples/gpio_events.rs b/bindings/rust/libgpiod/examples/gpio_events.rs new file mode 100644 index 000000000000..6f3c4d5cadb3 --- /dev/null +++ b/bindings/rust/libgpiod/examples/gpio_events.rs @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar +// +// Simplified Rust implementation to show handling of events, when the buffer +// is read into multiple times. Based on gpiomon example. + +use std::env; + +use libgpiod::{ + chip::Chip, + line::{self, Edge, Offset}, + request, Error, Result, +}; + +fn usage(name: &str) { + println!("Usage: {} ...", name); +} + +fn main() -> Result<()> { + let args: Vec = env::args().collect(); + if args.len() < 3 { + usage(&args[0]); + return Err(Error::InvalidArguments); + } + + let mut lsettings = line::Settings::new()?; + let lconfig = line::Config::new()?; + let mut offsets = Vec::::new(); + + for arg in &args[2..] { + let offset = arg.parse::().map_err(|_| Error::InvalidArguments)?; + offsets.push(offset); + } + + lsettings.set_edge_detection(Some(Edge::Both))?; + lconfig.add_line_settings(&offsets, lsettings)?; + + let path = format!("/dev/gpiochip{}", args[1]); + let chip = Chip::open(&path)?; + + let rconfig = request::Config::new()?; + + let mut buffer = request::Buffer::new(1)?; + let request = chip.request_lines(&rconfig, &lconfig)?; + + loop { + match request.wait_edge_event(None) { + Err(x) => { + println!("{:?}", x); + return Err(Error::InvalidArguments); + } + + Ok(false) => { + // This shouldn't happen as the call is blocking. + panic!(); + } + Ok(true) => (), + } + + let mut events = request.read_edge_events(&mut buffer)?; + + // This can't be used across the next read_edge_events(). + let event = events.next().unwrap()?; + + // This will out live `event` and the next read_edge_events(). + let cloned_event = request::Event::event_clone(event)?; + + let events = request.read_edge_events(&mut buffer)?; + for event in events { + let event = event?; + println!( + "line: {} type: {:?}, time: {:?}", + event.line_offset(), + event.event_type(), + event.timestamp() + ); + } + + // `cloned_event` is still available to be used. + println!( + "line: {} type: {:?}, time: {:?}", + cloned_event.line_offset(), + cloned_event.event_type(), + cloned_event.timestamp() + ); + } +} diff --git a/bindings/rust/libgpiod/examples/gpio_threaded_info_events.rs b/bindings/rust/libgpiod/examples/gpio_threaded_info_events.rs new file mode 100644 index 000000000000..04566f92e66c --- /dev/null +++ b/bindings/rust/libgpiod/examples/gpio_threaded_info_events.rs @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar +// +// Simplified Rust implementation to show handling of info events, that are +// generated from another thread. + +use std::{ + env, + sync::{ + mpsc::{self, Receiver, Sender}, + Arc, Mutex, + }, + thread, + time::Duration, +}; + +use libgpiod::{ + chip::Chip, + line::{self, Direction, InfoChangeKind, Offset}, + request, Error, Result, +}; + +fn usage(name: &str) { + println!("Usage: {} ", name); +} + +fn request_reconfigure_line( + chip: Arc>, + offset: Offset, + tx: Sender<()>, + rx: Receiver<()>, +) { + thread::spawn(move || { + let lconfig = line::Config::new().unwrap(); + let lsettings = line::Settings::new().unwrap(); + lconfig.add_line_settings(&[offset], lsettings).unwrap(); + let rconfig = request::Config::new().unwrap(); + + let request = chip + .lock() + .unwrap() + .request_lines(&rconfig, &lconfig) + .unwrap(); + + // Signal the parent to continue + tx.send(()).expect("Could not send signal on channel"); + + // Wait for parent to signal + rx.recv().expect("Could not receive from channel"); + + let lconfig = line::Config::new().unwrap(); + let mut lsettings = line::Settings::new().unwrap(); + lsettings.set_direction(Direction::Output).unwrap(); + lconfig.add_line_settings(&[offset], lsettings).unwrap(); + + request.reconfigure_lines(&lconfig).unwrap(); + + // Signal the parent to continue + tx.send(()).expect("Could not send signal on channel"); + + // Wait for parent to signal + rx.recv().expect("Could not receive from channel"); + }); +} + +fn main() -> Result<()> { + let args: Vec = env::args().collect(); + if args.len() != 3 { + usage(&args[0]); + return Err(Error::InvalidArguments); + } + + let path = format!("/dev/gpiochip{}", args[1]); + let offset = args[2] + .parse::() + .map_err(|_| Error::InvalidArguments)?; + + let chip = Arc::new(Mutex::new(Chip::open(&path)?)); + chip.lock().unwrap().watch_line_info(offset)?; + + // Thread synchronizing mechanism + let (tx_main, rx_thread) = mpsc::channel(); + let (tx_thread, rx_main) = mpsc::channel(); + + // Generate events + request_reconfigure_line(chip.clone(), offset, tx_thread, rx_thread); + + // Wait for thread to signal + rx_main.recv().expect("Could not receive from channel"); + + // Line requested event + assert!(chip + .lock() + .unwrap() + .wait_info_event(Some(Duration::from_secs(1)))?); + let event = chip.lock().unwrap().read_info_event()?; + assert_eq!(event.event_type()?, InfoChangeKind::LineRequested); + + // Signal the thread to continue + tx_main.send(()).expect("Could not send signal on channel"); + + // Wait for thread to signal + rx_main.recv().expect("Could not receive from channel"); + + // Line changed event + assert!(chip + .lock() + .unwrap() + .wait_info_event(Some(Duration::from_millis(10)))?); + let event = chip.lock().unwrap().read_info_event()?; + assert_eq!(event.event_type()?, InfoChangeKind::LineConfigChanged); + + // Signal the thread to continue + tx_main.send(()).expect("Could not send signal on channel"); + + // Line released event + assert!(chip + .lock() + .unwrap() + .wait_info_event(Some(Duration::from_millis(10)))?); + let event = chip.lock().unwrap().read_info_event()?; + assert_eq!(event.event_type().unwrap(), InfoChangeKind::LineReleased); + + // No events available + assert!(!chip + .lock() + .unwrap() + .wait_info_event(Some(Duration::from_millis(100)))?); + + Ok(()) +} diff --git a/bindings/rust/libgpiod/examples/gpiodetect.rs b/bindings/rust/libgpiod/examples/gpiodetect.rs new file mode 100644 index 000000000000..e1d8f59268b5 --- /dev/null +++ b/bindings/rust/libgpiod/examples/gpiodetect.rs @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar +// +// Simplified Rust implementation of gpiodetect tool. + +use std::env; +use std::path::Path; + +use libgpiod::{self, Error, Result}; + +fn main() -> Result<()> { + let args: Vec = env::args().collect(); + if args.len() > 1 { + println!("Usage: {}", args[0]); + return Err(Error::InvalidArguments); + } + + for chip in libgpiod::gpiochip_devices(&Path::new("/dev"))? { + let info = chip.info()?; + println!( + "{} [{}] ({})", + info.name()?, + info.label()?, + info.num_lines(), + ); + } + + Ok(()) +} diff --git a/bindings/rust/libgpiod/examples/gpiofind.rs b/bindings/rust/libgpiod/examples/gpiofind.rs new file mode 100644 index 000000000000..daaa93cc1bd2 --- /dev/null +++ b/bindings/rust/libgpiod/examples/gpiofind.rs @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar +// +// Simplified Rust implementation of gpiofind tool. + +use std::env; +use std::path::Path; + +use libgpiod::{self, Error, Result}; + +fn main() -> Result<()> { + let args: Vec = env::args().collect(); + if args.len() != 2 { + println!("Usage: {} ", args[0]); + return Err(Error::InvalidArguments); + } + + for chip in libgpiod::gpiochip_devices(&Path::new("/dev"))? { + let offset = chip.line_offset_from_name(&args[1]); + let info = chip.info()?; + + if offset.is_ok() { + println!( + "Line {} found: Chip: {}, offset: {}", + args[1], + info.name()?, + offset? + ); + return Ok(()); + } + } + + println!("Failed to find line: {}", args[1]); + Ok(()) +} diff --git a/bindings/rust/libgpiod/examples/gpioget.rs b/bindings/rust/libgpiod/examples/gpioget.rs new file mode 100644 index 000000000000..5ae50a4fdead --- /dev/null +++ b/bindings/rust/libgpiod/examples/gpioget.rs @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar +// +// Simplified Rust implementation of gpioget tool. + +use std::env; + +use libgpiod::{ + chip::Chip, + line::{self, Direction, Offset}, + request, Error, Result, +}; + +fn main() -> Result<()> { + let args: Vec = env::args().collect(); + if args.len() < 3 { + println!("Usage: {} ...", args[0]); + return Err(Error::InvalidArguments); + } + + let mut lsettings = line::Settings::new()?; + let lconfig = line::Config::new()?; + let mut offsets = Vec::::new(); + + for arg in &args[2..] { + let offset = arg.parse::().map_err(|_| Error::InvalidArguments)?; + offsets.push(offset); + } + + lsettings.set_direction(Direction::Input)?; + lconfig.add_line_settings(&offsets, lsettings)?; + + let path = format!("/dev/gpiochip{}", args[1]); + let chip = Chip::open(&path)?; + + let rconfig = request::Config::new()?; + rconfig.set_consumer(&args[0])?; + + let request = chip.request_lines(&rconfig, &lconfig)?; + let map = request.values()?; + + println!("{:?}", map); + Ok(()) +} diff --git a/bindings/rust/libgpiod/examples/gpioinfo.rs b/bindings/rust/libgpiod/examples/gpioinfo.rs new file mode 100644 index 000000000000..f972e4f405d2 --- /dev/null +++ b/bindings/rust/libgpiod/examples/gpioinfo.rs @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar +// +// Simplified Rust implementation of gpioinfo tool. + +use std::env; +use std::path::Path; + +use libgpiod::{ + chip::Chip, + line::{Direction, Offset}, + Error, Result, +}; + +fn line_info(chip: &Chip, offset: Offset) -> Result<()> { + let info = chip.line_info(offset)?; + let off = info.offset(); + + let name = match info.name() { + Ok(name) => name, + _ => "unused", + }; + + let consumer = match info.consumer() { + Ok(name) => name, + _ => "unnamed", + }; + + let low = if info.is_active_low() { + "active-low" + } else { + "active-high" + }; + + let dir = match info.direction()? { + Direction::AsIs => "None", + Direction::Input => "Input", + Direction::Output => "Output", + }; + + println!( + "\tline {:>3}\ + \t{:>10}\ + \t{:>10}\ + \t{:>6}\ + \t{:>14}", + off, name, consumer, dir, low + ); + + Ok(()) +} + +fn chip_info(chip: &Chip) -> Result<()> { + let info = chip.info()?; + let ngpio = info.num_lines(); + + println!("GPIO Chip name: {}", info.name()?); + println!("\tlabel: {}", info.label()?); + println!("\tpath: {}", chip.path()?); + println!("\tngpio: {}\n", ngpio); + + println!("\tLine information:"); + + for offset in 0..ngpio { + line_info(chip, offset as Offset)?; + } + println!("\n"); + + Ok(()) +} + +fn main() -> Result<()> { + let args: Vec = env::args().collect(); + if args.len() > 2 { + println!("Usage: {}", args[0]); + return Err(Error::InvalidArguments); + } + + if args.len() == 1 { + for chip in libgpiod::gpiochip_devices(&Path::new("/dev"))? { + chip_info(&chip)?; + } + } else { + let index = args[1] + .parse::() + .map_err(|_| Error::InvalidArguments)?; + let path = format!("/dev/gpiochip{}", index); + if libgpiod::is_gpiochip_device(&path) { + let chip = Chip::open(&path)?; + + chip_info(&chip)?; + } + } + + Ok(()) +} diff --git a/bindings/rust/libgpiod/examples/gpiomon.rs b/bindings/rust/libgpiod/examples/gpiomon.rs new file mode 100644 index 000000000000..fb5b208b3972 --- /dev/null +++ b/bindings/rust/libgpiod/examples/gpiomon.rs @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar +// +// Simplified Rust implementation of the gpiomon tool. + +use std::env; + +use libgpiod::{ + chip::Chip, + line::{self, Edge, EdgeKind, Offset}, + request, Error, Result, +}; + +fn usage(name: &str) { + println!("Usage: {} ...", name); +} + +fn main() -> Result<()> { + let args: Vec = env::args().collect(); + if args.len() < 3 { + usage(&args[0]); + return Err(Error::InvalidArguments); + } + + let mut lsettings = line::Settings::new()?; + let lconfig = line::Config::new()?; + let mut offsets = Vec::::new(); + + for arg in &args[2..] { + let offset = arg.parse::().map_err(|_| Error::InvalidArguments)?; + offsets.push(offset); + } + + lsettings.set_edge_detection(Some(Edge::Both))?; + lconfig.add_line_settings(&offsets, lsettings)?; + + let path = format!("/dev/gpiochip{}", args[1]); + let chip = Chip::open(&path)?; + + let rconfig = request::Config::new()?; + + let mut buffer = request::Buffer::new(1)?; + let request = chip.request_lines(&rconfig, &lconfig)?; + + loop { + match request.wait_edge_event(None) { + Err(x) => { + println!("{:?}", x); + return Err(Error::InvalidArguments); + } + + Ok(false) => { + // This shouldn't happen as the call is blocking. + panic!(); + } + Ok(true) => (), + } + + let events = request.read_edge_events(&mut buffer)?; + for event in events { + let event = event?; + println!( + "line: {} type: {}, time: {:?}", + event.line_offset(), + match event.event_type()? { + EdgeKind::Rising => "Rising", + EdgeKind::Falling => "Falling", + }, + event.timestamp() + ); + } + } +} diff --git a/bindings/rust/libgpiod/examples/gpioset.rs b/bindings/rust/libgpiod/examples/gpioset.rs new file mode 100644 index 000000000000..f72a623ab28c --- /dev/null +++ b/bindings/rust/libgpiod/examples/gpioset.rs @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar +// +// Simplified Rust implementation of the gpioset tool. + +use std::env; +use std::io::{stdin, Read}; + +use libgpiod::{ + chip::Chip, + line::{self, Direction, Offset, SettingVal, Value}, + request, Error, Result, +}; + +fn usage(name: &str) { + println!("Usage: {} = ...", name); +} + +fn main() -> Result<()> { + let args: Vec = env::args().collect(); + if args.len() < 3 { + usage(&args[0]); + return Err(Error::InvalidArguments); + } + + let lconfig = line::Config::new()?; + + for arg in &args[2..] { + let pair: Vec<&str> = arg.split('=').collect(); + if pair.len() != 2 { + usage(&args[0]); + return Err(Error::InvalidArguments); + } + + let offset = pair[0] + .parse::() + .map_err(|_| Error::InvalidArguments)?; + let value = pair[1] + .parse::() + .map_err(|_| Error::InvalidArguments)?; + + let mut lsettings = line::Settings::new()?; + lsettings.set_prop(&[ + SettingVal::Direction(Direction::Output), + SettingVal::OutputValue(Value::new(value)?), + ])?; + lconfig.add_line_settings(&[offset], lsettings)?; + } + + let path = format!("/dev/gpiochip{}", args[1]); + let chip = Chip::open(&path)?; + + let rconfig = request::Config::new()?; + rconfig.set_consumer(&args[0])?; + + chip.request_lines(&rconfig, &lconfig)?; + + // Wait for keypress, let user verify line status. + stdin().read_exact(&mut [0u8]).unwrap(); + + Ok(()) +} diff --git a/bindings/rust/libgpiod/examples/gpiowatch.rs b/bindings/rust/libgpiod/examples/gpiowatch.rs new file mode 100644 index 000000000000..ea62515b1e0d --- /dev/null +++ b/bindings/rust/libgpiod/examples/gpiowatch.rs @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar +// +// Simplified Rust implementation of the gpiowatch tool. + +use std::env; + +use libgpiod::{chip::Chip, line::Offset, Error, Result}; + +fn usage(name: &str) { + println!("Usage: {} ...", name); +} + +fn main() -> Result<()> { + let args: Vec = env::args().collect(); + if args.len() < 2 { + usage(&args[0]); + return Err(Error::InvalidArguments); + } + + let path = format!("/dev/gpiochip{}", args[1]); + let offset = args[2] + .parse::() + .map_err(|_| Error::InvalidArguments)?; + + let chip = Chip::open(&path)?; + let _info = chip.watch_line_info(offset)?; + + match chip.wait_info_event(None) { + Err(x) => { + println!("{:?}", x); + return Err(Error::InvalidArguments); + } + + Ok(false) => { + // This shouldn't happen as the call is blocking. + panic!(); + } + Ok(true) => (), + } + + let event = chip.read_info_event()?; + println!( + "line: {} type: {:?}, time: {:?}", + offset, + event.event_type()?, + event.timestamp() + ); + + chip.unwatch(offset); + Ok(()) +} From patchwork Mon Nov 7 09:57:33 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 623159 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 07DD5C433FE for ; Mon, 7 Nov 2022 09:58:12 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231423AbiKGJ6K (ORCPT ); Mon, 7 Nov 2022 04:58:10 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49318 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231355AbiKGJ6H (ORCPT ); Mon, 7 Nov 2022 04:58:07 -0500 Received: from mail-pg1-x529.google.com (mail-pg1-x529.google.com [IPv6:2607:f8b0:4864:20::529]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 729C82D2 for ; Mon, 7 Nov 2022 01:58:04 -0800 (PST) Received: by mail-pg1-x529.google.com with SMTP id h193so9978192pgc.10 for ; Mon, 07 Nov 2022 01:58:04 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=8d2PErGdXD/MhLFRaUbTnnc+KavJ2Xmj6RfFj5OtMN4=; b=imEl3FS6uqcOEl/7Rn46sku+i0gkUd9YL5E1X6yfTXi/dgcQJMyFLJQ9k/rF9EJen1 GemdRigGO3TmbowCNV4g4kNkxK47UNSTPv3/WmQ/ImTFvmd+HP+zUbfoQaK21u9BRLiV uNgPSYZnwEfJYxpJtXiJOzv+EoLrQBqvxYkd2p+esCCLvoX9QoQGvq+e+5wwVw5p7Q0r DSF5+c0Cq+hU+OxCIqIaE+FJYDEPKtg8Wv35Ag/01LHJGGOk7UVs4c7bFNloCHMidgif oZRhZwB7+/IrdZL0WFdEFcir3KAu80kA/QUFMy48YTVkjsyi42YJEwsD/V2gfj0lhUs3 oSWg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=8d2PErGdXD/MhLFRaUbTnnc+KavJ2Xmj6RfFj5OtMN4=; b=u2dHQcB1P2fgt7aLTy8s1ViYdpr4aMMH02o3RvexkD8JDfmyhIyl94n5KKd2H3Ryln tzay/YjSbNe1ymtFtv0cqH1Jfipwrb1Xm5HlVXnFVU401y4Huyjiv16PA9McCJNiZIPy zEowI0Ar1kLW7vTZoeBkFe3fYXgGSQChFbwk/8fdrVLOTh/f4AU6PPMks8cw0nHGW/7G CoAWp3kPxp75wWPh2hGPUpQL7kx5K4NcWIeR6NfVcp9LT6H8sY/Hsx/5AHCa5rZzjHG9 2EJ5MBo0rlstaTbNRtSwnmOMGZnJtsiXDYi1S3+Jllqj4iCR9O5R+A7Eq9x5rkto/Lnp CgYw== X-Gm-Message-State: ACrzQf0e5OOIqmkF4b5rrpArH1Hs+5hJDGAnfxNDV0H6tpewC8zsjmbS Ztu3VxcOsHxS0GIEgNpE9aNQaw== X-Google-Smtp-Source: AMsMyM68+Rj5KKKwx0v6ahulcsC9NzTngRVgpnuT0Jb92PU2+ZY3ruUCBRdmqNMttDvDrNqwaW3JmQ== X-Received: by 2002:aa7:8013:0:b0:537:15fc:f336 with SMTP id j19-20020aa78013000000b0053715fcf336mr808660pfi.60.1667815083277; Mon, 07 Nov 2022 01:58:03 -0800 (PST) Received: from localhost ([122.172.84.80]) by smtp.gmail.com with ESMTPSA id oj15-20020a17090b4d8f00b0020c899b11f1sm5806007pjb.23.2022.11.07.01.58.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 07 Nov 2022 01:58:02 -0800 (PST) From: Viresh Kumar To: Linus Walleij , Bartosz Golaszewski , Kent Gibson Cc: Viresh Kumar , Vincent Guittot , linux-gpio@vger.kernel.org, Miguel Ojeda , Wedson Almeida Filho , =?utf-8?q?Alex_Benn=C3=A9e?= , stratos-dev@op-lists.linaro.org, Gerard Ryan , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , y86-dev Subject: [libgpiod][PATCH V9 7/8] bindings: rust: Add tests for libgpiod crate Date: Mon, 7 Nov 2022 15:27:33 +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 tests for the rust bindings, quite similar to the ones in cxx bindings. Signed-off-by: Viresh Kumar --- bindings/rust/libgpiod/Cargo.toml | 3 + bindings/rust/libgpiod/tests/chip.rs | 98 ++++ bindings/rust/libgpiod/tests/common/config.rs | 143 +++++ bindings/rust/libgpiod/tests/common/mod.rs | 10 + bindings/rust/libgpiod/tests/edge_event.rs | 298 ++++++++++ bindings/rust/libgpiod/tests/info_event.rs | 167 ++++++ bindings/rust/libgpiod/tests/line_config.rs | 96 ++++ bindings/rust/libgpiod/tests/line_info.rs | 276 ++++++++++ bindings/rust/libgpiod/tests/line_request.rs | 510 ++++++++++++++++++ bindings/rust/libgpiod/tests/line_settings.rs | 204 +++++++ .../rust/libgpiod/tests/request_config.rs | 39 ++ 11 files changed, 1844 insertions(+) create mode 100644 bindings/rust/libgpiod/tests/chip.rs create mode 100644 bindings/rust/libgpiod/tests/common/config.rs create mode 100644 bindings/rust/libgpiod/tests/common/mod.rs create mode 100644 bindings/rust/libgpiod/tests/edge_event.rs create mode 100644 bindings/rust/libgpiod/tests/info_event.rs create mode 100644 bindings/rust/libgpiod/tests/line_config.rs create mode 100644 bindings/rust/libgpiod/tests/line_info.rs create mode 100644 bindings/rust/libgpiod/tests/line_request.rs create mode 100644 bindings/rust/libgpiod/tests/line_settings.rs create mode 100644 bindings/rust/libgpiod/tests/request_config.rs diff --git a/bindings/rust/libgpiod/Cargo.toml b/bindings/rust/libgpiod/Cargo.toml index 65ca079b5e15..e3fb9b800030 100644 --- a/bindings/rust/libgpiod/Cargo.toml +++ b/bindings/rust/libgpiod/Cargo.toml @@ -16,3 +16,6 @@ intmap = "2.0.0" libc = "0.2.39" libgpiod-sys = { path = "../libgpiod-sys" } thiserror = "1.0" + +[dev-dependencies] +gpiosim = { path = "../gpiosim" } diff --git a/bindings/rust/libgpiod/tests/chip.rs b/bindings/rust/libgpiod/tests/chip.rs new file mode 100644 index 000000000000..16b0207d3bcd --- /dev/null +++ b/bindings/rust/libgpiod/tests/chip.rs @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +mod common; + +mod chip { + use libc::{ENODEV, ENOENT, ENOTTY}; + use std::path::PathBuf; + + use gpiosim::Sim; + use libgpiod::{chip::Chip, Error as ChipError, OperationType}; + + mod open { + use super::*; + + #[test] + fn nonexistent_file() { + assert_eq!( + Chip::open(&PathBuf::from("/dev/nonexistent")).unwrap_err(), + ChipError::OperationFailed(OperationType::ChipOpen, errno::Errno(ENOENT)) + ); + } + + #[test] + fn no_dev_file() { + assert_eq!( + Chip::open(&PathBuf::from("/tmp")).unwrap_err(), + ChipError::OperationFailed(OperationType::ChipOpen, errno::Errno(ENOTTY)) + ); + } + + #[test] + fn non_gpio_char_dev_file() { + assert_eq!( + Chip::open(&PathBuf::from("/dev/null")).unwrap_err(), + ChipError::OperationFailed(OperationType::ChipOpen, errno::Errno(ENODEV)) + ); + } + + #[test] + fn gpiosim_file() { + let sim = Sim::new(None, None, true).unwrap(); + assert!(Chip::open(&sim.dev_path()).is_ok()); + } + } + + mod verify { + use super::*; + const NGPIO: usize = 16; + const LABEL: &str = "foobar"; + + #[test] + fn basic_helpers() { + let sim = Sim::new(Some(NGPIO), Some(LABEL), true).unwrap(); + let chip = Chip::open(&sim.dev_path()).unwrap(); + let info = chip.info().unwrap(); + + assert_eq!(info.label().unwrap(), LABEL); + assert_eq!(info.name().unwrap(), sim.chip_name()); + assert_eq!(chip.path().unwrap(), sim.dev_path().to_str().unwrap()); + assert_eq!(info.num_lines(), NGPIO as usize); + } + + #[test] + fn find_line() { + let sim = Sim::new(Some(NGPIO), None, false).unwrap(); + sim.set_line_name(0, "zero").unwrap(); + sim.set_line_name(2, "two").unwrap(); + sim.set_line_name(3, "three").unwrap(); + sim.set_line_name(5, "five").unwrap(); + sim.set_line_name(10, "ten").unwrap(); + sim.set_line_name(11, "ten").unwrap(); + sim.enable().unwrap(); + + let chip = Chip::open(&sim.dev_path()).unwrap(); + + // Success case + assert_eq!(chip.line_offset_from_name("zero").unwrap(), 0); + assert_eq!(chip.line_offset_from_name("two").unwrap(), 2); + assert_eq!(chip.line_offset_from_name("three").unwrap(), 3); + assert_eq!(chip.line_offset_from_name("five").unwrap(), 5); + + // Success with duplicate names, should return first entry + assert_eq!(chip.line_offset_from_name("ten").unwrap(), 10); + + // Failure + assert_eq!( + chip.line_offset_from_name("nonexistent").unwrap_err(), + ChipError::OperationFailed( + OperationType::ChipGetLineOffsetFromName, + errno::Errno(ENOENT), + ) + ); + } + } +} diff --git a/bindings/rust/libgpiod/tests/common/config.rs b/bindings/rust/libgpiod/tests/common/config.rs new file mode 100644 index 000000000000..779fa4f366bf --- /dev/null +++ b/bindings/rust/libgpiod/tests/common/config.rs @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use std::sync::{Arc, Mutex}; +use std::time::Duration; + +use gpiosim::{Pull, Sim, Value as SimValue}; +use libgpiod::{ + chip::Chip, + line::{self, Bias, Direction, Drive, Edge, EventClock, Offset, SettingVal, Value}, + request, Result, +}; + +pub(crate) struct TestConfig { + sim: Arc>, + chip: Option, + request: Option, + rconfig: request::Config, + lconfig: line::Config, + lsettings: Option, +} + +impl TestConfig { + pub(crate) fn new(ngpio: usize) -> Result { + Ok(Self { + sim: Arc::new(Mutex::new(Sim::new(Some(ngpio), None, true)?)), + chip: None, + request: None, + rconfig: request::Config::new().unwrap(), + lconfig: line::Config::new().unwrap(), + lsettings: Some(line::Settings::new().unwrap()), + }) + } + + pub(crate) fn set_pull(&self, offsets: &[Offset], pulls: &[Pull]) { + for i in 0..pulls.len() { + self.sim + .lock() + .unwrap() + .set_pull(offsets[i], pulls[i]) + .unwrap(); + } + } + + pub(crate) fn rconfig_set_consumer(&self, consumer: &str) { + self.rconfig.set_consumer(consumer).unwrap(); + } + + pub(crate) fn lconfig_val(&mut self, dir: Option, val: Option) { + let mut settings = Vec::new(); + + if let Some(dir) = dir { + settings.push(SettingVal::Direction(dir)); + } + + if let Some(val) = val { + settings.push(SettingVal::OutputValue(val)); + } + + if !settings.is_empty() { + self.lsettings().set_prop(&settings).unwrap(); + } + } + + pub(crate) fn lconfig_bias(&mut self, dir: Direction, bias: Option) { + let settings = vec![SettingVal::Direction(dir), SettingVal::Bias(bias)]; + self.lsettings().set_prop(&settings).unwrap(); + } + + pub(crate) fn lconfig_clock(&mut self, clock: EventClock) { + let settings = vec![SettingVal::EventClock(clock)]; + self.lsettings().set_prop(&settings).unwrap(); + } + + pub(crate) fn lconfig_debounce(&mut self, duration: Duration) { + let settings = vec![ + SettingVal::Direction(Direction::Input), + SettingVal::DebouncePeriod(duration), + ]; + self.lsettings().set_prop(&settings).unwrap(); + } + + pub(crate) fn lconfig_drive(&mut self, dir: Direction, drive: Drive) { + let settings = vec![SettingVal::Direction(dir), SettingVal::Drive(drive)]; + self.lsettings().set_prop(&settings).unwrap(); + } + + pub(crate) fn lconfig_edge(&mut self, dir: Option, edge: Option) { + let mut settings = Vec::new(); + + if let Some(dir) = dir { + settings.push(SettingVal::Direction(dir)); + } + + settings.push(SettingVal::EdgeDetection(edge)); + self.lsettings().set_prop(&settings).unwrap(); + } + + pub(crate) fn lconfig_add_settings(&mut self, offsets: &[Offset]) { + self.lconfig + .add_line_settings(offsets, self.lsettings.take().unwrap()) + .unwrap() + } + + pub(crate) fn request_lines(&mut self) -> Result<()> { + let chip = Chip::open(&self.sim.lock().unwrap().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 sim_val(&self, offset: Offset) -> Result { + self.sim.lock().unwrap().val(offset) + } + + pub(crate) fn chip(&self) -> &Chip { + self.chip.as_ref().unwrap() + } + + pub(crate) fn lsettings(&mut self) -> &mut line::Settings { + self.lsettings.as_mut().unwrap() + } + + pub(crate) fn request(&self) -> &request::Request { + self.request.as_ref().unwrap() + } +} + +impl Drop for TestConfig { + fn drop(&mut self) { + // Explicit freeing is important to make sure "request" get freed + // before "sim" and "chip". + self.request = None; + } +} diff --git a/bindings/rust/libgpiod/tests/common/mod.rs b/bindings/rust/libgpiod/tests/common/mod.rs new file mode 100644 index 000000000000..b795d8d8bfb1 --- /dev/null +++ b/bindings/rust/libgpiod/tests/common/mod.rs @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +#[allow(dead_code)] +mod config; + +#[allow(unused_imports)] +pub(crate) use config::*; diff --git a/bindings/rust/libgpiod/tests/edge_event.rs b/bindings/rust/libgpiod/tests/edge_event.rs new file mode 100644 index 000000000000..327447c3acae --- /dev/null +++ b/bindings/rust/libgpiod/tests/edge_event.rs @@ -0,0 +1,298 @@ +// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +mod common; + +mod edge_event { + use std::time::Duration; + + use crate::common::*; + use gpiosim::{Pull, Sim}; + use libgpiod::{ + line::{Edge, EdgeKind, Offset}, + request, + }; + + const NGPIO: usize = 8; + + mod buffer_capacity { + use super::*; + + #[test] + fn default_capacity() { + assert_eq!(request::Buffer::new(0).unwrap().capacity(), 64); + } + + #[test] + fn user_defined_capacity() { + assert_eq!(request::Buffer::new(123).unwrap().capacity(), 123); + } + + #[test] + fn max_capacity() { + assert_eq!(request::Buffer::new(1024 * 2).unwrap().capacity(), 1024); + } + } + + mod trigger { + use super::*; + use std::{ + sync::{Arc, Mutex}, + thread, + }; + + // Helpers to generate events + fn trigger_falling_and_rising_edge(sim: Arc>, offset: Offset) { + thread::spawn(move || { + thread::sleep(Duration::from_millis(30)); + sim.lock().unwrap().set_pull(offset, Pull::Up).unwrap(); + + thread::sleep(Duration::from_millis(30)); + sim.lock().unwrap().set_pull(offset, Pull::Down).unwrap(); + }); + } + + fn trigger_rising_edge_events_on_two_offsets(sim: Arc>, offset: [Offset; 2]) { + thread::spawn(move || { + thread::sleep(Duration::from_millis(30)); + sim.lock().unwrap().set_pull(offset[0], Pull::Up).unwrap(); + + thread::sleep(Duration::from_millis(30)); + sim.lock().unwrap().set_pull(offset[1], Pull::Up).unwrap(); + }); + } + + fn trigger_multiple_events(sim: Arc>, offset: Offset) { + sim.lock().unwrap().set_pull(offset, Pull::Up).unwrap(); + thread::sleep(Duration::from_millis(10)); + + sim.lock().unwrap().set_pull(offset, Pull::Down).unwrap(); + thread::sleep(Duration::from_millis(10)); + + sim.lock().unwrap().set_pull(offset, Pull::Up).unwrap(); + thread::sleep(Duration::from_millis(10)); + } + + #[test] + fn both_edges() { + const GPIO: Offset = 2; + let mut buf = request::Buffer::new(0).unwrap(); + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_edge(None, Some(Edge::Both)); + config.lconfig_add_settings(&[GPIO]); + config.request_lines().unwrap(); + + // Generate events + trigger_falling_and_rising_edge(config.sim(), GPIO); + + // Rising event + assert!(config + .request() + .wait_edge_event(Some(Duration::from_secs(1))) + .unwrap()); + + let mut events = config.request().read_edge_events(&mut buf).unwrap(); + assert_eq!(events.len(), 1); + + let event = events.next().unwrap().unwrap(); + let ts_rising = event.timestamp(); + assert_eq!(event.event_type().unwrap(), EdgeKind::Rising); + assert_eq!(event.line_offset(), GPIO); + + // Falling event + assert!(config + .request() + .wait_edge_event(Some(Duration::from_secs(1))) + .unwrap()); + + let mut events = config.request().read_edge_events(&mut buf).unwrap(); + assert_eq!(events.len(), 1); + + let event = events.next().unwrap().unwrap(); + let ts_falling = event.timestamp(); + assert_eq!(event.event_type().unwrap(), EdgeKind::Falling); + assert_eq!(event.line_offset(), GPIO); + + // No events available + assert!(!config + .request() + .wait_edge_event(Some(Duration::from_millis(100))) + .unwrap()); + + assert!(ts_falling > ts_rising); + } + + #[test] + fn rising_edge() { + const GPIO: Offset = 6; + let mut buf = request::Buffer::new(0).unwrap(); + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_edge(None, Some(Edge::Rising)); + config.lconfig_add_settings(&[GPIO]); + config.request_lines().unwrap(); + + // Generate events + trigger_falling_and_rising_edge(config.sim(), GPIO); + + // Rising event + assert!(config + .request() + .wait_edge_event(Some(Duration::from_secs(1))) + .unwrap()); + + let mut events = config.request().read_edge_events(&mut buf).unwrap(); + assert_eq!(events.len(), 1); + + let event = events.next().unwrap().unwrap(); + assert_eq!(event.event_type().unwrap(), EdgeKind::Rising); + assert_eq!(event.line_offset(), GPIO); + + // No events available + assert!(!config + .request() + .wait_edge_event(Some(Duration::from_millis(100))) + .unwrap()); + } + + #[test] + fn falling_edge() { + const GPIO: Offset = 7; + let mut buf = request::Buffer::new(0).unwrap(); + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_edge(None, Some(Edge::Falling)); + config.lconfig_add_settings(&[GPIO]); + config.request_lines().unwrap(); + + // Generate events + trigger_falling_and_rising_edge(config.sim(), GPIO); + + // Falling event + assert!(config + .request() + .wait_edge_event(Some(Duration::from_secs(1))) + .unwrap()); + + let mut events = config.request().read_edge_events(&mut buf).unwrap(); + assert_eq!(events.len(), 1); + + let event = events.next().unwrap().unwrap(); + assert_eq!(event.event_type().unwrap(), EdgeKind::Falling); + assert_eq!(event.line_offset(), GPIO); + + // No events available + assert!(!config + .request() + .wait_edge_event(Some(Duration::from_millis(100))) + .unwrap()); + } + + #[test] + fn edge_sequence() { + const GPIO: [u32; 2] = [0, 1]; + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_edge(None, Some(Edge::Both)); + config.lconfig_add_settings(&GPIO); + config.request_lines().unwrap(); + + // Generate events + trigger_rising_edge_events_on_two_offsets(config.sim(), GPIO); + + // Rising event GPIO 0 + let mut buf = request::Buffer::new(0).unwrap(); + assert!(config + .request() + .wait_edge_event(Some(Duration::from_secs(1))) + .unwrap()); + + let mut events = config.request().read_edge_events(&mut buf).unwrap(); + assert_eq!(events.len(), 1); + + let event = events.next().unwrap().unwrap(); + assert_eq!(event.event_type().unwrap(), EdgeKind::Rising); + assert_eq!(event.line_offset(), GPIO[0]); + assert_eq!(event.global_seqno(), 1); + assert_eq!(event.line_seqno(), 1); + + // Rising event GPIO 1 + assert!(config + .request() + .wait_edge_event(Some(Duration::from_secs(1))) + .unwrap()); + + let mut events = config.request().read_edge_events(&mut buf).unwrap(); + assert_eq!(events.len(), 1); + + let event = events.next().unwrap().unwrap(); + assert_eq!(event.event_type().unwrap(), EdgeKind::Rising); + assert_eq!(event.line_offset(), GPIO[1]); + assert_eq!(event.global_seqno(), 2); + assert_eq!(event.line_seqno(), 1); + + // No events available + assert!(!config + .request() + .wait_edge_event(Some(Duration::from_millis(100))) + .unwrap()); + } + + #[test] + fn multiple_events() { + const GPIO: Offset = 1; + let mut buf = request::Buffer::new(0).unwrap(); + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_edge(None, Some(Edge::Both)); + config.lconfig_add_settings(&[GPIO]); + config.request_lines().unwrap(); + + // Generate events + trigger_multiple_events(config.sim(), GPIO); + + // Read multiple events + assert!(config + .request() + .wait_edge_event(Some(Duration::from_secs(1))) + .unwrap()); + + let events = config.request().read_edge_events(&mut buf).unwrap(); + assert_eq!(events.len(), 3); + + let mut global_seqno = 1; + let mut line_seqno = 1; + + // Verify sequence number of events + for event in events { + let event = event.unwrap(); + assert_eq!(event.line_offset(), GPIO); + assert_eq!(event.global_seqno(), global_seqno); + assert_eq!(event.line_seqno(), line_seqno); + + global_seqno += 1; + line_seqno += 1; + } + } + + #[test] + fn over_capacity() { + const GPIO: Offset = 2; + let mut buf = request::Buffer::new(2).unwrap(); + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_edge(None, Some(Edge::Both)); + config.lconfig_add_settings(&[GPIO]); + config.request_lines().unwrap(); + + // Generate events + trigger_multiple_events(config.sim(), GPIO); + + // Read multiple events + assert!(config + .request() + .wait_edge_event(Some(Duration::from_secs(1))) + .unwrap()); + + let events = config.request().read_edge_events(&mut buf).unwrap(); + assert_eq!(events.len(), 2); + } + } +} diff --git a/bindings/rust/libgpiod/tests/info_event.rs b/bindings/rust/libgpiod/tests/info_event.rs new file mode 100644 index 000000000000..df461f21ec1c --- /dev/null +++ b/bindings/rust/libgpiod/tests/info_event.rs @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +mod common; + +mod info_event { + use libc::EINVAL; + use std::{ + sync::{ + mpsc::{self, Receiver, Sender}, + Arc, Mutex, + }, + thread, + time::Duration, + }; + + use gpiosim::Sim; + use libgpiod::{ + chip::Chip, + line::{self, Direction, InfoChangeKind, Offset}, + request, Error as ChipError, OperationType, + }; + + fn request_reconfigure_line(chip: Arc>, tx: Sender<()>, rx: Receiver<()>) { + thread::spawn(move || { + let lconfig1 = line::Config::new().unwrap(); + let lsettings = line::Settings::new().unwrap(); + lconfig1.add_line_settings(&[7], lsettings).unwrap(); + let rconfig = request::Config::new().unwrap(); + + let request = chip + .lock() + .unwrap() + .request_lines(&rconfig, &lconfig1) + .unwrap(); + + // Signal the parent to continue + tx.send(()).expect("Could not send signal on channel"); + + // Wait for parent to signal + rx.recv().expect("Could not receive from channel"); + + let lconfig2 = line::Config::new().unwrap(); + let mut lsettings = line::Settings::new().unwrap(); + lsettings.set_direction(Direction::Output).unwrap(); + lconfig2.add_line_settings(&[7], lsettings).unwrap(); + + request.reconfigure_lines(&lconfig2).unwrap(); + + // Signal the parent to continue + tx.send(()).expect("Could not send signal on channel"); + + // Wait for parent to signal + rx.recv().expect("Could not receive from channel"); + }); + } + + mod watch { + use super::*; + const NGPIO: usize = 8; + const GPIO: Offset = 7; + + #[test] + fn line_info() { + let sim = Sim::new(Some(NGPIO), None, true).unwrap(); + let chip = Chip::open(&sim.dev_path()).unwrap(); + + assert_eq!( + chip.watch_line_info(NGPIO as u32).unwrap_err(), + ChipError::OperationFailed(OperationType::ChipWatchLineInfo, errno::Errno(EINVAL)) + ); + + let info = chip.watch_line_info(GPIO).unwrap(); + assert_eq!(info.offset(), GPIO); + + // No events available + assert!(!chip + .wait_info_event(Some(Duration::from_millis(100))) + .unwrap()); + } + + #[test] + fn reconfigure() { + let sim = Sim::new(Some(NGPIO), None, true).unwrap(); + let chip = Arc::new(Mutex::new(Chip::open(&sim.dev_path()).unwrap())); + let info = chip.lock().unwrap().watch_line_info(GPIO).unwrap(); + + assert_eq!(info.direction().unwrap(), Direction::Input); + + // Thread synchronizing mechanism + let (tx_main, rx_thread) = mpsc::channel(); + let (tx_thread, rx_main) = mpsc::channel(); + + // Generate events + request_reconfigure_line(chip.clone(), tx_thread, rx_thread); + + // Wait for thread to signal + rx_main.recv().expect("Could not receive from channel"); + + // Line requested event + assert!(chip + .lock() + .unwrap() + .wait_info_event(Some(Duration::from_secs(1))) + .unwrap()); + let event = chip.lock().unwrap().read_info_event().unwrap(); + let ts_req = event.timestamp(); + + assert_eq!(event.event_type().unwrap(), InfoChangeKind::LineRequested); + assert_eq!( + event.line_info().unwrap().direction().unwrap(), + Direction::Input + ); + + // Signal the thread to continue + tx_main.send(()).expect("Could not send signal on channel"); + + // Wait for thread to signal + rx_main.recv().expect("Could not receive from channel"); + + // Line changed event + assert!(chip + .lock() + .unwrap() + .wait_info_event(Some(Duration::from_secs(1))) + .unwrap()); + let event = chip.lock().unwrap().read_info_event().unwrap(); + let ts_rec = event.timestamp(); + + assert_eq!( + event.event_type().unwrap(), + InfoChangeKind::LineConfigChanged + ); + assert_eq!( + event.line_info().unwrap().direction().unwrap(), + Direction::Output + ); + + // Signal the thread to continue + tx_main.send(()).expect("Could not send signal on channel"); + + // Line released event + assert!(chip + .lock() + .unwrap() + .wait_info_event(Some(Duration::from_secs(1))) + .unwrap()); + let event = chip.lock().unwrap().read_info_event().unwrap(); + let ts_rel = event.timestamp(); + + assert_eq!(event.event_type().unwrap(), InfoChangeKind::LineReleased); + + // No events available + assert!(!chip + .lock() + .unwrap() + .wait_info_event(Some(Duration::from_millis(100))) + .unwrap()); + + // Check timestamps are really monotonic. + assert!(ts_rel > ts_rec); + assert!(ts_rec > ts_req); + } + } +} diff --git a/bindings/rust/libgpiod/tests/line_config.rs b/bindings/rust/libgpiod/tests/line_config.rs new file mode 100644 index 000000000000..d0dc4ceb67c4 --- /dev/null +++ b/bindings/rust/libgpiod/tests/line_config.rs @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +mod common; + +mod line_config { + use libgpiod::line::{ + self, Bias, Direction, Drive, Edge, EventClock, SettingKind, SettingVal, Value, + }; + + #[test] + fn settings() { + let mut lsettings1 = line::Settings::new().unwrap(); + lsettings1 + .set_direction(Direction::Input) + .unwrap() + .set_edge_detection(Some(Edge::Both)) + .unwrap() + .set_bias(Some(Bias::PullDown)) + .unwrap() + .set_drive(Drive::PushPull) + .unwrap(); + + let mut lsettings2 = line::Settings::new().unwrap(); + lsettings2 + .set_direction(Direction::Output) + .unwrap() + .set_active_low(true) + .set_event_clock(EventClock::Realtime) + .unwrap() + .set_output_value(Value::Active) + .unwrap(); + + // Add settings for multiple lines + let lconfig = line::Config::new().unwrap(); + lconfig.add_line_settings(&[0, 1, 2], lsettings1).unwrap(); + lconfig.add_line_settings(&[4, 5], lsettings2).unwrap(); + + // Retrieve settings + let lsettings = lconfig.line_settings(1).unwrap(); + assert_eq!( + lsettings.prop(SettingKind::Direction).unwrap(), + SettingVal::Direction(Direction::Input) + ); + assert_eq!( + lsettings.prop(SettingKind::EdgeDetection).unwrap(), + SettingVal::EdgeDetection(Some(Edge::Both)) + ); + assert_eq!( + lsettings.prop(SettingKind::Bias).unwrap(), + SettingVal::Bias(Some(Bias::PullDown)) + ); + assert_eq!( + lsettings.prop(SettingKind::Drive).unwrap(), + SettingVal::Drive(Drive::PushPull) + ); + + let lsettings = lconfig.line_settings(5).unwrap(); + assert_eq!( + lsettings.prop(SettingKind::Direction).unwrap(), + SettingVal::Direction(Direction::Output) + ); + assert_eq!( + lsettings.prop(SettingKind::ActiveLow).unwrap(), + SettingVal::ActiveLow(true) + ); + assert_eq!( + lsettings.prop(SettingKind::EventClock).unwrap(), + SettingVal::EventClock(EventClock::Realtime) + ); + assert_eq!( + lsettings.prop(SettingKind::OutputValue).unwrap(), + SettingVal::OutputValue(Value::Active) + ); + } + + #[test] + fn offsets() { + let mut lsettings1 = line::Settings::new().unwrap(); + lsettings1.set_direction(Direction::Input).unwrap(); + + let mut lsettings2 = line::Settings::new().unwrap(); + lsettings2.set_event_clock(EventClock::Realtime).unwrap(); + + // Add settings for multiple lines + let lconfig = line::Config::new().unwrap(); + lconfig.add_line_settings(&[0, 1, 2], lsettings1).unwrap(); + lconfig.add_line_settings(&[4, 5], lsettings2).unwrap(); + + // Verify offsets + let offsets = lconfig.offsets().unwrap(); + assert_eq!(offsets, [0, 1, 2, 4, 5]); + } +} diff --git a/bindings/rust/libgpiod/tests/line_info.rs b/bindings/rust/libgpiod/tests/line_info.rs new file mode 100644 index 000000000000..f1ec1d59899c --- /dev/null +++ b/bindings/rust/libgpiod/tests/line_info.rs @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: Apache-2.0 OR 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 crate::common::*; + use gpiosim::{Direction as SimDirection, Sim}; + use libgpiod::{ + chip::Chip, + line::{Bias, Direction, Drive, Edge, EventClock}, + Error as ChipError, OperationType, + }; + + const NGPIO: usize = 8; + + mod properties { + use super::*; + + #[test] + fn default() { + let sim = Sim::new(Some(NGPIO), None, false).unwrap(); + sim.set_line_name(4, "four").unwrap(); + sim.hog_line(4, "hog4", SimDirection::OutputLow).unwrap(); + sim.enable().unwrap(); + + let chip = Chip::open(&sim.dev_path()).unwrap(); + + let info4 = chip.line_info(4).unwrap(); + assert_eq!(info4.offset(), 4); + assert_eq!(info4.name().unwrap(), "four"); + assert!(info4.is_used()); + assert_eq!(info4.consumer().unwrap(), "hog4"); + assert_eq!(info4.direction().unwrap(), Direction::Output); + assert!(!info4.is_active_low()); + assert_eq!(info4.bias().unwrap(), None); + assert_eq!(info4.drive().unwrap(), Drive::PushPull); + assert_eq!(info4.edge_detection().unwrap(), None); + assert_eq!(info4.event_clock().unwrap(), EventClock::Monotonic); + assert!(!info4.is_debounced()); + assert_eq!(info4.debounce_period(), Duration::from_millis(0)); + + assert_eq!( + chip.line_info(NGPIO as u32).unwrap_err(), + ChipError::OperationFailed(OperationType::ChipGetLineInfo, errno::Errno(EINVAL)) + ); + } + + #[test] + fn name_and_offset() { + let sim = Sim::new(Some(NGPIO), None, false).unwrap(); + + // Line 0 has no name + for i in 1..NGPIO { + sim.set_line_name(i as u32, &i.to_string()).unwrap(); + } + sim.enable().unwrap(); + + let chip = Chip::open(&sim.dev_path()).unwrap(); + let info = chip.line_info(0).unwrap(); + + assert_eq!(info.offset(), 0); + assert_eq!( + info.name().unwrap_err(), + ChipError::NullString("GPIO line's name") + ); + + for i in 1..NGPIO { + let info = chip.line_info(i as u32).unwrap(); + + assert_eq!(info.offset(), i as u32); + assert_eq!(info.name().unwrap(), &i.to_string()); + } + } + + #[test] + fn is_used() { + let sim = Sim::new(Some(NGPIO), None, false).unwrap(); + sim.hog_line(0, "hog", SimDirection::OutputHigh).unwrap(); + sim.enable().unwrap(); + + let chip = Chip::open(&sim.dev_path()).unwrap(); + + let info = chip.line_info(0).unwrap(); + assert!(info.is_used()); + + let info = chip.line_info(1).unwrap(); + assert!(!info.is_used()); + } + + #[test] + fn consumer() { + let sim = Sim::new(Some(NGPIO), None, false).unwrap(); + sim.hog_line(0, "hog", SimDirection::OutputHigh).unwrap(); + sim.enable().unwrap(); + + let chip = Chip::open(&sim.dev_path()).unwrap(); + + let info = chip.line_info(0).unwrap(); + assert_eq!(info.consumer().unwrap(), "hog"); + + let info = chip.line_info(1).unwrap(); + assert_eq!( + info.consumer().unwrap_err(), + ChipError::NullString("GPIO line's consumer name") + ); + } + + #[test] + fn direction() { + let sim = Sim::new(Some(NGPIO), None, false).unwrap(); + sim.hog_line(0, "hog", SimDirection::Input).unwrap(); + sim.hog_line(1, "hog", SimDirection::OutputHigh).unwrap(); + sim.hog_line(2, "hog", SimDirection::OutputLow).unwrap(); + sim.enable().unwrap(); + + let chip = Chip::open(&sim.dev_path()).unwrap(); + + let info = chip.line_info(0).unwrap(); + assert_eq!(info.direction().unwrap(), Direction::Input); + + let info = chip.line_info(1).unwrap(); + assert_eq!(info.direction().unwrap(), Direction::Output); + + let info = chip.line_info(2).unwrap(); + assert_eq!(info.direction().unwrap(), Direction::Output); + } + + #[test] + fn bias() { + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.bias().unwrap(), None); + + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_bias(Direction::Input, Some(Bias::PullUp)); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.bias().unwrap(), Some(Bias::PullUp)); + + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_bias(Direction::Input, Some(Bias::PullDown)); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.bias().unwrap(), Some(Bias::PullDown)); + + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_bias(Direction::Input, Some(Bias::Disabled)); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.bias().unwrap(), Some(Bias::Disabled)); + } + + #[test] + fn drive() { + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.drive().unwrap(), Drive::PushPull); + + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_drive(Direction::Input, Drive::PushPull); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.drive().unwrap(), Drive::PushPull); + + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_drive(Direction::Output, Drive::OpenDrain); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.drive().unwrap(), Drive::OpenDrain); + + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_drive(Direction::Output, Drive::OpenSource); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.drive().unwrap(), Drive::OpenSource); + } + + #[test] + fn edge() { + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.edge_detection().unwrap(), None); + + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_edge(Some(Direction::Input), Some(Edge::Both)); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.edge_detection().unwrap(), Some(Edge::Both)); + + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_edge(Some(Direction::Input), Some(Edge::Rising)); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.edge_detection().unwrap(), Some(Edge::Rising)); + + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_edge(Some(Direction::Input), Some(Edge::Falling)); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.edge_detection().unwrap(), Some(Edge::Falling)); + } + + #[test] + fn event_clock() { + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.event_clock().unwrap(), EventClock::Monotonic); + + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_clock(EventClock::Monotonic); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.event_clock().unwrap(), EventClock::Monotonic); + + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_clock(EventClock::Realtime); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.event_clock().unwrap(), EventClock::Realtime); + } + + #[test] + #[ignore] + fn event_clock_hte() { + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_clock(EventClock::HTE); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.event_clock().unwrap(), EventClock::HTE); + } + + #[test] + fn debounce() { + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert!(!info.is_debounced()); + assert_eq!(info.debounce_period(), Duration::from_millis(0)); + + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_debounce(Duration::from_millis(100)); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert!(info.is_debounced()); + assert_eq!(info.debounce_period(), Duration::from_millis(100)); + } + } +} diff --git a/bindings/rust/libgpiod/tests/line_request.rs b/bindings/rust/libgpiod/tests/line_request.rs new file mode 100644 index 000000000000..0f83a9df94f1 --- /dev/null +++ b/bindings/rust/libgpiod/tests/line_request.rs @@ -0,0 +1,510 @@ +// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +mod common; + +mod line_request { + use libc::{E2BIG, EINVAL}; + use std::time::Duration; + + use crate::common::*; + use gpiosim::{Pull, Value as SimValue}; + use libgpiod::{ + line::{ + self, Bias, Direction, Drive, Edge, EventClock, Offset, SettingVal, Value, ValueMap, + }, + Error as ChipError, OperationType, + }; + + const NGPIO: usize = 8; + + mod invalid_arguments { + use super::*; + + #[test] + fn no_offsets() { + let mut config = TestConfig::new(NGPIO).unwrap(); + + assert_eq!( + config.request_lines().unwrap_err(), + ChipError::OperationFailed(OperationType::ChipRequestLines, errno::Errno(EINVAL)) + ); + } + + #[test] + fn out_of_bound_offsets() { + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_add_settings(&[2, 0, 8, 4]); + + assert_eq!( + config.request_lines().unwrap_err(), + ChipError::OperationFailed(OperationType::ChipRequestLines, errno::Errno(EINVAL)) + ); + } + + #[test] + fn dir_out_edge_failure() { + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_edge(Some(Direction::Output), Some(Edge::Both)); + config.lconfig_add_settings(&[0]); + + assert_eq!( + config.request_lines().unwrap_err(), + ChipError::OperationFailed(OperationType::ChipRequestLines, errno::Errno(EINVAL)) + ); + } + } + + mod verify { + use super::*; + + #[test] + fn custom_consumer() { + const GPIO: Offset = 2; + const CONSUMER: &str = "foobar"; + let mut config = TestConfig::new(NGPIO).unwrap(); + config.rconfig_set_consumer(CONSUMER); + config.lconfig_add_settings(&[GPIO]); + config.request_lines().unwrap(); + + let info = config.chip().line_info(GPIO).unwrap(); + + assert!(info.is_used()); + assert_eq!(info.consumer().unwrap(), CONSUMER); + } + + #[test] + fn empty_consumer() { + const GPIO: Offset = 2; + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_add_settings(&[GPIO]); + config.request_lines().unwrap(); + + let info = config.chip().line_info(GPIO).unwrap(); + + assert!(info.is_used()); + assert_eq!(info.consumer().unwrap(), "?"); + } + + #[test] + fn read_values() { + let offsets = [7, 1, 0, 6, 2]; + let pulls = [Pull::Up, Pull::Up, Pull::Down, Pull::Up, Pull::Down]; + let mut config = TestConfig::new(NGPIO).unwrap(); + config.set_pull(&offsets, &pulls); + config.lconfig_val(Some(Direction::Input), None); + config.lconfig_add_settings(&offsets); + config.request_lines().unwrap(); + + let request = config.request(); + + // Single values read properly + assert_eq!(request.value(7).unwrap(), Value::Active); + + // Values read properly + let map = request.values().unwrap(); + for i in 0..offsets.len() { + assert_eq!( + *map.get(offsets[i].into()).unwrap(), + match pulls[i] { + Pull::Down => Value::InActive, + _ => Value::Active, + } + ); + } + + // Subset of values read properly + let map = request.values_subset(&[2, 0, 6]).unwrap(); + assert_eq!(*map.get(2).unwrap(), Value::InActive); + assert_eq!(*map.get(0).unwrap(), Value::InActive); + assert_eq!(*map.get(6).unwrap(), Value::Active); + + // Value read properly after reconfigure + let mut lsettings = line::Settings::new().unwrap(); + lsettings.set_active_low(true); + let lconfig = line::Config::new().unwrap(); + lconfig.add_line_settings(&offsets, lsettings).unwrap(); + request.reconfigure_lines(&lconfig).unwrap(); + assert_eq!(request.value(7).unwrap(), Value::InActive); + } + + #[test] + fn set_output_values() { + let offsets = [0, 1, 3, 4]; + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_val(Some(Direction::Output), Some(Value::Active)); + config.lconfig_add_settings(&offsets); + config.request_lines().unwrap(); + + assert_eq!(config.sim_val(0).unwrap(), SimValue::Active); + assert_eq!(config.sim_val(1).unwrap(), SimValue::Active); + assert_eq!(config.sim_val(3).unwrap(), SimValue::Active); + assert_eq!(config.sim_val(4).unwrap(), SimValue::Active); + + // Default + assert_eq!(config.sim_val(2).unwrap(), SimValue::InActive); + } + + #[test] + fn update_output_values() { + let offsets = [0, 1, 3, 4]; + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_val(Some(Direction::Output), Some(Value::InActive)); + config.lconfig_add_settings(&offsets); + config.request_lines().unwrap(); + let request = config.request(); + + // Set single value + request.set_value(1, Value::Active).unwrap(); + assert_eq!(config.sim_val(0).unwrap(), SimValue::InActive); + assert_eq!(config.sim_val(1).unwrap(), SimValue::Active); + assert_eq!(config.sim_val(3).unwrap(), SimValue::InActive); + assert_eq!(config.sim_val(4).unwrap(), SimValue::InActive); + request.set_value(1, Value::InActive).unwrap(); + assert_eq!(config.sim_val(1).unwrap(), SimValue::InActive); + + // Set values of subset + let mut map = ValueMap::new(); + map.insert(4, Value::Active); + map.insert(3, Value::Active); + request.set_values_subset(map).unwrap(); + assert_eq!(config.sim_val(0).unwrap(), SimValue::InActive); + assert_eq!(config.sim_val(1).unwrap(), SimValue::InActive); + assert_eq!(config.sim_val(3).unwrap(), SimValue::Active); + assert_eq!(config.sim_val(4).unwrap(), SimValue::Active); + + let mut map = ValueMap::new(); + map.insert(4, Value::InActive); + map.insert(3, Value::InActive); + request.set_values_subset(map).unwrap(); + assert_eq!(config.sim_val(3).unwrap(), SimValue::InActive); + assert_eq!(config.sim_val(4).unwrap(), SimValue::InActive); + + // Set all values + request + .set_values(&[ + Value::Active, + Value::InActive, + Value::Active, + Value::InActive, + ]) + .unwrap(); + assert_eq!(config.sim_val(0).unwrap(), SimValue::Active); + assert_eq!(config.sim_val(1).unwrap(), SimValue::InActive); + assert_eq!(config.sim_val(3).unwrap(), SimValue::Active); + assert_eq!(config.sim_val(4).unwrap(), SimValue::InActive); + request + .set_values(&[ + Value::InActive, + Value::InActive, + Value::InActive, + Value::InActive, + ]) + .unwrap(); + assert_eq!(config.sim_val(0).unwrap(), SimValue::InActive); + assert_eq!(config.sim_val(1).unwrap(), SimValue::InActive); + assert_eq!(config.sim_val(3).unwrap(), SimValue::InActive); + assert_eq!(config.sim_val(4).unwrap(), SimValue::InActive); + } + + #[test] + fn set_bias() { + let offsets = [3]; + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_bias(Direction::Input, Some(Bias::PullUp)); + config.lconfig_add_settings(&offsets); + config.request_lines().unwrap(); + config.request(); + + // Set single value + assert_eq!(config.sim_val(3).unwrap(), SimValue::Active); + } + + #[test] + fn no_events() { + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_edge(None, Some(Edge::Both)); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + + // No events available + assert!(!config + .request() + .wait_edge_event(Some(Duration::from_millis(100))) + .unwrap()); + } + } + + mod reconfigure { + use super::*; + + #[test] + fn e2big() { + let offsets = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + let mut config = TestConfig::new(16).unwrap(); + config.lconfig_add_settings(&offsets); + config.request_lines().unwrap(); + + let request = config.request(); + + // Reconfigure + let mut lsettings = line::Settings::new().unwrap(); + lsettings.set_direction(Direction::Input).unwrap(); + let lconfig = line::Config::new().unwrap(); + + // The uAPI config has only 10 attribute slots, this should pass. + for offset in offsets { + lsettings.set_debounce_period(Duration::from_millis((100 + offset).into())); + lconfig + .add_line_settings(&[offset as Offset], lsettings.settings_clone().unwrap()) + .unwrap(); + } + + assert!(request.reconfigure_lines(&lconfig).is_ok()); + + // The uAPI config has only 10 attribute slots, and this is the 11th entry. + // This should fail with E2BIG. + lsettings.set_debounce_period(Duration::from_millis(100 + 11)); + lconfig.add_line_settings(&[11], lsettings).unwrap(); + + assert_eq!( + request.reconfigure_lines(&lconfig).unwrap_err(), + ChipError::OperationFailed( + OperationType::LineRequestReconfigLines, + errno::Errno(E2BIG), + ) + ); + } + + #[test] + fn bias() { + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.bias().unwrap(), None); + + let request = config.request(); + + // Reconfigure + let lconfig = line::Config::new().unwrap(); + let mut lsettings = line::Settings::new().unwrap(); + lsettings + .set_prop(&[ + SettingVal::Direction(Direction::Input), + SettingVal::Bias(Some(Bias::PullUp)), + ]) + .unwrap(); + lconfig.add_line_settings(&[0], lsettings).unwrap(); + request.reconfigure_lines(&lconfig).unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.bias().unwrap(), Some(Bias::PullUp)); + + let lconfig = line::Config::new().unwrap(); + let mut lsettings = line::Settings::new().unwrap(); + lsettings + .set_prop(&[ + SettingVal::Direction(Direction::Input), + SettingVal::Bias(Some(Bias::PullDown)), + ]) + .unwrap(); + lconfig.add_line_settings(&[0], lsettings).unwrap(); + request.reconfigure_lines(&lconfig).unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.bias().unwrap(), Some(Bias::PullDown)); + + let lconfig = line::Config::new().unwrap(); + let mut lsettings = line::Settings::new().unwrap(); + lsettings + .set_prop(&[ + SettingVal::Direction(Direction::Input), + SettingVal::Bias(Some(Bias::Disabled)), + ]) + .unwrap(); + lconfig.add_line_settings(&[0], lsettings).unwrap(); + request.reconfigure_lines(&lconfig).unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.bias().unwrap(), Some(Bias::Disabled)); + } + + #[test] + fn drive() { + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.drive().unwrap(), Drive::PushPull); + + let request = config.request(); + + // Reconfigure + let lconfig = line::Config::new().unwrap(); + let mut lsettings = line::Settings::new().unwrap(); + lsettings + .set_prop(&[ + SettingVal::Direction(Direction::Input), + SettingVal::Drive(Drive::PushPull), + ]) + .unwrap(); + lconfig.add_line_settings(&[0], lsettings).unwrap(); + request.reconfigure_lines(&lconfig).unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.drive().unwrap(), Drive::PushPull); + + let lconfig = line::Config::new().unwrap(); + let mut lsettings = line::Settings::new().unwrap(); + lsettings + .set_prop(&[ + SettingVal::Direction(Direction::Output), + SettingVal::Drive(Drive::OpenDrain), + ]) + .unwrap(); + lconfig.add_line_settings(&[0], lsettings).unwrap(); + request.reconfigure_lines(&lconfig).unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.drive().unwrap(), Drive::OpenDrain); + + let lconfig = line::Config::new().unwrap(); + let mut lsettings = line::Settings::new().unwrap(); + lsettings + .set_prop(&[ + SettingVal::Direction(Direction::Output), + SettingVal::Drive(Drive::OpenSource), + ]) + .unwrap(); + lconfig.add_line_settings(&[0], lsettings).unwrap(); + request.reconfigure_lines(&lconfig).unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.drive().unwrap(), Drive::OpenSource); + } + + #[test] + fn edge() { + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.edge_detection().unwrap(), None); + + let request = config.request(); + + // Reconfigure + let lconfig = line::Config::new().unwrap(); + let mut lsettings = line::Settings::new().unwrap(); + lsettings + .set_prop(&[ + SettingVal::Direction(Direction::Input), + SettingVal::EdgeDetection(Some(Edge::Both)), + ]) + .unwrap(); + lconfig.add_line_settings(&[0], lsettings).unwrap(); + request.reconfigure_lines(&lconfig).unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.edge_detection().unwrap(), Some(Edge::Both)); + + let lconfig = line::Config::new().unwrap(); + let mut lsettings = line::Settings::new().unwrap(); + lsettings + .set_prop(&[ + SettingVal::Direction(Direction::Input), + SettingVal::EdgeDetection(Some(Edge::Rising)), + ]) + .unwrap(); + lconfig.add_line_settings(&[0], lsettings).unwrap(); + request.reconfigure_lines(&lconfig).unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.edge_detection().unwrap(), Some(Edge::Rising)); + + let lconfig = line::Config::new().unwrap(); + let mut lsettings = line::Settings::new().unwrap(); + lsettings + .set_prop(&[ + SettingVal::Direction(Direction::Input), + SettingVal::EdgeDetection(Some(Edge::Falling)), + ]) + .unwrap(); + lconfig.add_line_settings(&[0], lsettings).unwrap(); + request.reconfigure_lines(&lconfig).unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.edge_detection().unwrap(), Some(Edge::Falling)); + } + + #[test] + fn event_clock() { + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.event_clock().unwrap(), EventClock::Monotonic); + + let request = config.request(); + + // Reconfigure + let lconfig = line::Config::new().unwrap(); + let mut lsettings = line::Settings::new().unwrap(); + lsettings.set_event_clock(EventClock::Monotonic).unwrap(); + lconfig.add_line_settings(&[0], lsettings).unwrap(); + request.reconfigure_lines(&lconfig).unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.event_clock().unwrap(), EventClock::Monotonic); + + let lconfig = line::Config::new().unwrap(); + let mut lsettings = line::Settings::new().unwrap(); + lsettings.set_event_clock(EventClock::Realtime).unwrap(); + lconfig.add_line_settings(&[0], lsettings).unwrap(); + request.reconfigure_lines(&lconfig).unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.event_clock().unwrap(), EventClock::Realtime); + } + + #[test] + #[ignore] + fn event_clock_hte() { + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.event_clock().unwrap(), EventClock::Monotonic); + + let request = config.request(); + + // Reconfigure + let lconfig = line::Config::new().unwrap(); + let mut lsettings = line::Settings::new().unwrap(); + lsettings.set_event_clock(EventClock::HTE).unwrap(); + lconfig.add_line_settings(&[0], lsettings).unwrap(); + request.reconfigure_lines(&lconfig).unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.event_clock().unwrap(), EventClock::HTE); + } + + #[test] + fn debounce() { + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert!(!info.is_debounced()); + assert_eq!(info.debounce_period(), Duration::from_millis(0)); + + let request = config.request(); + + // Reconfigure + let lconfig = line::Config::new().unwrap(); + let mut lsettings = line::Settings::new().unwrap(); + lsettings + .set_prop(&[ + SettingVal::Direction(Direction::Input), + SettingVal::DebouncePeriod(Duration::from_millis(100)), + ]) + .unwrap(); + lconfig.add_line_settings(&[0], lsettings).unwrap(); + request.reconfigure_lines(&lconfig).unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert!(info.is_debounced()); + assert_eq!(info.debounce_period(), Duration::from_millis(100)); + } + } +} diff --git a/bindings/rust/libgpiod/tests/line_settings.rs b/bindings/rust/libgpiod/tests/line_settings.rs new file mode 100644 index 000000000000..2a9de15ed283 --- /dev/null +++ b/bindings/rust/libgpiod/tests/line_settings.rs @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +mod common; + +mod line_settings { + use std::time::Duration; + + use libgpiod::line::{ + self, Bias, Direction, Drive, Edge, EventClock, SettingKind, SettingVal, Value, + }; + + #[test] + fn direction() { + let mut lsettings = line::Settings::new().unwrap(); + assert_eq!( + lsettings.prop(SettingKind::Direction).unwrap(), + SettingVal::Direction(Direction::AsIs) + ); + + lsettings.set_direction(Direction::Input).unwrap(); + assert_eq!( + lsettings.prop(SettingKind::Direction).unwrap(), + SettingVal::Direction(Direction::Input) + ); + + lsettings.set_direction(Direction::Output).unwrap(); + assert_eq!( + lsettings.prop(SettingKind::Direction).unwrap(), + SettingVal::Direction(Direction::Output) + ); + } + + #[test] + fn edge_detection() { + let mut lsettings = line::Settings::new().unwrap(); + assert_eq!( + lsettings.prop(SettingKind::EdgeDetection).unwrap(), + SettingVal::EdgeDetection(None) + ); + + lsettings.set_edge_detection(Some(Edge::Both)).unwrap(); + assert_eq!( + lsettings.prop(SettingKind::EdgeDetection).unwrap(), + SettingVal::EdgeDetection(Some(Edge::Both)) + ); + + lsettings.set_edge_detection(Some(Edge::Rising)).unwrap(); + assert_eq!( + lsettings.prop(SettingKind::EdgeDetection).unwrap(), + SettingVal::EdgeDetection(Some(Edge::Rising)) + ); + + lsettings.set_edge_detection(Some(Edge::Falling)).unwrap(); + assert_eq!( + lsettings.prop(SettingKind::EdgeDetection).unwrap(), + SettingVal::EdgeDetection(Some(Edge::Falling)) + ); + } + + #[test] + fn bias() { + let mut lsettings = line::Settings::new().unwrap(); + assert_eq!( + lsettings.prop(SettingKind::Bias).unwrap(), + SettingVal::Bias(None) + ); + + lsettings.set_bias(Some(Bias::PullDown)).unwrap(); + assert_eq!( + lsettings.prop(SettingKind::Bias).unwrap(), + SettingVal::Bias(Some(Bias::PullDown)) + ); + + lsettings.set_bias(Some(Bias::PullUp)).unwrap(); + assert_eq!( + lsettings.prop(SettingKind::Bias).unwrap(), + SettingVal::Bias(Some(Bias::PullUp)) + ); + } + + #[test] + fn drive() { + let mut lsettings = line::Settings::new().unwrap(); + assert_eq!( + lsettings.prop(SettingKind::Drive).unwrap(), + SettingVal::Drive(Drive::PushPull) + ); + + lsettings.set_drive(Drive::PushPull).unwrap(); + assert_eq!( + lsettings.prop(SettingKind::Drive).unwrap(), + SettingVal::Drive(Drive::PushPull) + ); + + lsettings.set_drive(Drive::OpenDrain).unwrap(); + assert_eq!( + lsettings.prop(SettingKind::Drive).unwrap(), + SettingVal::Drive(Drive::OpenDrain) + ); + + lsettings.set_drive(Drive::OpenSource).unwrap(); + assert_eq!( + lsettings.prop(SettingKind::Drive).unwrap(), + SettingVal::Drive(Drive::OpenSource) + ); + } + + #[test] + fn active_low() { + let mut lsettings = line::Settings::new().unwrap(); + assert_eq!( + lsettings.prop(SettingKind::ActiveLow).unwrap(), + SettingVal::ActiveLow(false) + ); + + lsettings.set_active_low(true); + assert_eq!( + lsettings.prop(SettingKind::ActiveLow).unwrap(), + SettingVal::ActiveLow(true) + ); + + lsettings.set_active_low(false); + assert_eq!( + lsettings.prop(SettingKind::ActiveLow).unwrap(), + SettingVal::ActiveLow(false) + ); + } + + #[test] + fn debounce_period() { + let mut lsettings = line::Settings::new().unwrap(); + assert_eq!( + lsettings.prop(SettingKind::DebouncePeriod).unwrap(), + SettingVal::DebouncePeriod(Duration::from_millis(0)) + ); + + lsettings.set_debounce_period(Duration::from_millis(5)); + assert_eq!( + lsettings.prop(SettingKind::DebouncePeriod).unwrap(), + SettingVal::DebouncePeriod(Duration::from_millis(5)) + ); + } + + #[test] + fn event_clock() { + let mut lsettings = line::Settings::new().unwrap(); + assert_eq!( + lsettings.prop(SettingKind::EventClock).unwrap(), + SettingVal::EventClock(EventClock::Monotonic) + ); + + lsettings.set_event_clock(EventClock::Realtime).unwrap(); + assert_eq!( + lsettings.prop(SettingKind::EventClock).unwrap(), + SettingVal::EventClock(EventClock::Realtime) + ); + + lsettings.set_event_clock(EventClock::Monotonic).unwrap(); + assert_eq!( + lsettings.prop(SettingKind::EventClock).unwrap(), + SettingVal::EventClock(EventClock::Monotonic) + ); + } + + #[test] + #[ignore] + fn event_clock_hte() { + let mut lsettings = line::Settings::new().unwrap(); + assert_eq!( + lsettings.prop(SettingKind::EventClock).unwrap(), + SettingVal::EventClock(EventClock::Monotonic) + ); + + lsettings.set_event_clock(EventClock::HTE).unwrap(); + assert_eq!( + lsettings.prop(SettingKind::EventClock).unwrap(), + SettingVal::EventClock(EventClock::HTE) + ); + } + + #[test] + fn output_value() { + let mut lsettings = line::Settings::new().unwrap(); + assert_eq!( + lsettings.prop(SettingKind::OutputValue).unwrap(), + SettingVal::OutputValue(Value::InActive) + ); + + lsettings.set_output_value(Value::Active).unwrap(); + assert_eq!( + lsettings.prop(SettingKind::OutputValue).unwrap(), + SettingVal::OutputValue(Value::Active) + ); + + lsettings.set_output_value(Value::InActive).unwrap(); + assert_eq!( + lsettings.prop(SettingKind::OutputValue).unwrap(), + SettingVal::OutputValue(Value::InActive) + ); + } +} diff --git a/bindings/rust/libgpiod/tests/request_config.rs b/bindings/rust/libgpiod/tests/request_config.rs new file mode 100644 index 000000000000..9729fb17368b --- /dev/null +++ b/bindings/rust/libgpiod/tests/request_config.rs @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +mod common; + +mod request_config { + use libgpiod::{request, Error as ChipError, OperationType}; + + mod verify { + use super::*; + + #[test] + fn default() { + let rconfig = request::Config::new().unwrap(); + + assert_eq!(rconfig.event_buffer_size(), 0); + assert_eq!( + rconfig.consumer().unwrap_err(), + ChipError::OperationFailed( + OperationType::RequestConfigGetConsumer, + errno::Errno(0), + ) + ); + } + + #[test] + fn initialized() { + const CONSUMER: &str = "foobar"; + let rconfig = request::Config::new().unwrap(); + rconfig.set_consumer(CONSUMER).unwrap(); + rconfig.set_event_buffer_size(64); + + assert_eq!(rconfig.event_buffer_size(), 64); + assert_eq!(rconfig.consumer().unwrap(), CONSUMER); + } + } +} From patchwork Mon Nov 7 09:57:34 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 622476 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 5B3F1C4332F for ; Mon, 7 Nov 2022 09:58:12 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231355AbiKGJ6L (ORCPT ); Mon, 7 Nov 2022 04:58:11 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49324 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231589AbiKGJ6H (ORCPT ); Mon, 7 Nov 2022 04:58:07 -0500 Received: from mail-pg1-x536.google.com (mail-pg1-x536.google.com [IPv6:2607:f8b0:4864:20::536]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id BE7691581E for ; Mon, 7 Nov 2022 01:58:06 -0800 (PST) Received: by mail-pg1-x536.google.com with SMTP id q71so9989151pgq.8 for ; Mon, 07 Nov 2022 01:58:06 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=ibQIYKNHrvV99f8U/JaozemK8LCdkqYO52B0gI5m82c=; b=BMHG5+KCGkaCdE6pc9EZ61pvrv9TkGZCK2B8KMKBneys3M0RiS7ijHIJ3epbj1SCQz cGXsF4uurJjxiaveV+ZZ1V9onX3eax2eKGLaambtj7q6yF9eVdnYiAAEBZZ5kPHC3gFW nikrlkPwHVi4a1bP7rAhNoz19+hDrrxu711NG4qG5ZKXCtafcOtiDXQvMnCc7VsfdO7a tX64NjrPmiVOx64szdoZhEJwoyiSEYSHIjnSVjUa69I7ek7QAaaylNEKGlbu+g3Dz/zO gel8XxqU+WcgSIKfHoVlUDJ6ZAGv6V4m9SmShB26vIVao/xU83SxycT7dWrHJ7Z+4b58 j74g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=ibQIYKNHrvV99f8U/JaozemK8LCdkqYO52B0gI5m82c=; b=BqFQ1FgvgSKPsuHrMQef8GxDvL/KswVBSBC/Ms5Kp6VlfVX1qZhTgAUN8umjcHv78u 6tah7MXuRmwmZis7mP5qq1gWFWu7KLFLOd0fUoXx5YLjXTX7Kh9nQwZKAstB9uxfna6F YGR5KaAzT7GGdFr7F8MsMjvWnokj4sygCyHjFQlUdvuH6nElPKQCMs+4smhk+5SKkt+r LB9TUL3/qGgb/muVWQmWYUwMsBEvTL6nid0PDwHOg1oJWkK5kwbkrd2RkEsnXcvKB8si U4Rxv8fc+I6k+MOImm71lHSV8H4AAy/aYVnfdqDQPKmjWUT8Y8HWgxvOBatKM7enwpas zo+Q== X-Gm-Message-State: ACrzQf1hyZblNrI9DGT4OLP5CbTa19UnxRDv00loK+DW8YIEZizCGxLi tmq7Zbi31qIjv+yTF7jZPiuvmA== X-Google-Smtp-Source: AMsMyM5knZXFVWdzGCNLcHuJQVLhohpQur4ND0qZfjlkmTQhEkIcDyLbj5I0nXhyiGj1LFOfWuV8rQ== X-Received: by 2002:a63:1c52:0:b0:470:a4a:66b3 with SMTP id c18-20020a631c52000000b004700a4a66b3mr23537462pgm.573.1667815086260; Mon, 07 Nov 2022 01:58:06 -0800 (PST) Received: from localhost ([122.172.84.80]) by smtp.gmail.com with ESMTPSA id a20-20020a1709027d9400b0018862b7f8besm4607785plm.160.2022.11.07.01.58.05 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 07 Nov 2022 01:58:05 -0800 (PST) From: Viresh Kumar To: Linus Walleij , Bartosz Golaszewski , Kent Gibson Cc: Viresh Kumar , Vincent Guittot , linux-gpio@vger.kernel.org, Miguel Ojeda , Wedson Almeida Filho , =?utf-8?q?Alex_Benn=C3=A9e?= , stratos-dev@op-lists.linaro.org, Gerard Ryan , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , y86-dev Subject: [libgpiod][PATCH V9 8/8] bindings: rust: Integrate building of bindings with make Date: Mon, 7 Nov 2022 15:27:34 +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 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 048b2ac7af8f..4a2cdb68b8b0 100644 --- a/configure.ac +++ b/configure.ac @@ -212,6 +212,21 @@ then [AC_SUBST(PYTHON_LIBS, [`$PYTHON-config --libs`])]) fi +AC_ARG_ENABLE([bindings-rust], + [AS_HELP_STRING([--enable-bindings-rust],[enable rust bindings [default=no]])], + [if test "x$enableval" = xyes; then with_bindings_rust=true; fi], + [with_bindings_rust=false]) +AM_CONDITIONAL([WITH_BINDINGS_RUST], [test "x$with_bindings_rust" = xtrue]) + +if test "x$with_bindings_rust" = xtrue +then + AC_CHECK_PROG([has_cargo], [cargo], [true], [false]) + if test "x$has_cargo" = xfalse + then + AC_MSG_ERROR([cargo not found - needed for rust bindings]) + fi +fi + AC_CHECK_PROG([has_doxygen], [doxygen], [true], [false]) AM_CONDITIONAL([HAS_DOXYGEN], [test "x$has_doxygen" = xtrue]) if test "x$has_doxygen" = xfalse @@ -249,6 +264,7 @@ AC_CONFIG_FILES([Makefile bindings/python/examples/Makefile bindings/python/tests/Makefile bindings/python/tests/gpiosim/Makefile + bindings/rust/Makefile man/Makefile]) AC_OUTPUT