From patchwork Mon Oct 31 11:47:09 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 620723 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 A54EEC38A02 for ; Mon, 31 Oct 2022 11:47:35 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230265AbiJaLrf (ORCPT ); Mon, 31 Oct 2022 07:47:35 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46296 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230256AbiJaLre (ORCPT ); Mon, 31 Oct 2022 07:47:34 -0400 Received: from mail-pj1-x1031.google.com (mail-pj1-x1031.google.com [IPv6:2607:f8b0:4864:20::1031]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E5D1AE0D7 for ; Mon, 31 Oct 2022 04:47:32 -0700 (PDT) Received: by mail-pj1-x1031.google.com with SMTP id l22-20020a17090a3f1600b00212fbbcfb78so15655698pjc.3 for ; Mon, 31 Oct 2022 04:47:32 -0700 (PDT) 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=rNKElcrmbNIZeC7S7h4x4lyXqk1EBxG4YOO2/1GJ8UM=; b=WJAszw8IxGPNBuI2fmj3LYakpXR/KP0nigCG0akXzKhLkrLz8bRx5ebWnAh31UOYp5 2UOa2ObOCUqL8iwJ18V0KkN/KjgOj+U4/yPYrPyStMHBWhVb8FyDggwa6stKnVcpKc5H LEAdvjFVzhxAikHZi91XPiAK9ORhln/FdDA4HoFiuDXx3ZnSlwqJidqiTsb2iT/rwuud blkFl1SEsa81MAbnoRmyz9VAQ4rg4EjR11MvAFjiqkLlpY/i/iz9py4pb4jBvJdIBaU4 3rvOHJCT0UNodCLtSb73hSausq5iMlCv355tEK+JnYpMzr2mGq7YnTTCMJeVzpkTNa4a TjYA== 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=rNKElcrmbNIZeC7S7h4x4lyXqk1EBxG4YOO2/1GJ8UM=; b=OyPIIRkeTDfSDeUwUOcEnCovZnTsv9GnPqEs8FBC2Wb7Q2UqdEQnV9yc63+iXF66Ka 6lZZAFwupC3FpZOKac4gGR8/pWVGqAkObknO96+dYof5N8wS7Tq5CNKeuws1Pz/RVFYf 8LvK6GhU6bxH9SUcMSxOs/1udv5ZIRAhI4jfkCIZiJEi1ChAB5H2qM79ZGn5Wfx/dZ3f 9Il8trsA4iv38Yqse9y3QFGOoRiSet9gG4d718zk0YPbQPbeO3FhViVR5bb5yj3P0BUw dx8LR+GSPgMYm64tysgJ/5FsIMDWDNpZq+m51zbmzMqAiW7C/U/lNPB1mR0aaaAdYOFF u5hQ== X-Gm-Message-State: ACrzQf2k1YTCVCMo09XZgnZQiQFxSz/1N7AgzS0fItqayantK3WvhFAD qTvZUY/RN+jdn68jQCRh8ieP3Q== X-Google-Smtp-Source: AMsMyM50LiRY5jEDRxRPKyhamt8HFb3J5o7V0PlellRKUZ4igyy8SNNAfLZhaWO3j1VdFkPG41qLTA== X-Received: by 2002:a17:902:7d8b:b0:187:3030:ab02 with SMTP id a11-20020a1709027d8b00b001873030ab02mr514544plm.80.1667216852399; Mon, 31 Oct 2022 04:47:32 -0700 (PDT) Received: from localhost ([122.172.84.80]) by smtp.gmail.com with ESMTPSA id o125-20020a625a83000000b00560e5da42d5sm4395642pfb.201.2022.10.31.04.47.31 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 31 Oct 2022 04:47:32 -0700 (PDT) From: Viresh Kumar To: Linus Walleij , Bartosz Golaszewski Cc: Viresh Kumar , Vincent Guittot , linux-gpio@vger.kernel.org, Kent Gibson , Miguel Ojeda , Wedson Almeida Filho , =?utf-8?q?Alex_Benn=C3=A9e?= , stratos-dev@op-lists.linaro.org, Gerard Ryan , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , y86-dev Subject: [libgpiod v2][PATCH V8 1/9] bindings: rust: Add libgpiod-sys rust crate Date: Mon, 31 Oct 2022 17:17:09 +0530 Message-Id: <8762a113cf8540e4dd56e45b187925b06f8b72c5.1667215380.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 | 22 ++++++++++++++ bindings/rust/libgpiod-sys/build.rs | 41 +++++++++++++++++++++++++++ bindings/rust/libgpiod-sys/src/lib.rs | 13 +++++++++ 5 files changed, 86 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..716dde551263 --- /dev/null +++ b/bindings/rust/libgpiod-sys/Cargo.toml @@ -0,0 +1,22 @@ +[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" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[features] +generate = [ "bindgen" ] + +[build-dependencies] +bindgen = { version = "0.59.1", optional = true } +cc = "1.0.46" diff --git a/bindings/rust/libgpiod-sys/build.rs b/bindings/rust/libgpiod-sys/build.rs new file mode 100644 index 000000000000..98863686c7af --- /dev/null +++ b/bindings/rust/libgpiod-sys/build.rs @@ -0,0 +1,41 @@ +#[cfg(feature = "generate")] +extern crate bindgen; +#[cfg(feature = "generate")] +use std::env; +#[cfg(feature = "generate")] +use std::path::PathBuf; + +#[cfg(feature = "generate")] +fn generate_bindings() { + // Tell cargo to invalidate the built crate whenever following files change + println!("cargo:rerun-if-changed=../../../include/gpiod.h"); + + // The bindgen::Builder is the main entry point + // to bindgen, and lets you build up options for + // the resulting bindings. + let bindings = bindgen::Builder::default() + // The input header we would like to generate + // bindings for. + .header("../../../include/gpiod.h") + // Tell cargo to invalidate the built crate whenever any of the + // included header files changed. + .parse_callbacks(Box::new(bindgen::CargoCallbacks)) + // Finish the builder and generate the bindings. + .generate() + // Unwrap the Result and panic on failure. + .expect("Unable to generate bindings"); + + // Write the bindings to the $OUT_DIR/bindings.rs file. + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Couldn't write bindings!"); +} + +fn main() { + #[cfg(feature = "generate")] + generate_bindings(); + + println!("cargo:rustc-link-search=./../../lib/.libs/"); + println!("cargo:rustc-link-lib=static=gpiod"); +} diff --git a/bindings/rust/libgpiod-sys/src/lib.rs b/bindings/rust/libgpiod-sys/src/lib.rs new file mode 100644 index 000000000000..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 Oct 31 11:47:10 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 620355 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 C4DDCFA3740 for ; Mon, 31 Oct 2022 11:47:42 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230327AbiJaLrk (ORCPT ); Mon, 31 Oct 2022 07:47:40 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46384 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229566AbiJaLrj (ORCPT ); Mon, 31 Oct 2022 07:47:39 -0400 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 48E51EE1A for ; Mon, 31 Oct 2022 04:47:36 -0700 (PDT) Received: by mail-pg1-x536.google.com with SMTP id g129so10471698pgc.7 for ; Mon, 31 Oct 2022 04:47:36 -0700 (PDT) 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=yZGqu5cM/GMLOeGaQbGu2GkfwQ11zUUAFIIw4oRRnAK7HUWVOQ4+3wlbANJJ20cLAk XaJaKz63nvT6WZjXjTKpgqmwSyi9gqIZUNhjK15e1VXSjYyE3u8dJJjnM9HV/S2IlOr5 JX7UoxSzddLq3wyY5Z0v8K/owvjcjfcuxFeKTnBODkrNURMaCNQ1mYDJHrPmT9qoRFnd INiUwpk6ZCYD6Y82TRa93fGd5xoqYbeq/VMtoHvKV2H/uvkhZX21zTT43CgIXs84WpGZ cODHnbMiA1BtWOY73J2/P4U9dtBK/VMTCRo0sv4PPzkRRJyf0uzzfSWCLcu4A37i+puX OoHw== 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=r9Rubyrhl0Q+9VpE4szeY8F7z9wCe3Mb1wCnxpKc73G1My+WdibY+cd5tSgHvSKMMn uRv2YCORalmBWodOFEHktVDZzpC6x7HvG75/JTVUI+JDPv9aw/Njm2o6llzgouvwbGBE lMO3QHaFosm1rt5wwtLERF8sMBhb6hijddQIYA2SWwhr/8N/cE7aoIqkax6q5W0uJmWI RszhePNZsX7sqpKG6NqS9EXn4ZLdPPJYSIEVtww10Gh8hg0l9vbGidJ6cnd3PX3rCUEX wQeWnWPSUf+NAXVnNBQ8NHiF91U+HxvptcGsfExMJE6kluYkEA6WHYXwsWUR4avOUS0W X3TA== X-Gm-Message-State: ACrzQf3vxgcI/Bv6Lb3hABTLS6mA9k1o4mFRujPLj24YsVQuf2L8+3fW gbHsLGfc7Msk/Es0Ylu52Mw5ew== X-Google-Smtp-Source: AMsMyM7FqIqMJlXPeWSE6OGdCD5/7xErDLuw54mzm/5CThA6Mb++KKyVBFB5cx8Pl4Bm9TroKr4+ww== X-Received: by 2002:a63:1a45:0:b0:439:49b4:9672 with SMTP id a5-20020a631a45000000b0043949b49672mr12098884pgm.551.1667216854989; Mon, 31 Oct 2022 04:47:34 -0700 (PDT) Received: from localhost ([122.172.84.80]) by smtp.gmail.com with ESMTPSA id x17-20020aa78f11000000b0056c410fd03fsm4388853pfr.40.2022.10.31.04.47.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 31 Oct 2022 04:47:34 -0700 (PDT) From: Viresh Kumar To: Linus Walleij , Bartosz Golaszewski Cc: Viresh Kumar , Vincent Guittot , linux-gpio@vger.kernel.org, Kent Gibson , Miguel Ojeda , Wedson Almeida Filho , =?utf-8?q?Alex_Benn=C3=A9e?= , stratos-dev@op-lists.linaro.org, Gerard Ryan , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , y86-dev Subject: [libgpiod v2][PATCH V8 2/9] bindings: rust: Add pre generated bindings for libgpiod-sys Date: Mon, 31 Oct 2022 17:17:10 +0530 Message-Id: <62726bc4dffffffb841cba09fb22d00e7e1bd12b.1667215380.git.viresh.kumar@linaro.org> X-Mailer: git-send-email 2.31.1.272.g89b43f80a514 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org This adds a copy of pre generated bindings and adds the suggested way of updating those in README. Signed-off-by: Viresh Kumar --- bindings/rust/libgpiod-sys/README.md | 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 Oct 31 11:47:11 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 620722 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 99076C38A02 for ; Mon, 31 Oct 2022 11:47:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229566AbiJaLrl (ORCPT ); Mon, 31 Oct 2022 07:47:41 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46388 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230329AbiJaLrj (ORCPT ); Mon, 31 Oct 2022 07:47:39 -0400 Received: from mail-pl1-x62b.google.com (mail-pl1-x62b.google.com [IPv6:2607:f8b0:4864:20::62b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 515BBE0D7 for ; Mon, 31 Oct 2022 04:47:38 -0700 (PDT) Received: by mail-pl1-x62b.google.com with SMTP id k7so779004pll.6 for ; Mon, 31 Oct 2022 04:47:38 -0700 (PDT) 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=xETML4fKYTWTQZ0kO68G1H1+CjNqzcQf8PVJA8eJptU=; b=uPqB0PuGeY0eL9/lHAtrm8qoNyqGoP2GBxGplrgdqliPKqvEWXOqu2k7EQ3QLMNnGQ PtiubUvoCaQ+klVGsW7uKZA6ce6/QRE6XDYx/OfXKv9oEZoNA+hd2vDJnVasWT73bXaz mFc5BGftO8zt8XZjjP0riZh0uUIB7EHD2KZtOBypaYwE8sD+QT4C+Vu2M5JmP1F+6kkB eIDriIf7mX9jFpCQi8Yk9GVRzStAQwzW2EAY+WLl/nYlfaCIbBepIj6vz10LIKlGESEg joyFyeclBvJ4KRjaZTL30UAPXh53Ha7YzHYtTueAv/9S4XU4PKljZyc1bSCMed1oQp/6 UahA== 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=xETML4fKYTWTQZ0kO68G1H1+CjNqzcQf8PVJA8eJptU=; b=Vbn4vy3BRjEOFLB+LZmTKjhR0v5diw1DtJpgmRGIzI/kjB5lykfLsS7cvgzQFGnRvV ps5mRVYkouS2wC1RBq7g66Rubdr7x5FBE2F7up4pQQlYCr74e/lkGKYZAdHS3tF5lp1Q jH/YYz2AXkQ87lvhnKTchGGQjfk9lvfi83dXOhCm613NM9zohq7g/Q0EC1QLdGiHqeRl F3+ovGp8q592FHpfBhDibHgMDXk6PiTJybXDtLTqVksqhVYYBHkAPvrMDlrqrP61Qr2J 22d4ycAc720VXymEbRlf6m/xEmG8uoxz/LE/GDUe+CyFJC/7sik44qeQImzOAPI27Upg D4wg== X-Gm-Message-State: ACrzQf3+HdnReM3HYhJ/DS4CzJ4W2u3tisjFbIfLVn2YmonibJ1SKpRf pQwtn9oXMF90kUgogl+Yc/DUlw== X-Google-Smtp-Source: AMsMyM7bwFimf2sX7JjPDRIzYQkhwbI2QXhTzB1lrSKNRsmWikF0FHijQ/9sk6NUjtLyHs6AiYAHHA== X-Received: by 2002:a17:902:d386:b0:187:190f:6ac7 with SMTP id e6-20020a170902d38600b00187190f6ac7mr7500126pld.170.1667216857645; Mon, 31 Oct 2022 04:47:37 -0700 (PDT) Received: from localhost ([122.172.84.80]) by smtp.gmail.com with ESMTPSA id f2-20020a633802000000b004411a054d2dsm3962566pga.82.2022.10.31.04.47.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 31 Oct 2022 04:47:37 -0700 (PDT) From: Viresh Kumar To: Linus Walleij , Bartosz Golaszewski Cc: Viresh Kumar , Vincent Guittot , linux-gpio@vger.kernel.org, Kent Gibson , Miguel Ojeda , Wedson Almeida Filho , =?utf-8?q?Alex_Benn=C3=A9e?= , stratos-dev@op-lists.linaro.org, Gerard Ryan , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , y86-dev Subject: [libgpiod v2][PATCH V8 3/9] bindings: rust: Add gpiosim crate Date: Mon, 31 Oct 2022 17:17:11 +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 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 | 24 +++ bindings/rust/gpiosim/build.rs | 43 ++++ bindings/rust/gpiosim/src/lib.rs | 79 ++++++++ bindings/rust/gpiosim/src/sim.rs | 331 +++++++++++++++++++++++++++++++ 5 files changed, 478 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..8ae6931f8a16 --- /dev/null +++ b/bindings/rust/gpiosim/Cargo.toml @@ -0,0 +1,24 @@ +[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" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[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 Oct 31 11:47:12 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 620721 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 43B56FA3742 for ; Mon, 31 Oct 2022 11:47:44 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230329AbiJaLrn (ORCPT ); Mon, 31 Oct 2022 07:47:43 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46394 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230436AbiJaLrm (ORCPT ); Mon, 31 Oct 2022 07:47:42 -0400 Received: from mail-pj1-x102c.google.com (mail-pj1-x102c.google.com [IPv6:2607:f8b0:4864:20::102c]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 08DC1203 for ; Mon, 31 Oct 2022 04:47:41 -0700 (PDT) Received: by mail-pj1-x102c.google.com with SMTP id l6so10197373pjj.0 for ; Mon, 31 Oct 2022 04:47:41 -0700 (PDT) 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=KFKUIdhg8vdRqUsUECfykbtoMoHBQPZnLJbrD8qtyJXFhS4x8IufuGphzSh8XKfhrc KJjJpmY8cEp+ltDFeXZ8YNacHDoHUu7umtZSvzgObaTdxN+OGfodI376PzxlflzxtrGn Y+nkrNJnyA5JRnuArp9mWCFUW78CvxT9HY1C9D5QyNVqR8XHpg4WnXXKhHbLmY+Hb50f NrsC5AxJKDaRMHaBejv3IB7wylB8iymj9RkM/FyxuEbfO52L1tCRR3rf8x6kjIpvJcqN Z1uzxbuC2gWCFCMS1GsI5N/D2eygMeWSHGYsaoI5TRA5nlnZtz3cFGsy81nyoRTPJvGp xlqA== 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=eCRnjdkbkOAUe/L3epUBNvCPRvZdSm8WOFW92eIp2qDwtBbynKlI/vpY7hE6oTM1Lu gG7eN4GR/kavcW3MXOyVwoz50fjQ6xbAjUrtkz1UqcF0ezcVbk3k8SVX3fW6rovoJnxS tnDj8xnKwcVtWSdLU6LhwXtR7TAA0FVfePZJiEEtvJk8vVCeXFh0+MN+pWwmt/3pDLWl TDiYxVrfh/Wyb8LqBGrjMEoUPOE7ioYqNvlIizFATr5erVhZBq8r25ufPCjnw8+Lwyqi Cqubt7xAPrTTjIeVdHtUhUxJCFMizgNo51QFZ/SKm5jTHWhuMnj4yhFcijkF30UBKsTc lQMg== X-Gm-Message-State: ACrzQf1tuKwBaVoHOVAIqycF1Tm7fyGRAuG3HpCvTWpwj+pctTZYdU+k 1JZEUh4kpG4eQeo8HyDLiS4sSw== X-Google-Smtp-Source: AMsMyM5JVmA6MD0gyqxv5rkjoSnEaZJ1aiKo7q++CsWFgnnD3z2RjdJncswF+qr+26mxW3zlmkaNJA== X-Received: by 2002:a17:90b:80c:b0:213:13aa:3e2c with SMTP id bk12-20020a17090b080c00b0021313aa3e2cmr31912043pjb.243.1667216860521; Mon, 31 Oct 2022 04:47:40 -0700 (PDT) Received: from localhost ([122.172.84.80]) by smtp.gmail.com with ESMTPSA id k7-20020aa79727000000b0053e8fe8a705sm4579484pfg.17.2022.10.31.04.47.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 31 Oct 2022 04:47:40 -0700 (PDT) From: Viresh Kumar To: Linus Walleij , Bartosz Golaszewski Cc: Viresh Kumar , Vincent Guittot , linux-gpio@vger.kernel.org, Kent Gibson , Miguel Ojeda , Wedson Almeida Filho , =?utf-8?q?Alex_Benn=C3=A9e?= , stratos-dev@op-lists.linaro.org, Gerard Ryan , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , y86-dev Subject: [libgpiod v2][PATCH V8 4/9] bindings: rust: Add pre generated bindings for gpiosim Date: Mon, 31 Oct 2022 17:17:12 +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/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 Oct 31 11:47:13 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 620354 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 A8747C38A02 for ; Mon, 31 Oct 2022 11:47:50 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230436AbiJaLrt (ORCPT ); Mon, 31 Oct 2022 07:47:49 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46498 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230454AbiJaLrs (ORCPT ); Mon, 31 Oct 2022 07:47:48 -0400 Received: from mail-pf1-x42a.google.com (mail-pf1-x42a.google.com [IPv6:2607:f8b0:4864:20::42a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9A771E0ED for ; Mon, 31 Oct 2022 04:47:45 -0700 (PDT) Received: by mail-pf1-x42a.google.com with SMTP id i3so10429057pfc.11 for ; Mon, 31 Oct 2022 04:47:45 -0700 (PDT) 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=qsBFk66oWCr+PQ3BbXjiDBtrBhRPVWtqweEn/ym9N74=; b=Yu5P4/qluTjupRINj3bi1NHx5jLDOg5O4bmhviHx4SGJVxQbLA1qC0Zn3N1OFOpoIQ vVT1f0oglVHb2UH4lYX1c82bNoASVuXjLy5DLHShpG1CueJHV7DOvaxe0C7uuYMVcmSz 2GXQvnDHhr0rdC6kzPfIFgNcbyLpdGPuUiuA3H8629xbLnnAFueyKqf8gQRFlj/VH8Sr J3ba+dFwCSWvJOI+P40jsnU+V8OlFDgfrw1JT/dAnqgNpAnVyUTxVv2Vr5rBnC301Nw8 +tjtOKVxvzmqBssniyJlpLc1sKGk8WHFfQ7Mfswmpgel5TpXM3EbX+SxSmyMTX0uxsKO yANA== 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=qsBFk66oWCr+PQ3BbXjiDBtrBhRPVWtqweEn/ym9N74=; b=M74UwUaAJ7zEO+1z5/tSiQcly6ReAiKckED4ByOBK1+U9ZVcgQFq7s5YZ/LYBcgEZT US1LYAKMLaiuKEfrYxmFn2aVqTbRyrC0Oil4G76cImtBdCu5juE5DvCfdnQDVZ290j0Z 2hOsqPFx46gvTSDzOg+zA2++HQyr2ieFyHtLuz5kzcQaLAFJ4gs0RszD6xHKIrBsHLJz CAEnU0IhoSMtLRhcpG6nwkIv9Ge6aJ1PrqZ77MADURCkt3IHBBQcmMH40a5+Q6MJt2vQ yLZ3lz1m51sNAVJBd63QJ95MMd7HHPqXAQReNGwTfY8Wc6P6KSQttXJnZJ6PuC2QS0lC 72YQ== X-Gm-Message-State: ACrzQf1LiWpyK2pYtamkpOnrKdufWAV9crxz60ajLNHRZJfoFADNwcSf HnMPTUqnvRCv6UMFoxSyfVRnfw== X-Google-Smtp-Source: AMsMyM4SPN7B3DfjsGuiRwfdETQbRipBXjTYuqmHq2Bg6AVOYX7T1j/xEf5/BuQZCidEarWt3YFAUg== X-Received: by 2002:aa7:81cc:0:b0:563:4e53:c08b with SMTP id c12-20020aa781cc000000b005634e53c08bmr13935699pfn.19.1667216863944; Mon, 31 Oct 2022 04:47:43 -0700 (PDT) Received: from localhost ([122.172.84.80]) by smtp.gmail.com with ESMTPSA id w2-20020a623002000000b00561beff1e09sm4409961pfw.164.2022.10.31.04.47.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 31 Oct 2022 04:47:42 -0700 (PDT) From: Viresh Kumar To: Linus Walleij , Bartosz Golaszewski Cc: Viresh Kumar , Vincent Guittot , linux-gpio@vger.kernel.org, Kent Gibson , Miguel Ojeda , Wedson Almeida Filho , =?utf-8?q?Alex_Benn=C3=A9e?= , stratos-dev@op-lists.linaro.org, Gerard Ryan , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , y86-dev Subject: [libgpiod v2][PATCH V8 5/9] bindings: rust: Add libgpiod crate Date: Mon, 31 Oct 2022 17:17:13 +0530 Message-Id: <08d4095c314caf50430c2eaa733d925122242b12.1667215380.git.viresh.kumar@linaro.org> X-Mailer: git-send-email 2.31.1.272.g89b43f80a514 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org Add rust wrapper crate, around the libpiod-sys crate added earlier, to provide a convenient interface for the users. Signed-off-by: Viresh Kumar --- bindings/rust/Cargo.toml | 1 + bindings/rust/libgpiod/Cargo.toml | 20 + bindings/rust/libgpiod/src/chip.rs | 317 ++++++++++++ bindings/rust/libgpiod/src/edge_event.rs | 128 +++++ bindings/rust/libgpiod/src/event_buffer.rs | 102 ++++ bindings/rust/libgpiod/src/info_event.rs | 69 +++ bindings/rust/libgpiod/src/lib.rs | 478 +++++++++++++++++++ bindings/rust/libgpiod/src/line_config.rs | 135 ++++++ bindings/rust/libgpiod/src/line_info.rs | 162 +++++++ bindings/rust/libgpiod/src/line_request.rs | 224 +++++++++ bindings/rust/libgpiod/src/line_settings.rs | 297 ++++++++++++ bindings/rust/libgpiod/src/request_config.rs | 95 ++++ 12 files changed, 2028 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..ef52fdc198d7 --- /dev/null +++ b/bindings/rust/libgpiod/Cargo.toml @@ -0,0 +1,20 @@ +[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 = ["command-line-utilities", "os::linux-apis"] +rust-version = "1.56" +keywords = ["libgpiod", "gpio"] +license = "Apache-2.0 OR BSD-3-Clause" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[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..c9c01cd21feb --- /dev/null +++ b/bindings/rust/libgpiod/src/chip.rs @@ -0,0 +1,317 @@ +// 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; +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); + } + } + + /// Get the file descriptor associated with the chip. + /// + /// The returned file descriptor must not be closed by the caller, else other methods for the + /// `struct Chip` may fail. + pub fn fd(&self) -> Result { + // SAFETY: `gpiod_chip` is guaranteed to be valid here. + let fd = unsafe { gpiod::gpiod_chip_get_fd(self.ichip.chip) }; + + if fd < 0 { + Err(Error::OperationFailed( + OperationType::ChipGetFd, + errno::errno(), + )) + } else { + Ok(fd as u32) + } + } + + /// Wait for line status events on any of the watched lines on the chip. + pub fn wait_info_event(&self, timeout: Option) -> Result { + let timeout = match timeout { + Some(x) => x.as_nanos() as i64, + // Block indefinitely + None => -1, + }; + + // 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) + } +} + +/// 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..95b05e947d26 --- /dev/null +++ b/bindings/rust/libgpiod/src/edge_event.rs @@ -0,0 +1,128 @@ +// 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}, + request::Buffer, + 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<'b> { + buffer: Option<&'b Buffer>, + event: *mut gpiod::gpiod_edge_event, +} + +impl<'b> Event<'b> { + /// Get an event stored in the buffer. + pub(crate) fn new(buffer: &'b Buffer, index: usize) -> Result> { + // 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(buffer.buffer, index.try_into().unwrap()) + }; + if event.is_null() { + return Err(Error::OperationFailed( + OperationType::EdgeEventBufferGetEvent, + errno::errno(), + )); + } + + Ok(Self { + buffer: Some(buffer), + 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.event) } 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.event) }) + } + + /// 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.event) } + } + + /// Get the global sequence number of the event. + /// + /// Returns sequence number of the event relative to all lines in the + /// associated line request. + pub fn global_seqno(&self) -> usize { + // SAFETY: `gpiod_edge_event` is guaranteed to be valid here. + unsafe { + gpiod::gpiod_edge_event_get_global_seqno(self.event) + .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.event) + .try_into() + .unwrap() + } + } +} + +impl<'e, 'b> Event<'e> { + pub fn event_clone(event: &Event<'b>) -> Result> + where + 'e: 'b, + { + // SAFETY: `gpiod_edge_event` is guaranteed to be valid here. + let event = unsafe { gpiod::gpiod_edge_event_copy(event.event) }; + if event.is_null() { + return Err(Error::OperationFailed( + OperationType::EdgeEventCopy, + errno::errno(), + )); + } + + Ok(Self { + buffer: None, + event, + }) + } +} + +impl<'b> Drop for Event<'b> { + /// Free the edge event. + fn drop(&mut self) { + // Free the event only if a copy is made + if self.buffer.is_none() { + // SAFETY: `gpiod_edge_event` is guaranteed to be valid here. + unsafe { gpiod::gpiod_edge_event_free(self.event) }; + } + } +} diff --git a/bindings/rust/libgpiod/src/event_buffer.rs b/bindings/rust/libgpiod/src/event_buffer.rs new file mode 100644 index 000000000000..16d7022034df --- /dev/null +++ b/bindings/rust/libgpiod/src/event_buffer.rs @@ -0,0 +1,102 @@ +// 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 super::{gpiod, request, Error, OperationType, Result}; + +/// Line edge events buffer +#[derive(Debug, Eq, PartialEq)] +pub struct Buffer { + pub(crate) buffer: *mut gpiod::gpiod_edge_event_buffer, + event_count: usize, +} + +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(), + )); + } + + Ok(Self { + buffer, + event_count: 0, + }) + } + + /// Get a number of edge events from a line request. + /// + /// This function will block if no event was queued for the line. + pub fn read_edge_events(&mut self, request: &request::Request) -> Result { + // 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.capacity().try_into().unwrap(), + ) + }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::LineRequestReadEdgeEvent, + errno::errno(), + )) + } else { + // Set count of events read in the buffer + self.set_event_count(ret as usize); + Ok(ret as u32) + } + } + + /// Set the number of events read into the event buffer. + pub(crate) fn set_event_count(&mut self, count: usize) { + self.event_count = count + } + + /// Get the capacity of the event buffer. + pub fn capacity(&self) -> usize { + // SAFETY: `gpiod_edge_event_buffer` is guaranteed to be valid here. + unsafe { gpiod::gpiod_edge_event_buffer_get_capacity(self.buffer) as usize } + } + + /// Read an event stored in the buffer. + pub fn event(&self, index: usize) -> Result { + if index >= self.event_count { + Err(Error::InvalidArguments) + } else { + request::Event::new(self, index) + } + } + + /// Get the number of events the buffer contains. + pub fn len(&self) -> usize { + // SAFETY: `gpiod_edge_event_buffer` is guaranteed to be valid here. + unsafe { gpiod::gpiod_edge_event_buffer_get_num_events(self.buffer) as usize } + } + + /// Check if buffer is empty. + pub fn is_empty(&self) -> bool { + self.len() == 0 + } +} + +impl Drop for Buffer { + /// Free the edge event buffer and release all associated resources. + fn drop(&mut self) { + // 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..5452d47d51bc --- /dev/null +++ b/bindings/rust/libgpiod/src/lib.rs @@ -0,0 +1,478 @@ +// 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, + ChipGetFd, + 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("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..0f917fac383f --- /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, + from_event: bool, +} + +impl Info { + fn new_internal(info: *mut gpiod::gpiod_line_info, from_event: bool) -> Result { + Ok(Self { info, from_event }) + } + + /// 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.from_event { + // 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..10d2197b876a --- /dev/null +++ b/bindings/rust/libgpiod/src/line_request.rs @@ -0,0 +1,224 @@ +// 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(&self, buffer: &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 Oct 31 11:47:14 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 620720 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 D0D4DFA3749 for ; Mon, 31 Oct 2022 11:47:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230490AbiJaLrw (ORCPT ); Mon, 31 Oct 2022 07:47:52 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46532 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230462AbiJaLrv (ORCPT ); Mon, 31 Oct 2022 07:47:51 -0400 Received: from mail-pj1-x1032.google.com (mail-pj1-x1032.google.com [IPv6:2607:f8b0:4864:20::1032]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8DDC8E0D7 for ; Mon, 31 Oct 2022 04:47:47 -0700 (PDT) Received: by mail-pj1-x1032.google.com with SMTP id d59-20020a17090a6f4100b00213202d77e1so15679855pjk.2 for ; Mon, 31 Oct 2022 04:47:47 -0700 (PDT) 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=aC0VO0h74sK2z1Flqq5NajCFm6MxQGoFAhxwQT/0SJ8=; b=p1CWOMWwpfXSkEgMRwgoeXZ+ziRpXkR6aJy2GVYXUNzNaUxQ355+SAWWi/GFC3ye/D n0tCqM0JNtKQc1gzn8Bj1XZkzqiRCIKQPZUSX2+e/6iVaGO6pBgLOWAhyzNSuZfEdg75 jcL193Msda46vEBmnGs4Aw7pgroTlWAfr3CI8CU6VFclqV+DKOrYOv6yYnlc2Whyvthz D47UVS904CMPROVxXOOoEbotmmP0wpBF1lz34oY1k4eUm3HGYv25Ns1FV9+Dk50J/LO5 qlD8yE0iKmxwdthOYN8OU8ZZd+D/RJ5rdbzGObzhs2yJCDhXegZKfE1PPFie31mJD3OQ s5Gg== 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=aC0VO0h74sK2z1Flqq5NajCFm6MxQGoFAhxwQT/0SJ8=; b=RQdRa3hf14MHXdWwi9ngGjyDhpdiEBIQ0rSaN+wumzP1LUG/JqInmomuJJrW3wiTpA GCy4kudBqxn+KA5deTt06++N5hc8yAwfOdQ6Z1RFsC3rOZ4Z9C2UtkzX0Ddt6ey6X8yv N9cOwxH6kKmBtjn3O2Bb2Wwq27SVM8vEN17BWUxehHJEFGGo2LX742fQq5TtfiWiGGjV l2NkjB2EUMu1aujPklaOD/vKZBEARAe0gwFyxnfo8eKoHVWmAExzCdN7eM4m1Wz4qNH9 WhQr4OdOlUmNDby/QmpybE2Q4QEPX6Jqvs9le0kTCreMvH+eogO5v5Te+zZQXakSAGSq dRGA== X-Gm-Message-State: ACrzQf3IAoJdVVUZRJSUxuC/S6mNi6GxqyZVcJoGHgqRVmbiPXMkdIOW JaYFjwWQgcbcGIc6+5H4BcZDMA== X-Google-Smtp-Source: AMsMyM4wn/4SDwxgPGjrSZCGQHk7/QO+52v5v1V4olqP8uP+TwfEsyCwaymi+TS2vnwwN5qoNqH/uA== X-Received: by 2002:a17:902:9b8e:b0:187:30ec:67dd with SMTP id y14-20020a1709029b8e00b0018730ec67ddmr283607plp.79.1667216866922; Mon, 31 Oct 2022 04:47:46 -0700 (PDT) Received: from localhost ([122.172.84.80]) by smtp.gmail.com with ESMTPSA id n11-20020a170903404b00b00176e6f553efsm4303957pla.84.2022.10.31.04.47.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 31 Oct 2022 04:47:46 -0700 (PDT) From: Viresh Kumar To: Linus Walleij , Bartosz Golaszewski Cc: Viresh Kumar , Vincent Guittot , linux-gpio@vger.kernel.org, Kent Gibson , Miguel Ojeda , Wedson Almeida Filho , =?utf-8?q?Alex_Benn=C3=A9e?= , stratos-dev@op-lists.linaro.org, Gerard Ryan , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , y86-dev Subject: [libgpiod v2][PATCH V8 6/9] bindings: rust: Add examples to libgpiod crate Date: Mon, 31 Oct 2022 17:17:14 +0530 Message-Id: X-Mailer: git-send-email 2.31.1.272.g89b43f80a514 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org Add examples for the usage of the rust bindings, quite similar to the ones in cxx bindings. Signed-off-by: Viresh Kumar --- .../rust/libgpiod/examples/gpio_events.rs | 88 ++++++++++++ .../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, 626 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..77a7d2a5faa1 --- /dev/null +++ b/bindings/rust/libgpiod/examples/gpio_events.rs @@ -0,0 +1,88 @@ +// 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 count = request.read_edge_events(&mut buffer)?; + if count == 1 { + let event = buffer.event(0)?; + let cloned_event = request::Event::event_clone(&event)?; + + // This is required before reading events again into the buffer. + drop(event); + + let count = request.read_edge_events(&mut buffer)?; + if count == 1 { + let event = buffer.event(0)?; + println!( + "line: {} type: {:?}, time: {:?}", + cloned_event.line_offset(), + cloned_event.event_type(), + cloned_event.timestamp() + ); + println!( + "line: {} type: {:?}, time: {:?}", + event.line_offset(), + event.event_type(), + 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..a03ea31dfd53 --- /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 count = request.read_edge_events(&mut buffer)?; + if count == 1 { + let event = buffer.event(0)?; + println!( + "line: {} type: {}, time: {:?}", + event.line_offset(), + match event.event_type()? { + EdgeKind::Rising => "Rising", + EdgeKind::Falling => "Falling", + }, + event.timestamp() + ); + } + } +} diff --git a/bindings/rust/libgpiod/examples/gpioset.rs b/bindings/rust/libgpiod/examples/gpioset.rs new file mode 100644 index 000000000000..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 Oct 31 11:47:15 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 620353 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 00F70C38A02 for ; Mon, 31 Oct 2022 11:47:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230488AbiJaLr5 (ORCPT ); Mon, 31 Oct 2022 07:47:57 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46604 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230481AbiJaLrz (ORCPT ); Mon, 31 Oct 2022 07:47:55 -0400 Received: from mail-pl1-x62c.google.com (mail-pl1-x62c.google.com [IPv6:2607:f8b0:4864:20::62c]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 3930BE0F0 for ; Mon, 31 Oct 2022 04:47:51 -0700 (PDT) Received: by mail-pl1-x62c.google.com with SMTP id u6so10480534plq.12 for ; Mon, 31 Oct 2022 04:47:51 -0700 (PDT) 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=7WiU8eYe2piYZsfBQLX77iyXBOeAAEPVWvDKFGBf/3Q=; b=LGj6gFNMOhUUBV5dz2Hwhyf0ypn6FNiBMPa2aSyujTVDFDgtFKAlHkYvRSDcHoS5Wh jawqR+6uGZVbF8vWBBUzbxpjG9PFaoCswP7t1OCLMgHJHF7z5B0v0TtCqJAQkfdDzeeZ 7+8RI3IXQRC9p1+5ZNL3ODLg2NoQyTMiOVEzBcjE7uUJ0JmWGFZDqsSEVU8C2hEUDh3V hpW1udX6sGXYbmHIM9mFrQ9aWLw3seGFFcd+XVlZgZmOSapwjs8IsMyjXnt+Xb74WV4F p+cf4T8O/fMlqpZAigBRM+OWxw2+fOhYWI/EYc4ernKnxf+nBq6gOW2x2RSmPE7c29Q4 wiLQ== 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=7WiU8eYe2piYZsfBQLX77iyXBOeAAEPVWvDKFGBf/3Q=; b=Np49JbenMgqTnnpwAY069gVYWhPhWccyk2lBwfNeaeJQNR10mmYEzkxZmpO7b4T83q EE2NwAIGzOxxeeJhXcE6D/88dYBPqTOHpUFCxUMKFat3doPrD+lw+ylYBun0/3/EHiJD 85vVG25O6eYzLuSwWa9iaIvMyJSUCQBSQlGWyBggqy6FGBgqgYepW8Y+83ZQMHhwwktA p9HcUS0LDcHrbrBm90Anos9HrgLLmpTxLvRIrHhbhOeyKpPS5JKhrCOKoli4mzOZ4tT7 fsKRPB4nZhgZ8qe3Bnw75vCOr3Q7Eqtc71oC/wTmI/KalNSNTPS65zyWf0k4dKkr6Jx3 BbNg== X-Gm-Message-State: ACrzQf0YDXTYxmrGjCXXVCkJHK9U/YJ4ZvtnNgGSvwBBPmT3eAJQ5qru TEdtCgMo9jQNUyeK1zFwkB7QGQ== X-Google-Smtp-Source: AMsMyM7x3U1tuj8XaShlArGxYdjDc2mJZw3KyCzFsWusD0dCvBs/9UKWhpXmrFFRqyjEJ3Xa2ZabDw== X-Received: by 2002:a17:90a:2847:b0:213:9ae5:b9b4 with SMTP id p7-20020a17090a284700b002139ae5b9b4mr17469946pjf.136.1667216870257; Mon, 31 Oct 2022 04:47:50 -0700 (PDT) Received: from localhost ([122.172.84.80]) by smtp.gmail.com with ESMTPSA id x17-20020aa78f11000000b0053e4baecc14sm4404873pfr.108.2022.10.31.04.47.48 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 31 Oct 2022 04:47:49 -0700 (PDT) From: Viresh Kumar To: Linus Walleij , Bartosz Golaszewski Cc: Viresh Kumar , Vincent Guittot , linux-gpio@vger.kernel.org, Kent Gibson , Miguel Ojeda , Wedson Almeida Filho , =?utf-8?q?Alex_Benn=C3=A9e?= , stratos-dev@op-lists.linaro.org, Gerard Ryan , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , y86-dev Subject: [libgpiod v2][PATCH V8 7/9] bindings: rust: Add tests for libgpiod crate Date: Mon, 31 Oct 2022 17:17:15 +0530 Message-Id: <0e1deffab733e0a8458d1626bc20cd496cbd5099.1667215380.git.viresh.kumar@linaro.org> X-Mailer: git-send-email 2.31.1.272.g89b43f80a514 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org Add tests for the rust bindings, quite similar to the ones in cxx bindings. Signed-off-by: Viresh Kumar --- bindings/rust/libgpiod/Cargo.toml | 3 + bindings/rust/libgpiod/tests/chip.rs | 99 ++++ bindings/rust/libgpiod/tests/common/config.rs | 143 +++++ bindings/rust/libgpiod/tests/common/mod.rs | 10 + bindings/rust/libgpiod/tests/edge_event.rs | 300 +++++++++++ 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, 1847 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 ef52fdc198d7..e920eac61a34 100644 --- a/bindings/rust/libgpiod/Cargo.toml +++ b/bindings/rust/libgpiod/Cargo.toml @@ -18,3 +18,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..f508e1e38628 --- /dev/null +++ b/bindings/rust/libgpiod/tests/chip.rs @@ -0,0 +1,99 @@ +// 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); + assert!(chip.fd().is_ok()); + } + + #[test] + fn find_line() { + let sim = Sim::new(Some(NGPIO), None, false).unwrap(); + sim.set_line_name(0, "zero").unwrap(); + sim.set_line_name(2, "two").unwrap(); + sim.set_line_name(3, "three").unwrap(); + sim.set_line_name(5, "five").unwrap(); + sim.set_line_name(10, "ten").unwrap(); + sim.set_line_name(11, "ten").unwrap(); + sim.enable().unwrap(); + + let chip = Chip::open(&sim.dev_path()).unwrap(); + + // Success case + assert_eq!(chip.line_offset_from_name("zero").unwrap(), 0); + assert_eq!(chip.line_offset_from_name("two").unwrap(), 2); + assert_eq!(chip.line_offset_from_name("three").unwrap(), 3); + assert_eq!(chip.line_offset_from_name("five").unwrap(), 5); + + // Success with duplicate names, should return first entry + assert_eq!(chip.line_offset_from_name("ten").unwrap(), 10); + + // Failure + assert_eq!( + chip.line_offset_from_name("nonexistent").unwrap_err(), + ChipError::OperationFailed( + OperationType::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..dd95f6d82caa --- /dev/null +++ b/bindings/rust/libgpiod/tests/edge_event.rs @@ -0,0 +1,300 @@ +// 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()); + + assert_eq!(config.request().read_edge_events(&mut buf).unwrap(), 1); + assert_eq!(buf.len(), 1); + + let event = buf.event(0).unwrap(); + let ts_rising = event.timestamp(); + assert_eq!(event.event_type().unwrap(), EdgeKind::Rising); + assert_eq!(event.line_offset(), GPIO); + drop(event); + + // Falling event + assert!(config + .request() + .wait_edge_event(Some(Duration::from_secs(1))) + .unwrap()); + + assert_eq!(config.request().read_edge_events(&mut buf).unwrap(), 1); + assert_eq!(buf.len(), 1); + + let event = buf.event(0).unwrap(); + let ts_falling = event.timestamp(); + assert_eq!(event.event_type().unwrap(), EdgeKind::Falling); + assert_eq!(event.line_offset(), GPIO); + + // No events available + assert!(!config + .request() + .wait_edge_event(Some(Duration::from_millis(100))) + .unwrap()); + + assert!(ts_falling > ts_rising); + } + + #[test] + fn rising_edge() { + const GPIO: Offset = 6; + let mut buf = 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()); + + assert_eq!(config.request().read_edge_events(&mut buf).unwrap(), 1); + assert_eq!(buf.len(), 1); + + let event = buf.event(0).unwrap(); + assert_eq!(event.event_type().unwrap(), EdgeKind::Rising); + assert_eq!(event.line_offset(), GPIO); + + // No events available + assert!(!config + .request() + .wait_edge_event(Some(Duration::from_millis(100))) + .unwrap()); + } + + #[test] + fn falling_edge() { + const GPIO: Offset = 7; + let mut buf = 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()); + + assert_eq!(config.request().read_edge_events(&mut buf).unwrap(), 1); + assert_eq!(buf.len(), 1); + + let event = buf.event(0).unwrap(); + assert_eq!(event.event_type().unwrap(), EdgeKind::Falling); + assert_eq!(event.line_offset(), GPIO); + + // No events available + assert!(!config + .request() + .wait_edge_event(Some(Duration::from_millis(100))) + .unwrap()); + } + + #[test] + fn edge_sequence() { + const GPIO: [u32; 2] = [0, 1]; + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_edge(None, Some(Edge::Both)); + config.lconfig_add_settings(&GPIO); + config.request_lines().unwrap(); + + // Generate events + trigger_rising_edge_events_on_two_offsets(config.sim(), GPIO); + + // Rising event GPIO 0 + let mut buf = request::Buffer::new(0).unwrap(); + assert!(config + .request() + .wait_edge_event(Some(Duration::from_secs(1))) + .unwrap()); + + assert_eq!(config.request().read_edge_events(&mut buf).unwrap(), 1); + assert_eq!(buf.len(), 1); + + let event = buf.event(0).unwrap(); + assert_eq!(event.event_type().unwrap(), EdgeKind::Rising); + assert_eq!(event.line_offset(), GPIO[0]); + assert_eq!(event.global_seqno(), 1); + assert_eq!(event.line_seqno(), 1); + drop(event); + + // Rising event GPIO 1 + assert!(config + .request() + .wait_edge_event(Some(Duration::from_secs(1))) + .unwrap()); + + assert_eq!(config.request().read_edge_events(&mut buf).unwrap(), 1); + assert_eq!(buf.len(), 1); + + let event = buf.event(0).unwrap(); + assert_eq!(event.event_type().unwrap(), EdgeKind::Rising); + assert_eq!(event.line_offset(), GPIO[1]); + assert_eq!(event.global_seqno(), 2); + assert_eq!(event.line_seqno(), 1); + + // No events available + assert!(!config + .request() + .wait_edge_event(Some(Duration::from_millis(100))) + .unwrap()); + } + + #[test] + fn multiple_events() { + const GPIO: Offset = 1; + let mut buf = 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()); + + assert_eq!(config.request().read_edge_events(&mut buf).unwrap(), 3); + assert_eq!(buf.len(), 3); + + let mut global_seqno = 1; + let mut line_seqno = 1; + + // Verify sequence number of events + for i in 0..3 { + let event = buf.event(i).unwrap(); + assert_eq!(event.line_offset(), GPIO); + assert_eq!(event.global_seqno(), global_seqno); + assert_eq!(event.line_seqno(), line_seqno); + + global_seqno += 1; + line_seqno += 1; + } + } + + #[test] + fn over_capacity() { + const GPIO: Offset = 2; + let mut buf = 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()); + + assert_eq!(config.request().read_edge_events(&mut buf).unwrap(), 2); + assert_eq!(buf.len(), 2); + } + } +} diff --git a/bindings/rust/libgpiod/tests/info_event.rs b/bindings/rust/libgpiod/tests/info_event.rs new file mode 100644 index 000000000000..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 Oct 31 11:47:16 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 620719 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 EE648FA3742 for ; Mon, 31 Oct 2022 11:47:59 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230462AbiJaLr6 (ORCPT ); Mon, 31 Oct 2022 07:47:58 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46608 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230492AbiJaLrz (ORCPT ); Mon, 31 Oct 2022 07:47:55 -0400 Received: from mail-pl1-x62d.google.com (mail-pl1-x62d.google.com [IPv6:2607:f8b0:4864:20::62d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7822FEE14 for ; Mon, 31 Oct 2022 04:47:53 -0700 (PDT) Received: by mail-pl1-x62d.google.com with SMTP id u6so10480601plq.12 for ; Mon, 31 Oct 2022 04:47:53 -0700 (PDT) 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=CvPzxJ38CUPkVYVAssYdZNkY/+dJfoZPwufMsKM5FhVghmfCScRybqr3noA65WLB4L ZtcUM1fA7OeeRedLhE7GDJ13YvqTE1s6whgMCPSNtlxGy8eLwSJaphOmziys6GgJLvo5 e//HLr7iUB1nxB7JBR4V6R6/bw2dWmd+whf51ZBEJApZCFVa+Uedp8tcAupP0LStjlP5 U9CaTdnwR4Jn+8mYWnWJhB30p+0A7cNX1+rkI2maQL7UrN14nsfT1ExvaSNz+Hwko84w eUxJcVBmKGRgs8TxgCXngKAo3yoHKSCRRQwuNd64XTOto7zRgA+DPV9WFUcoUGB79xMI o/Ug== 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=OGohpCzhHje2v81ozkuQpgKKTHvjAYaaQ7gYM5NRzRm4AwgQRx/LxMUNsSXuSmdcaE 9+XNH6MRfIzF2kC9L3KWHwg8pVRZ3NIkRVjUs2yRqHVUlSk9mThrnEFo8EeRB4mVvbHd GQdwW1P4MGgJwjDx/QNSj/nxPGRr3CqUxAj1NsUQWwbUMzRfbgprFnUYXeMN0VkZ/r79 20RaGQynic/onMzdu6rD5DLd44d6TbN7HVK2TAlnjd7oHPB1CpfS2p+q/OnfR/iGH4Bl HimlBoBDKd+FU1ByInaqpMIqZpVhdLQ11CY4h8LIRrz62udPNCdjPG4fExbcdFkkmtTk OPhw== X-Gm-Message-State: ACrzQf1DA2OfUzt56h3sM1oEYTpvLKFcvRnX12bajOrbCS31NKpBbhXu CUzs2Eqqh/899ZfMe9jfAJikMw== X-Google-Smtp-Source: AMsMyM7m5RlZpHVSLgEZ2pLoDbj43yh3fZNhx2bFhm3kogjwQ9cNtgDbkmmjeILLGdFQnF0Hk96uxA== X-Received: by 2002:a17:90a:b904:b0:213:f5ec:3915 with SMTP id p4-20020a17090ab90400b00213f5ec3915mr1886227pjr.33.1667216872922; Mon, 31 Oct 2022 04:47:52 -0700 (PDT) Received: from localhost ([122.172.84.80]) by smtp.gmail.com with ESMTPSA id x5-20020a170902ec8500b00187022627d8sm4311699plg.62.2022.10.31.04.47.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 31 Oct 2022 04:47:52 -0700 (PDT) From: Viresh Kumar To: Linus Walleij , Bartosz Golaszewski Cc: Viresh Kumar , Vincent Guittot , linux-gpio@vger.kernel.org, Kent Gibson , Miguel Ojeda , Wedson Almeida Filho , =?utf-8?q?Alex_Benn=C3=A9e?= , stratos-dev@op-lists.linaro.org, Gerard Ryan , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , y86-dev Subject: [libgpiod v2][PATCH V8 8/9] bindings: rust: Integrate building of bindings with make Date: Mon, 31 Oct 2022 17:17:16 +0530 Message-Id: <97c5f431327191c4a222c7fc0f667c8be4004622.1667215380.git.viresh.kumar@linaro.org> X-Mailer: git-send-email 2.31.1.272.g89b43f80a514 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org Lets make build rust bindings as well. Signed-off-by: Viresh Kumar --- README | 8 +++++--- TODO | 8 -------- bindings/Makefile.am | 6 ++++++ bindings/rust/Makefile.am | 18 ++++++++++++++++++ configure.ac | 16 ++++++++++++++++ 5 files changed, 45 insertions(+), 11 deletions(-) create mode 100644 bindings/rust/Makefile.am diff --git a/README b/README index 814a0f161fd2..68b5d69f9b66 100644 --- a/README +++ b/README @@ -119,9 +119,9 @@ TOOLS BINDINGS -------- -High-level, object-oriented bindings for C++ and python3 are provided. They -can be enabled by passing --enable-bindings-cxx and --enable-bindings-python -arguments respectively to configure. +High-level, object-oriented bindings for C++, python3 and Rust are provided. +They can be enabled by passing --enable-bindings-cxx, --enable-bindings-python +and --enable-bindings-rust arguments respectively to configure. C++ bindings require C++11 support and autoconf-archive collection if building from git. @@ -132,6 +132,8 @@ the PYTHON_CPPFLAGS and PYTHON_LIBS variables in order to point the build system to the correct locations. During native builds, the configure script can auto-detect the location of the development files. +Rust bindings require cargo support. + TESTING ------- diff --git a/TODO b/TODO index 8bb4d8f3ad56..cf4fd7b4a962 100644 --- a/TODO +++ b/TODO @@ -28,14 +28,6 @@ and is partially functional. ---------- -* implement rust bindings - -With Rust gaining popularity as a low-level system's language and the -possibility of it making its way into the linux kernel, it's probably time to -provide Rust bindings to libgpiod as part of the project. - ----------- - * implement a simple daemon for controlling GPIOs in C together with a client program diff --git a/bindings/Makefile.am b/bindings/Makefile.am index 8f8c762f254f..004ae23dbc58 100644 --- a/bindings/Makefile.am +++ b/bindings/Makefile.am @@ -14,3 +14,9 @@ if WITH_BINDINGS_PYTHON SUBDIRS += python endif + +if WITH_BINDINGS_RUST + +SUBDIRS += rust + +endif diff --git a/bindings/rust/Makefile.am b/bindings/rust/Makefile.am new file mode 100644 index 000000000000..79a52bc691ae --- /dev/null +++ b/bindings/rust/Makefile.am @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# SPDX-FileCopyrightText: 2022 Viresh Kumar + +command = cargo build --release --lib + +if WITH_TESTS +command += --tests +endif + +if WITH_EXAMPLES +command += --examples +endif + +all: + $(command) + +clean: + cargo clean diff --git a/configure.ac b/configure.ac index 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 From patchwork Mon Oct 31 11:47:17 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 620352 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 482BBFA3744 for ; Mon, 31 Oct 2022 11:48:01 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230492AbiJaLsA (ORCPT ); Mon, 31 Oct 2022 07:48:00 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46668 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230481AbiJaLr6 (ORCPT ); Mon, 31 Oct 2022 07:47:58 -0400 Received: from mail-pf1-x42d.google.com (mail-pf1-x42d.google.com [IPv6:2607:f8b0:4864:20::42d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id B8D5E203 for ; Mon, 31 Oct 2022 04:47:56 -0700 (PDT) Received: by mail-pf1-x42d.google.com with SMTP id f140so10478254pfa.1 for ; Mon, 31 Oct 2022 04:47:56 -0700 (PDT) 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=0MVCfi3lWu1Om23SOS6ps2Bv8bFkn/B3WyCcptsWJJo=; b=Ycc8bsp2gpLT5OlNa2PHaQuWvUcRAiwAsdQeoGbc+FJUlmNaqKvTDciQMAUFhU2x5F 4CZz3zs4QLKjmRiAx4nXkmyeYOtBQY6nZbpfVSlQY+FyBNJSetEzSx8/yiuyKJxcO/gO MHABKmmPe90U/JvKNYR8xH9iKfXozNw+iYH1GLfJjTcFfx2mBCO0OjifiTtsqSFoIMPp GveqInWD6+26y8srz/tE8XToG2qSwfiTX1BKwc3UMZKGhTk36n/CP5BaH9diF8ZZwD+x Mv6WZ7XR0alBi4vJ08pNyLUopL6SuLFDGk4Vtp0in45PZM4fujbBXClI8WPHPVK27vU8 CTkA== 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=0MVCfi3lWu1Om23SOS6ps2Bv8bFkn/B3WyCcptsWJJo=; b=XLpo41CPomHMRgw3LxrtOArXG0LhMEtZ8iOKYiLNKmkk2Cqni8/lTgbE024Pi3Hh27 fitNJUyxRu/8GihAsms4+nqnAI5bVMRZOKBUts2mmf+6ZHewnr54QM3uzWuTAUfVF0TP 05jNDNvskQS/bOZ+EjsElzXgo1QuyDEnDT+ZMVKWJo9gdDw64AkjwU+Ez5W62zvN7Ij0 +vWNlvXwc+yHZhXW8eZwQSD5yXCp23CoMnlLtDh1Jnq/23ITLLy34o6fiMetlurftsR6 GOKfCtrsIp6r4xQdM3ke0nrprLiu8kbXIFnrjw4vBNIuyc63BE4zh5pdkB51y7csi8Uu tx5Q== X-Gm-Message-State: ACrzQf0p2kno3i3580PN9G8uQEZKlk2wDzwwb1IpC+5yHsY2o1l/g3Iu F1nAGXpiSDSZXbeov7O6y7KvTw== X-Google-Smtp-Source: AMsMyM5qW0hV2b2c+p+/chSAPOKYCdwCaBuw7vilDP0RjKTAkfa9WQ/fE22/tLE75fmi82AYoA9clQ== X-Received: by 2002:a62:cd4b:0:b0:56d:5b06:c88b with SMTP id o72-20020a62cd4b000000b0056d5b06c88bmr6578800pfg.78.1667216876126; Mon, 31 Oct 2022 04:47:56 -0700 (PDT) Received: from localhost ([122.172.84.80]) by smtp.gmail.com with ESMTPSA id b14-20020aa7950e000000b00560a25fae1fsm4387414pfp.206.2022.10.31.04.47.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 31 Oct 2022 04:47:55 -0700 (PDT) From: Viresh Kumar To: Linus Walleij , Bartosz Golaszewski Cc: Viresh Kumar , Vincent Guittot , linux-gpio@vger.kernel.org, Kent Gibson , Miguel Ojeda , Wedson Almeida Filho , =?utf-8?q?Alex_Benn=C3=A9e?= , stratos-dev@op-lists.linaro.org, Gerard Ryan , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , y86-dev Subject: [libgpiod v2][PATCH V8 9/9] bindings: rust: Implement iterator for edge events Date: Mon, 31 Oct 2022 17:17:17 +0530 Message-Id: <4082a496a1da3e3c11b08a360f5ba7f7d70f9719.1667215380.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 It would be much more convenient for the user to iterate over the events in the Rust idiomatic way (i.e. "for event in events"), instead of indexing into the vector. This also lets us drop the hard requirement of explicitly dropping the event before reading new events from the buffer again. Suggested-by: Kent Gibson Signed-off-by: Viresh Kumar --- .../rust/libgpiod/examples/gpio_events.rs | 47 +++---- bindings/rust/libgpiod/examples/gpiomon.rs | 5 +- bindings/rust/libgpiod/src/edge_event.rs | 56 +++------ bindings/rust/libgpiod/src/event_buffer.rs | 119 ++++++++++++++---- bindings/rust/libgpiod/src/lib.rs | 2 + bindings/rust/libgpiod/src/line_request.rs | 5 +- bindings/rust/libgpiod/tests/edge_event.rs | 47 ++++--- 7 files changed, 172 insertions(+), 109 deletions(-) diff --git a/bindings/rust/libgpiod/examples/gpio_events.rs b/bindings/rust/libgpiod/examples/gpio_events.rs index 77a7d2a5faa1..57dd5b4db546 100644 --- a/bindings/rust/libgpiod/examples/gpio_events.rs +++ b/bindings/rust/libgpiod/examples/gpio_events.rs @@ -59,30 +59,31 @@ fn main() -> Result<()> { Ok(true) => (), } - let count = request.read_edge_events(&mut buffer)?; - if count == 1 { - let event = buffer.event(0)?; - let cloned_event = request::Event::event_clone(&event)?; - - // This is required before reading events again into the buffer. - drop(event); + let events = request.read_edge_events(&mut buffer)?; + for event in events { + let cloned_event = request::Event::event_clone(event)?; + println!( + "line: {} type: {:?}, time: {:?}", + cloned_event.line_offset(), + cloned_event.event_type(), + cloned_event.timestamp() + ); + println!( + "line: {} type: {:?}, time: {:?}", + event.line_offset(), + event.event_type(), + event.timestamp() + ); + } - let count = request.read_edge_events(&mut buffer)?; - if count == 1 { - let event = buffer.event(0)?; - println!( - "line: {} type: {:?}, time: {:?}", - cloned_event.line_offset(), - cloned_event.event_type(), - cloned_event.timestamp() - ); - println!( - "line: {} type: {:?}, time: {:?}", - event.line_offset(), - event.event_type(), - event.timestamp() - ); - } + let events = request.read_edge_events(&mut buffer)?; + for event in events { + println!( + "line: {} type: {:?}, time: {:?}", + event.line_offset(), + event.event_type(), + event.timestamp() + ); } } } diff --git a/bindings/rust/libgpiod/examples/gpiomon.rs b/bindings/rust/libgpiod/examples/gpiomon.rs index a03ea31dfd53..4dea5d1c8dd7 100644 --- a/bindings/rust/libgpiod/examples/gpiomon.rs +++ b/bindings/rust/libgpiod/examples/gpiomon.rs @@ -58,9 +58,8 @@ fn main() -> Result<()> { Ok(true) => (), } - let count = request.read_edge_events(&mut buffer)?; - if count == 1 { - let event = buffer.event(0)?; + let events = request.read_edge_events(&mut buffer)?; + for event in events { println!( "line: {} type: {}, time: {:?}", event.line_offset(), diff --git a/bindings/rust/libgpiod/src/edge_event.rs b/bindings/rust/libgpiod/src/edge_event.rs index 95b05e947d26..dc71efcb2396 100644 --- a/bindings/rust/libgpiod/src/edge_event.rs +++ b/bindings/rust/libgpiod/src/edge_event.rs @@ -8,7 +8,6 @@ use std::time::Duration; use super::{ gpiod, line::{EdgeKind, Offset}, - request::Buffer, Error, OperationType, Result, }; @@ -24,29 +23,33 @@ use super::{ /// number of events are being read. #[derive(Debug, Eq, PartialEq)] -pub struct Event<'b> { - buffer: Option<&'b Buffer>, - event: *mut gpiod::gpiod_edge_event, +pub struct Event { + pub(crate) event: *mut gpiod::gpiod_edge_event, + pub(crate) cloned: bool, } -impl<'b> Event<'b> { +impl Event { /// Get an event stored in the buffer. - pub(crate) fn new(buffer: &'b Buffer, index: usize) -> Result> { - // 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(buffer.buffer, index.try_into().unwrap()) - }; + pub(crate) fn new(event: *mut gpiod::gpiod_edge_event) -> Event { + Self { + event, + cloned: false, + } + } + + 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.event) }; if event.is_null() { return Err(Error::OperationFailed( - OperationType::EdgeEventBufferGetEvent, + OperationType::EdgeEventCopy, errno::errno(), )); } Ok(Self { - buffer: Some(buffer), event, + cloned: true, }) } @@ -95,32 +98,11 @@ impl<'b> Event<'b> { } } -impl<'e, 'b> Event<'e> { - pub fn event_clone(event: &Event<'b>) -> Result> - where - 'e: 'b, - { - // SAFETY: `gpiod_edge_event` is guaranteed to be valid here. - let event = unsafe { gpiod::gpiod_edge_event_copy(event.event) }; - if event.is_null() { - return Err(Error::OperationFailed( - OperationType::EdgeEventCopy, - errno::errno(), - )); - } - - Ok(Self { - buffer: None, - event, - }) - } -} - -impl<'b> Drop for Event<'b> { +impl Drop for Event { /// Free the edge event. fn drop(&mut self) { - // Free the event only if a copy is made - if self.buffer.is_none() { + // Only free the cloned event, others are freed with the buffer. + if self.cloned { // SAFETY: `gpiod_edge_event` is guaranteed to be valid here. unsafe { gpiod::gpiod_edge_event_free(self.event) }; } diff --git a/bindings/rust/libgpiod/src/event_buffer.rs b/bindings/rust/libgpiod/src/event_buffer.rs index 16d7022034df..d1bfac54070c 100644 --- a/bindings/rust/libgpiod/src/event_buffer.rs +++ b/bindings/rust/libgpiod/src/event_buffer.rs @@ -4,14 +4,74 @@ // Viresh Kumar use std::os::raw::c_ulong; +use std::ptr; -use super::{gpiod, request, Error, OperationType, Result}; +use super::{ + gpiod, + request::{Event, Request}, + Error, OperationType, Result, +}; + +pub struct Events<'a> { + buffer: &'a mut Buffer, + events: Vec<&'a Event>, + index: usize, +} + +impl<'a> Events<'a> { + pub fn new(buffer: &'a mut Buffer, len: usize) -> Self { + let mut events = Vec::new(); + + for i in 0..len { + // SAFETY: We could have simply pushed the reference of the event to the events vector + // here, but that causes a build error as buffer is borrowed as both mutable and + // immutable. Instead of a reference, perform unsafe pointer magic to do the same + // thing. It is safe as the events buffer will always outlive Events and the reference + // will always be valid. + events.push(unsafe { + (buffer.events.as_ptr().add(i) as *const Event) + .as_ref() + .unwrap() + }); + } + + Self { + buffer, + events, + index: 0, + } + } + + /// Get the number of events the buffer contains. + pub fn len(&self) -> usize { + self.events.len() + } + + /// Check if buffer is empty. + pub fn is_empty(&self) -> bool { + self.events.is_empty() + } +} + +impl<'a> Iterator for Events<'a> { + type Item = &'a Event; + + fn next(&mut self) -> Option { + if self.events.is_empty() || self.buffer.update_event(self.index).is_err() { + return None; + } + + self.index += 1; + Some(self.events.remove(0)) + } +} /// Line edge events buffer #[derive(Debug, Eq, PartialEq)] pub struct Buffer { pub(crate) buffer: *mut gpiod::gpiod_edge_event_buffer, - event_count: usize, + events: Vec, + capacity: usize, } impl Buffer { @@ -30,22 +90,32 @@ impl Buffer { )); } + // SAFETY: `gpiod_edge_event_buffer` is guaranteed to be valid here. + let capacity = unsafe { gpiod::gpiod_edge_event_buffer_get_capacity(buffer) as usize }; + let mut events = Vec::new(); + events.resize_with(capacity, || Event::new(ptr::null_mut())); + Ok(Self { buffer, - event_count: 0, + events, + capacity, }) } - /// Get a number of edge events from a line request. + /// 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::Request) -> Result { + pub fn read_edge_events(&mut self, request: &Request) -> Result { + for i in 0..self.events.len() { + self.events[i].event = 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.capacity().try_into().unwrap(), + self.capacity.try_into().unwrap(), ) }; @@ -55,30 +125,37 @@ impl Buffer { errno::errno(), )) } else { - // Set count of events read in the buffer - self.set_event_count(ret as usize); - Ok(ret as u32) - } - } + let ret = ret as usize; - /// Set the number of events read into the event buffer. - pub(crate) fn set_event_count(&mut self, count: usize) { - self.event_count = count + if ret > self.capacity { + Err(Error::TooManyEvents(ret, self.capacity)) + } else { + Ok(Events::new(self, ret)) + } + } } /// Get the capacity of the event buffer. pub fn capacity(&self) -> usize { - // SAFETY: `gpiod_edge_event_buffer` is guaranteed to be valid here. - unsafe { gpiod::gpiod_edge_event_buffer_get_capacity(self.buffer) as usize } + self.capacity } /// Read an event stored in the buffer. - pub fn event(&self, index: usize) -> Result { - if index >= self.event_count { - Err(Error::InvalidArguments) - } else { - request::Event::new(self, index) + fn update_event(&mut self, index: usize) -> Result<()> { + // 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 = event; + Ok(()) } /// Get the number of events the buffer contains. diff --git a/bindings/rust/libgpiod/src/lib.rs b/bindings/rust/libgpiod/src/lib.rs index 5452d47d51bc..3bcd3b97647c 100644 --- a/bindings/rust/libgpiod/src/lib.rs +++ b/bindings/rust/libgpiod/src/lib.rs @@ -103,6 +103,8 @@ pub enum Error { 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, } diff --git a/bindings/rust/libgpiod/src/line_request.rs b/bindings/rust/libgpiod/src/line_request.rs index 10d2197b876a..7101d098b6f5 100644 --- a/bindings/rust/libgpiod/src/line_request.rs +++ b/bindings/rust/libgpiod/src/line_request.rs @@ -202,7 +202,10 @@ impl Request { /// Get a number of edge events from a line request. /// /// This function will block if no event was queued for the line. - pub fn read_edge_events(&self, buffer: &mut request::Buffer) -> Result { + pub fn read_edge_events<'a>( + &'a self, + buffer: &'a mut request::Buffer, + ) -> Result { buffer.read_edge_events(self) } } diff --git a/bindings/rust/libgpiod/tests/edge_event.rs b/bindings/rust/libgpiod/tests/edge_event.rs index dd95f6d82caa..ac32f7b2ba2c 100644 --- a/bindings/rust/libgpiod/tests/edge_event.rs +++ b/bindings/rust/libgpiod/tests/edge_event.rs @@ -93,10 +93,10 @@ mod edge_event { .wait_edge_event(Some(Duration::from_secs(1))) .unwrap()); - assert_eq!(config.request().read_edge_events(&mut buf).unwrap(), 1); - assert_eq!(buf.len(), 1); + let mut events = config.request().read_edge_events(&mut buf).unwrap(); + assert_eq!(events.len(), 1); - let event = buf.event(0).unwrap(); + let event = events.next().unwrap(); let ts_rising = event.timestamp(); assert_eq!(event.event_type().unwrap(), EdgeKind::Rising); assert_eq!(event.line_offset(), GPIO); @@ -108,10 +108,10 @@ mod edge_event { .wait_edge_event(Some(Duration::from_secs(1))) .unwrap()); - assert_eq!(config.request().read_edge_events(&mut buf).unwrap(), 1); - assert_eq!(buf.len(), 1); + let mut events = config.request().read_edge_events(&mut buf).unwrap(); + assert_eq!(events.len(), 1); - let event = buf.event(0).unwrap(); + let event = events.next().unwrap(); let ts_falling = event.timestamp(); assert_eq!(event.event_type().unwrap(), EdgeKind::Falling); assert_eq!(event.line_offset(), GPIO); @@ -143,10 +143,10 @@ mod edge_event { .wait_edge_event(Some(Duration::from_secs(1))) .unwrap()); - assert_eq!(config.request().read_edge_events(&mut buf).unwrap(), 1); - assert_eq!(buf.len(), 1); + let mut events = config.request().read_edge_events(&mut buf).unwrap(); + assert_eq!(events.len(), 1); - let event = buf.event(0).unwrap(); + let event = events.next().unwrap(); assert_eq!(event.event_type().unwrap(), EdgeKind::Rising); assert_eq!(event.line_offset(), GPIO); @@ -175,10 +175,10 @@ mod edge_event { .wait_edge_event(Some(Duration::from_secs(1))) .unwrap()); - assert_eq!(config.request().read_edge_events(&mut buf).unwrap(), 1); - assert_eq!(buf.len(), 1); + let mut events = config.request().read_edge_events(&mut buf).unwrap(); + assert_eq!(events.len(), 1); - let event = buf.event(0).unwrap(); + let event = events.next().unwrap(); assert_eq!(event.event_type().unwrap(), EdgeKind::Falling); assert_eq!(event.line_offset(), GPIO); @@ -207,10 +207,10 @@ mod edge_event { .wait_edge_event(Some(Duration::from_secs(1))) .unwrap()); - assert_eq!(config.request().read_edge_events(&mut buf).unwrap(), 1); - assert_eq!(buf.len(), 1); + let mut events = config.request().read_edge_events(&mut buf).unwrap(); + assert_eq!(events.len(), 1); - let event = buf.event(0).unwrap(); + let event = events.next().unwrap(); assert_eq!(event.event_type().unwrap(), EdgeKind::Rising); assert_eq!(event.line_offset(), GPIO[0]); assert_eq!(event.global_seqno(), 1); @@ -223,10 +223,10 @@ mod edge_event { .wait_edge_event(Some(Duration::from_secs(1))) .unwrap()); - assert_eq!(config.request().read_edge_events(&mut buf).unwrap(), 1); - assert_eq!(buf.len(), 1); + let mut events = config.request().read_edge_events(&mut buf).unwrap(); + assert_eq!(events.len(), 1); - let event = buf.event(0).unwrap(); + let event = events.next().unwrap(); assert_eq!(event.event_type().unwrap(), EdgeKind::Rising); assert_eq!(event.line_offset(), GPIO[1]); assert_eq!(event.global_seqno(), 2); @@ -257,15 +257,14 @@ mod edge_event { .wait_edge_event(Some(Duration::from_secs(1))) .unwrap()); - assert_eq!(config.request().read_edge_events(&mut buf).unwrap(), 3); - assert_eq!(buf.len(), 3); + let 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 i in 0..3 { - let event = buf.event(i).unwrap(); + for event in events { assert_eq!(event.line_offset(), GPIO); assert_eq!(event.global_seqno(), global_seqno); assert_eq!(event.line_seqno(), line_seqno); @@ -293,8 +292,8 @@ mod edge_event { .wait_edge_event(Some(Duration::from_secs(1))) .unwrap()); - assert_eq!(config.request().read_edge_events(&mut buf).unwrap(), 2); - assert_eq!(buf.len(), 2); + let events = config.request().read_edge_events(&mut buf).unwrap(); + assert_eq!(events.len(), 2); } } }