From patchwork Fri Oct 14 10:47:18 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 615145 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 8C825C433FE for ; Fri, 14 Oct 2022 10:47:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229537AbiJNKrh (ORCPT ); Fri, 14 Oct 2022 06:47:37 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47444 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229607AbiJNKrd (ORCPT ); Fri, 14 Oct 2022 06:47:33 -0400 Received: from mail-pj1-x102e.google.com (mail-pj1-x102e.google.com [IPv6:2607:f8b0:4864:20::102e]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 16480160841 for ; Fri, 14 Oct 2022 03:47:33 -0700 (PDT) Received: by mail-pj1-x102e.google.com with SMTP id t10-20020a17090a4e4a00b0020af4bcae10so4429622pjl.3 for ; Fri, 14 Oct 2022 03:47:33 -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=LFTqU4xvWm4ZVAWqCXbZIt73vYr5gjNJa4wLaZGnjzI=; b=Tu4hvz2tGAiKPI328/eHSeUTdYozFykAxF2M6RZRERTy3YcFSmwOlbFbiEbJyErgC2 G+ZayU8FDS1ptLe2FEN8eaO9Ibg960GqwX37K/vKN98KzXHK3pbGLwn+fhFQiNKn3Y2F rXxplX8RGPEW0LVXX9NDpklyz3P93yOvXM4otSEuefiCzxhcnfYO3XBHSqmtAwstleEN m0OGggaJ+1jnDJC98pNFT5B7Mlu2NVZ2bwgWepFbEHdiSrziaGf6SnyA6baEYHtocpN2 5gBeluHH0XhiXrYdZMpZL2aJDngVuf4rkp2YtbULf25WhQKDupV5CJF/ra8SDrLTzGF0 VxXQ== 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=LFTqU4xvWm4ZVAWqCXbZIt73vYr5gjNJa4wLaZGnjzI=; b=OK9xyLNiHi1oA4A7GuYmQErs9vlVaerYPLi/sXZlWI/6henF106sGh7B3339MYfrC5 Te3+ZJiPj4w+Xzv3ChkSkOt/KmgCQKWWNW18TAZjWcNeraoYNvY/gcRvlw93xM7XiCVu wCmRyVvQ8tfDX9jrjGqFA7TXzHKx2pd3wgMaPGwiINR/NtVnjOsdRvPePKsoVWQh6JSr UiROj/frd97j9ioEZe+tS9VF9Kbd4IOKGM0no4l9Mla8k14abW011hbonqDceQKyBSxC 9YivHUwS8RGb84SctGmvdgH2YYYymZGI7XR3tMXQ42wq45DCsMFipS0LvRiUhEe88Ire CXXA== X-Gm-Message-State: ACrzQf398ee/VnodlbA58EDGqhvj5XEJfrJHivGZK5PQR0Tlr7KatzNW N5YR25f7SkLjcSE5n4QCHh6VfQ== X-Google-Smtp-Source: AMsMyM4xwHGTv7ezCB4tGBEk9m3Ejb9GkstvNZcIbM+DFVK9Eb0j7S3FZHrcklmv52qdywS5cSMunw== X-Received: by 2002:a17:903:40c3:b0:180:cfb7:827b with SMTP id t3-20020a17090340c300b00180cfb7827bmr4740429pld.0.1665744452601; Fri, 14 Oct 2022 03:47:32 -0700 (PDT) Received: from localhost ([122.172.86.128]) by smtp.gmail.com with ESMTPSA id s21-20020a170903201500b0016c9e5f290esm1440859pla.10.2022.10.14.03.47.31 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 14 Oct 2022 03:47:31 -0700 (PDT) From: Viresh Kumar To: Linus Walleij , Bartosz Golaszewski Cc: Viresh Kumar , Vincent Guittot , linux-gpio@vger.kernel.org, Kent Gibson , Miguel Ojeda , Wedson Almeida Filho , =?utf-8?q?Alex_Benn=C3=A9e?= , stratos-dev@op-lists.linaro.org, Gerard Ryan Subject: [PATCH V7 1/8] libgpiod: Add libgpiod-sys rust crate Date: Fri, 14 Oct 2022 16:17:18 +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 libgpiod-sys rust crate, which provides FFI (foreign function interface) bindings for libgpiod APIs. Signed-off-by: Viresh Kumar --- .gitignore | 5 ++++ bindings/rust/Cargo.toml | 5 ++++ bindings/rust/libgpiod-sys/Cargo.toml | 15 ++++++++++ bindings/rust/libgpiod-sys/build.rs | 41 +++++++++++++++++++++++++++ bindings/rust/libgpiod-sys/src/lib.rs | 13 +++++++++ 5 files changed, 79 insertions(+) create mode 100644 bindings/rust/Cargo.toml create mode 100644 bindings/rust/libgpiod-sys/Cargo.toml create mode 100644 bindings/rust/libgpiod-sys/build.rs create mode 100644 bindings/rust/libgpiod-sys/src/lib.rs diff --git a/.gitignore b/.gitignore index 6c08415b390d..9f2fcf440c5d 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,8 @@ stamp-h1 # profiling *.gcda *.gcno + +# Added by cargo + +target +Cargo.lock diff --git a/bindings/rust/Cargo.toml b/bindings/rust/Cargo.toml new file mode 100644 index 000000000000..d0b3a3c88ff1 --- /dev/null +++ b/bindings/rust/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] + +members = [ + "libgpiod-sys", +] diff --git a/bindings/rust/libgpiod-sys/Cargo.toml b/bindings/rust/libgpiod-sys/Cargo.toml new file mode 100644 index 000000000000..77f82719d269 --- /dev/null +++ b/bindings/rust/libgpiod-sys/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "libgpiod-sys" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[features] +generate = [ "bindgen" ] + +[build-dependencies] +bindgen = { version = "0.59.1", optional = true } +cc = "1.0.46" diff --git a/bindings/rust/libgpiod-sys/build.rs b/bindings/rust/libgpiod-sys/build.rs new file mode 100644 index 000000000000..98863686c7af --- /dev/null +++ b/bindings/rust/libgpiod-sys/build.rs @@ -0,0 +1,41 @@ +#[cfg(feature = "generate")] +extern crate bindgen; +#[cfg(feature = "generate")] +use std::env; +#[cfg(feature = "generate")] +use std::path::PathBuf; + +#[cfg(feature = "generate")] +fn generate_bindings() { + // Tell cargo to invalidate the built crate whenever following files change + println!("cargo:rerun-if-changed=../../../include/gpiod.h"); + + // The bindgen::Builder is the main entry point + // to bindgen, and lets you build up options for + // the resulting bindings. + let bindings = bindgen::Builder::default() + // The input header we would like to generate + // bindings for. + .header("../../../include/gpiod.h") + // Tell cargo to invalidate the built crate whenever any of the + // included header files changed. + .parse_callbacks(Box::new(bindgen::CargoCallbacks)) + // Finish the builder and generate the bindings. + .generate() + // Unwrap the Result and panic on failure. + .expect("Unable to generate bindings"); + + // Write the bindings to the $OUT_DIR/bindings.rs file. + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Couldn't write bindings!"); +} + +fn main() { + #[cfg(feature = "generate")] + generate_bindings(); + + println!("cargo:rustc-link-search=./../../lib/.libs/"); + println!("cargo:rustc-link-lib=static=gpiod"); +} diff --git a/bindings/rust/libgpiod-sys/src/lib.rs b/bindings/rust/libgpiod-sys/src/lib.rs new file mode 100644 index 000000000000..1ca355b5f5ac --- /dev/null +++ b/bindings/rust/libgpiod-sys/src/lib.rs @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0 + +#[allow(non_camel_case_types, non_upper_case_globals)] +#[cfg_attr(test, allow(deref_nullptr, non_snake_case))] + +mod bindings_raw { + #[cfg(feature = "generate")] + include!(concat!(env!("OUT_DIR"), "/bindings.rs")); + + #[cfg(not(feature = "generate"))] + include!("bindings.rs"); +} +pub use bindings_raw::*; From patchwork Fri Oct 14 10:47:19 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 615144 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 BF8C4C4332F for ; Fri, 14 Oct 2022 10:47:42 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229607AbiJNKrl (ORCPT ); Fri, 14 Oct 2022 06:47:41 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47480 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229587AbiJNKrk (ORCPT ); Fri, 14 Oct 2022 06:47:40 -0400 Received: from mail-pl1-x634.google.com (mail-pl1-x634.google.com [IPv6:2607:f8b0:4864:20::634]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7184526F6 for ; Fri, 14 Oct 2022 03:47:37 -0700 (PDT) Received: by mail-pl1-x634.google.com with SMTP id l4so4370347plb.8 for ; Fri, 14 Oct 2022 03:47:37 -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=MXSDt8h0qcyGzZ9s/lmCjfP476IaAhYQsGlcxZjzERDJQIvCiyYzhBo4eqLtmJ47rn zxnuF8JtQWto6yNGcuXiETeBxwvJ9nKn6vaMqpFxWKhITo+mdrrusN/SrcFrJfp8DBOy MomFIx1Oeq5tdtMXmVV5GO0zmK3+M0bTk3ArBEt/jIENgOd85Ynajh6YbTa+fI9V8XT6 wvSxQ10dCYteZ4nd4jNPFlfCGxvXRlpx4y2WidrFizWv3lUgGxUCOum5MacT7U47O08p xZP4FbqeetAcRg1EurCTIa/PmYCap7nIhHoViaKW5Zbu2ftZSjc3AuFmqxCPIsOrOTmG rfgg== 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=X+5nFeV0k91JIrUzxCLMSPY5O0zzzONmc5RcI1fc7Bibc/3DewZuoazpu7YKTlX8Fh mrfIzXWuEUfvlG5UievEf5IFMKxrNtIrL4CfBbSPvkUroB1JBgpKtGmzYIoJIDngj1xX 6w9bV47tjXnZkBjsqrBzlV5+VuH4S4ggliD8vig/h7M1eg7DcZBENUikm+E7El6LgG+f 984clGld3dHYrtEOBTLVXEjL8/vGNDY8TSCUubu3Qdal7WtX1t/GQD6J+xRoTMBXwEAP XNpUDIpxK4gBciWV+IcGhWzz6TdnN7QU9gs3RTp5SQ6deu09qXtm1ahIAUXn+MIeXEs0 KEsw== X-Gm-Message-State: ACrzQf0ZF+tdeuk64tFLu5AKq4++BcXH8HLftt/Iqjl5I6OHJ6fM74f7 nZFSZnVX18bTJ5TurnH48Ku2XQ== X-Google-Smtp-Source: AMsMyM4MPWlLPhl9aEBe+I0ytnq/LisNTNkw/l6jP8N1jIIWMPQDYuNzvbvKB+cQyYnOf0HSWghB8Q== X-Received: by 2002:a17:90a:5892:b0:20a:9cf5:c13c with SMTP id j18-20020a17090a589200b0020a9cf5c13cmr16349813pji.63.1665744456038; Fri, 14 Oct 2022 03:47:36 -0700 (PDT) Received: from localhost ([122.172.86.128]) by smtp.gmail.com with ESMTPSA id w2-20020a170902e88200b0017cc29a5536sm1470239plg.17.2022.10.14.03.47.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 14 Oct 2022 03:47:35 -0700 (PDT) From: Viresh Kumar To: Linus Walleij , Bartosz Golaszewski Cc: Viresh Kumar , Vincent Guittot , linux-gpio@vger.kernel.org, Kent Gibson , Miguel Ojeda , Wedson Almeida Filho , =?utf-8?q?Alex_Benn=C3=A9e?= , stratos-dev@op-lists.linaro.org, Gerard Ryan Subject: [PATCH V7 2/8] libgpiod-sys: Add pre generated rust bindings Date: Fri, 14 Oct 2022 16:17:19 +0530 Message-Id: X-Mailer: git-send-email 2.31.1.272.g89b43f80a514 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org This adds a copy of pre generated bindings and adds the suggested way of updating those in README. Signed-off-by: Viresh Kumar --- bindings/rust/libgpiod-sys/README.md | 11 + bindings/rust/libgpiod-sys/src/bindings.rs | 1173 ++++++++++++++++++++ 2 files changed, 1184 insertions(+) create mode 100644 bindings/rust/libgpiod-sys/README.md create mode 100644 bindings/rust/libgpiod-sys/src/bindings.rs diff --git a/bindings/rust/libgpiod-sys/README.md b/bindings/rust/libgpiod-sys/README.md new file mode 100644 index 000000000000..ecf75b31c41e --- /dev/null +++ b/bindings/rust/libgpiod-sys/README.md @@ -0,0 +1,11 @@ +# Generated libgpiod-sys Rust FFI bindings +Automatically generated Rust FFI bindings via + [bindgen](https://github.com/rust-lang/rust-bindgen). + +## Updating bindings +1. Clone the source from + +2. run `cd libgpiod/bindings/rust/libgpiod-sys/` +2. run `cargo build --features generate` +3. Copy the bindings 'cp ../target/debug/build/libgpiod-sys-###/out/bindings.rs src/bindings.rs' +4. Commit changes in `src/bindings.rs` diff --git a/bindings/rust/libgpiod-sys/src/bindings.rs b/bindings/rust/libgpiod-sys/src/bindings.rs new file mode 100644 index 000000000000..fc2ffa02c3c4 --- /dev/null +++ b/bindings/rust/libgpiod-sys/src/bindings.rs @@ -0,0 +1,1173 @@ +/* automatically generated by rust-bindgen 0.59.2 */ + +pub const true_: u32 = 1; +pub const false_: u32 = 0; +pub const __bool_true_false_are_defined: u32 = 1; +pub const _STDINT_H: u32 = 1; +pub const _FEATURES_H: u32 = 1; +pub const _DEFAULT_SOURCE: u32 = 1; +pub const __GLIBC_USE_ISOC2X: u32 = 0; +pub const __USE_ISOC11: u32 = 1; +pub const __USE_ISOC99: u32 = 1; +pub const __USE_ISOC95: u32 = 1; +pub const __USE_POSIX_IMPLICITLY: u32 = 1; +pub const _POSIX_SOURCE: u32 = 1; +pub const _POSIX_C_SOURCE: u32 = 200809; +pub const __USE_POSIX: u32 = 1; +pub const __USE_POSIX2: u32 = 1; +pub const __USE_POSIX199309: u32 = 1; +pub const __USE_POSIX199506: u32 = 1; +pub const __USE_XOPEN2K: u32 = 1; +pub const __USE_XOPEN2K8: u32 = 1; +pub const _ATFILE_SOURCE: u32 = 1; +pub const __USE_MISC: u32 = 1; +pub const __USE_ATFILE: u32 = 1; +pub const __USE_FORTIFY_LEVEL: u32 = 0; +pub const __GLIBC_USE_DEPRECATED_GETS: u32 = 0; +pub const __GLIBC_USE_DEPRECATED_SCANF: u32 = 0; +pub const _STDC_PREDEF_H: u32 = 1; +pub const __STDC_IEC_559__: u32 = 1; +pub const __STDC_IEC_559_COMPLEX__: u32 = 1; +pub const __STDC_ISO_10646__: u32 = 201706; +pub const __GNU_LIBRARY__: u32 = 6; +pub const __GLIBC__: u32 = 2; +pub const __GLIBC_MINOR__: u32 = 31; +pub const _SYS_CDEFS_H: u32 = 1; +pub const __glibc_c99_flexarr_available: u32 = 1; +pub const __WORDSIZE: u32 = 64; +pub const __WORDSIZE_TIME64_COMPAT32: u32 = 1; +pub const __SYSCALL_WORDSIZE: u32 = 64; +pub const __LONG_DOUBLE_USES_FLOAT128: u32 = 0; +pub const __HAVE_GENERIC_SELECTION: u32 = 1; +pub const __GLIBC_USE_LIB_EXT2: u32 = 0; +pub const __GLIBC_USE_IEC_60559_BFP_EXT: u32 = 0; +pub const __GLIBC_USE_IEC_60559_BFP_EXT_C2X: u32 = 0; +pub const __GLIBC_USE_IEC_60559_FUNCS_EXT: u32 = 0; +pub const __GLIBC_USE_IEC_60559_FUNCS_EXT_C2X: u32 = 0; +pub const __GLIBC_USE_IEC_60559_TYPES_EXT: u32 = 0; +pub const _BITS_TYPES_H: u32 = 1; +pub const __TIMESIZE: u32 = 64; +pub const _BITS_TYPESIZES_H: u32 = 1; +pub const __OFF_T_MATCHES_OFF64_T: u32 = 1; +pub const __INO_T_MATCHES_INO64_T: u32 = 1; +pub const __RLIM_T_MATCHES_RLIM64_T: u32 = 1; +pub const __STATFS_MATCHES_STATFS64: u32 = 1; +pub const __FD_SETSIZE: u32 = 1024; +pub const _BITS_TIME64_H: u32 = 1; +pub const _BITS_WCHAR_H: u32 = 1; +pub const _BITS_STDINT_INTN_H: u32 = 1; +pub const _BITS_STDINT_UINTN_H: u32 = 1; +pub const INT8_MIN: i32 = -128; +pub const INT16_MIN: i32 = -32768; +pub const INT32_MIN: i32 = -2147483648; +pub const INT8_MAX: u32 = 127; +pub const INT16_MAX: u32 = 32767; +pub const INT32_MAX: u32 = 2147483647; +pub const UINT8_MAX: u32 = 255; +pub const UINT16_MAX: u32 = 65535; +pub const UINT32_MAX: u32 = 4294967295; +pub const INT_LEAST8_MIN: i32 = -128; +pub const INT_LEAST16_MIN: i32 = -32768; +pub const INT_LEAST32_MIN: i32 = -2147483648; +pub const INT_LEAST8_MAX: u32 = 127; +pub const INT_LEAST16_MAX: u32 = 32767; +pub const INT_LEAST32_MAX: u32 = 2147483647; +pub const UINT_LEAST8_MAX: u32 = 255; +pub const UINT_LEAST16_MAX: u32 = 65535; +pub const UINT_LEAST32_MAX: u32 = 4294967295; +pub const INT_FAST8_MIN: i32 = -128; +pub const INT_FAST16_MIN: i64 = -9223372036854775808; +pub const INT_FAST32_MIN: i64 = -9223372036854775808; +pub const INT_FAST8_MAX: u32 = 127; +pub const INT_FAST16_MAX: u64 = 9223372036854775807; +pub const INT_FAST32_MAX: u64 = 9223372036854775807; +pub const UINT_FAST8_MAX: u32 = 255; +pub const UINT_FAST16_MAX: i32 = -1; +pub const UINT_FAST32_MAX: i32 = -1; +pub const INTPTR_MIN: i64 = -9223372036854775808; +pub const INTPTR_MAX: u64 = 9223372036854775807; +pub const UINTPTR_MAX: i32 = -1; +pub const PTRDIFF_MIN: i64 = -9223372036854775808; +pub const PTRDIFF_MAX: u64 = 9223372036854775807; +pub const SIG_ATOMIC_MIN: i32 = -2147483648; +pub const SIG_ATOMIC_MAX: u32 = 2147483647; +pub const SIZE_MAX: i32 = -1; +pub const WINT_MIN: u32 = 0; +pub const WINT_MAX: u32 = 4294967295; +pub type size_t = ::std::os::raw::c_ulong; +pub type wchar_t = ::std::os::raw::c_int; +#[repr(C)] +#[repr(align(16))] +#[derive(Debug, Copy, Clone)] +pub struct max_align_t { + pub __clang_max_align_nonce1: ::std::os::raw::c_longlong, + pub __bindgen_padding_0: u64, + pub __clang_max_align_nonce2: u128, +} +#[test] +fn bindgen_test_layout_max_align_t() { + assert_eq!( + ::std::mem::size_of::(), + 32usize, + concat!("Size of: ", stringify!(max_align_t)) + ); + assert_eq!( + ::std::mem::align_of::(), + 16usize, + concat!("Alignment of ", stringify!(max_align_t)) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).__clang_max_align_nonce1 as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(max_align_t), + "::", + stringify!(__clang_max_align_nonce1) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).__clang_max_align_nonce2 as *const _ as usize + }, + 16usize, + concat!( + "Offset of field: ", + stringify!(max_align_t), + "::", + stringify!(__clang_max_align_nonce2) + ) + ); +} +pub type __u_char = ::std::os::raw::c_uchar; +pub type __u_short = ::std::os::raw::c_ushort; +pub type __u_int = ::std::os::raw::c_uint; +pub type __u_long = ::std::os::raw::c_ulong; +pub type __int8_t = ::std::os::raw::c_schar; +pub type __uint8_t = ::std::os::raw::c_uchar; +pub type __int16_t = ::std::os::raw::c_short; +pub type __uint16_t = ::std::os::raw::c_ushort; +pub type __int32_t = ::std::os::raw::c_int; +pub type __uint32_t = ::std::os::raw::c_uint; +pub type __int64_t = ::std::os::raw::c_long; +pub type __uint64_t = ::std::os::raw::c_ulong; +pub type __int_least8_t = __int8_t; +pub type __uint_least8_t = __uint8_t; +pub type __int_least16_t = __int16_t; +pub type __uint_least16_t = __uint16_t; +pub type __int_least32_t = __int32_t; +pub type __uint_least32_t = __uint32_t; +pub type __int_least64_t = __int64_t; +pub type __uint_least64_t = __uint64_t; +pub type __quad_t = ::std::os::raw::c_long; +pub type __u_quad_t = ::std::os::raw::c_ulong; +pub type __intmax_t = ::std::os::raw::c_long; +pub type __uintmax_t = ::std::os::raw::c_ulong; +pub type __dev_t = ::std::os::raw::c_ulong; +pub type __uid_t = ::std::os::raw::c_uint; +pub type __gid_t = ::std::os::raw::c_uint; +pub type __ino_t = ::std::os::raw::c_ulong; +pub type __ino64_t = ::std::os::raw::c_ulong; +pub type __mode_t = ::std::os::raw::c_uint; +pub type __nlink_t = ::std::os::raw::c_ulong; +pub type __off_t = ::std::os::raw::c_long; +pub type __off64_t = ::std::os::raw::c_long; +pub type __pid_t = ::std::os::raw::c_int; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __fsid_t { + pub __val: [::std::os::raw::c_int; 2usize], +} +#[test] +fn bindgen_test_layout___fsid_t() { + assert_eq!( + ::std::mem::size_of::<__fsid_t>(), + 8usize, + concat!("Size of: ", stringify!(__fsid_t)) + ); + assert_eq!( + ::std::mem::align_of::<__fsid_t>(), + 4usize, + concat!("Alignment of ", stringify!(__fsid_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__fsid_t>())).__val as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__fsid_t), + "::", + stringify!(__val) + ) + ); +} +pub type __clock_t = ::std::os::raw::c_long; +pub type __rlim_t = ::std::os::raw::c_ulong; +pub type __rlim64_t = ::std::os::raw::c_ulong; +pub type __id_t = ::std::os::raw::c_uint; +pub type __time_t = ::std::os::raw::c_long; +pub type __useconds_t = ::std::os::raw::c_uint; +pub type __suseconds_t = ::std::os::raw::c_long; +pub type __daddr_t = ::std::os::raw::c_int; +pub type __key_t = ::std::os::raw::c_int; +pub type __clockid_t = ::std::os::raw::c_int; +pub type __timer_t = *mut ::std::os::raw::c_void; +pub type __blksize_t = ::std::os::raw::c_long; +pub type __blkcnt_t = ::std::os::raw::c_long; +pub type __blkcnt64_t = ::std::os::raw::c_long; +pub type __fsblkcnt_t = ::std::os::raw::c_ulong; +pub type __fsblkcnt64_t = ::std::os::raw::c_ulong; +pub type __fsfilcnt_t = ::std::os::raw::c_ulong; +pub type __fsfilcnt64_t = ::std::os::raw::c_ulong; +pub type __fsword_t = ::std::os::raw::c_long; +pub type __ssize_t = ::std::os::raw::c_long; +pub type __syscall_slong_t = ::std::os::raw::c_long; +pub type __syscall_ulong_t = ::std::os::raw::c_ulong; +pub type __loff_t = __off64_t; +pub type __caddr_t = *mut ::std::os::raw::c_char; +pub type __intptr_t = ::std::os::raw::c_long; +pub type __socklen_t = ::std::os::raw::c_uint; +pub type __sig_atomic_t = ::std::os::raw::c_int; +pub type int_least8_t = __int_least8_t; +pub type int_least16_t = __int_least16_t; +pub type int_least32_t = __int_least32_t; +pub type int_least64_t = __int_least64_t; +pub type uint_least8_t = __uint_least8_t; +pub type uint_least16_t = __uint_least16_t; +pub type uint_least32_t = __uint_least32_t; +pub type uint_least64_t = __uint_least64_t; +pub type int_fast8_t = ::std::os::raw::c_schar; +pub type int_fast16_t = ::std::os::raw::c_long; +pub type int_fast32_t = ::std::os::raw::c_long; +pub type int_fast64_t = ::std::os::raw::c_long; +pub type uint_fast8_t = ::std::os::raw::c_uchar; +pub type uint_fast16_t = ::std::os::raw::c_ulong; +pub type uint_fast32_t = ::std::os::raw::c_ulong; +pub type uint_fast64_t = ::std::os::raw::c_ulong; +pub type intmax_t = __intmax_t; +pub type uintmax_t = __uintmax_t; +#[doc = " @mainpage libgpiod public API"] +#[doc = ""] +#[doc = " This is the complete documentation of the public API made available to"] +#[doc = " users of libgpiod."] +#[doc = ""] +#[doc = "

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

General note on error handling: all functions exported by libgpiod that"] +#[doc = " can fail, set errno to one of the error values defined in errno.h upon"] +#[doc = " failure. The way of notifying the caller that an error occurred varies"] +#[doc = " between functions, but in general a function that returns an int, returns -1"] +#[doc = " on error, while a function returning a pointer indicates an error condition"] +#[doc = " by returning a NULL pointer. It's not practical to list all possible error"] +#[doc = " codes for every function as they propagate errors from the underlying libc"] +#[doc = " functions."] +#[doc = ""] +#[doc = "

In general libgpiod functions are not NULL-aware and it's expected that"] +#[doc = " users pass valid pointers to objects as arguments. An exception to this rule"] +#[doc = " are the functions that free/close/release resources - which work when passed"] +#[doc = " a NULL-pointer as argument. Other exceptions are documented."] +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct gpiod_chip { + _unused: [u8; 0], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct gpiod_chip_info { + _unused: [u8; 0], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct gpiod_line_info { + _unused: [u8; 0], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct gpiod_line_settings { + _unused: [u8; 0], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct gpiod_line_config { + _unused: [u8; 0], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct gpiod_request_config { + _unused: [u8; 0], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct gpiod_line_request { + _unused: [u8; 0], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct gpiod_info_event { + _unused: [u8; 0], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct gpiod_edge_event { + _unused: [u8; 0], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct gpiod_edge_event_buffer { + _unused: [u8; 0], +} +extern "C" { + #[doc = " @brief Open a chip by path."] + #[doc = " @param path Path to the gpiochip device file."] + #[doc = " @return GPIO chip request or NULL if an error occurred."] + pub fn gpiod_chip_open(path: *const ::std::os::raw::c_char) -> *mut gpiod_chip; +} +extern "C" { + #[doc = " @brief Close the chip and release all associated resources."] + #[doc = " @param chip Chip to close."] + pub fn gpiod_chip_close(chip: *mut gpiod_chip); +} +extern "C" { + #[doc = " @brief Get information about the chip."] + #[doc = " @param chip GPIO chip object."] + #[doc = " @return New GPIO chip info object or NULL if an error occurred. The returned"] + #[doc = " object must be freed by the caller using ::gpiod_chip_info_free."] + pub fn gpiod_chip_get_info(chip: *mut gpiod_chip) -> *mut gpiod_chip_info; +} +extern "C" { + #[doc = " @brief Get the path used to open the chip."] + #[doc = " @param chip GPIO chip object."] + #[doc = " @return Path to the file passed as argument to ::gpiod_chip_open."] + pub fn gpiod_chip_get_path(chip: *mut gpiod_chip) -> *const ::std::os::raw::c_char; +} +extern "C" { + #[doc = " @brief Get a snapshot of information about a line."] + #[doc = " @param chip GPIO chip object."] + #[doc = " @param offset The offset of the GPIO line."] + #[doc = " @return New GPIO line info object or NULL if an error occurred. The returned"] + #[doc = "\t object must be freed by the caller using ::gpiod_line_info_free."] + pub fn gpiod_chip_get_line_info( + chip: *mut gpiod_chip, + offset: ::std::os::raw::c_uint, + ) -> *mut gpiod_line_info; +} +extern "C" { + #[doc = " @brief Get a snapshot of the status of a line and start watching it for"] + #[doc = "\t future changes."] + #[doc = " @param chip GPIO chip object."] + #[doc = " @param offset The offset of the GPIO line."] + #[doc = " @return New GPIO line info object or NULL if an error occurred. The returned"] + #[doc = "\t object must be freed by the caller using ::gpiod_line_info_free."] + #[doc = " @note Line status does not include the line value. To monitor the line"] + #[doc = "\t value the line must be requested as an input with edge detection set."] + pub fn gpiod_chip_watch_line_info( + chip: *mut gpiod_chip, + offset: ::std::os::raw::c_uint, + ) -> *mut gpiod_line_info; +} +extern "C" { + #[doc = " @brief Stop watching a line for status changes."] + #[doc = " @param chip GPIO chip object."] + #[doc = " @param offset The offset of the line to stop watching."] + #[doc = " @return 0 on success, -1 on failure."] + pub fn gpiod_chip_unwatch_line_info( + chip: *mut gpiod_chip, + offset: ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Get the file descriptor associated with the chip."] + #[doc = " @param chip GPIO chip object."] + #[doc = " @return File descriptor number for the chip."] + #[doc = "\t This function never fails."] + #[doc = "\t The returned file descriptor must not be closed by the caller."] + #[doc = "\t Call ::gpiod_chip_close to close the file descriptor."] + pub fn gpiod_chip_get_fd(chip: *mut gpiod_chip) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Wait for line status change events on any of the watched lines"] + #[doc = "\t on the chip."] + #[doc = " @param chip GPIO chip object."] + #[doc = " @param timeout_ns Wait time limit in nanoseconds. If set to 0, the function"] + #[doc = "\t\t returns immediatelly. If set to a negative number, the"] + #[doc = "\t\t function blocks indefinitely until an event becomes"] + #[doc = "\t\t available."] + #[doc = " @return 0 if wait timed out, -1 if an error occurred, 1 if an event is"] + #[doc = "\t pending."] + pub fn gpiod_chip_wait_info_event( + chip: *mut gpiod_chip, + timeout_ns: i64, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Read a single line status change event from the chip."] + #[doc = " @param chip GPIO chip object."] + #[doc = " @return Newly read watch event object or NULL on error. The event must be"] + #[doc = "\t freed by the caller using ::gpiod_info_event_free."] + #[doc = " @note If no events are pending, this function will block."] + pub fn gpiod_chip_read_info_event(chip: *mut gpiod_chip) -> *mut gpiod_info_event; +} +extern "C" { + #[doc = " @brief Map a line's name to its offset within the chip."] + #[doc = " @param chip GPIO chip object."] + #[doc = " @param name Name of the GPIO line to map."] + #[doc = " @return Offset of the line within the chip or -1 on error."] + #[doc = " @note If a line with given name is not exposed by the chip, the function"] + #[doc = " sets errno to ENOENT."] + pub fn gpiod_chip_get_line_offset_from_name( + chip: *mut gpiod_chip, + name: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Request a set of lines for exclusive usage."] + #[doc = " @param chip GPIO chip object."] + #[doc = " @param req_cfg Request config object. Can be NULL for default settings."] + #[doc = " @param line_cfg Line config object."] + #[doc = " @return New line request object or NULL if an error occurred. The request"] + #[doc = "\t must be released by the caller using ::gpiod_line_request_release."] + pub fn gpiod_chip_request_lines( + chip: *mut gpiod_chip, + req_cfg: *mut gpiod_request_config, + line_cfg: *mut gpiod_line_config, + ) -> *mut gpiod_line_request; +} +extern "C" { + #[doc = " @brief Free a chip info object and release all associated resources."] + #[doc = " @param info GPIO chip info object to free."] + pub fn gpiod_chip_info_free(info: *mut gpiod_chip_info); +} +extern "C" { + #[doc = " @brief Get the name of the chip as represented in the kernel."] + #[doc = " @param info GPIO chip info object."] + #[doc = " @return Pointer to a human-readable string containing the chip name."] + pub fn gpiod_chip_info_get_name(info: *mut gpiod_chip_info) -> *const ::std::os::raw::c_char; +} +extern "C" { + #[doc = " @brief Get the label of the chip as represented in the kernel."] + #[doc = " @param info GPIO chip info object."] + #[doc = " @return Pointer to a human-readable string containing the chip label."] + pub fn gpiod_chip_info_get_label(info: *mut gpiod_chip_info) -> *const ::std::os::raw::c_char; +} +extern "C" { + #[doc = " @brief Get the number of lines exposed by the chip."] + #[doc = " @param info GPIO chip info object."] + #[doc = " @return Number of GPIO lines."] + pub fn gpiod_chip_info_get_num_lines(info: *mut gpiod_chip_info) -> size_t; +} +pub const GPIOD_LINE_VALUE_INACTIVE: ::std::os::raw::c_uint = 0; +pub const GPIOD_LINE_VALUE_ACTIVE: ::std::os::raw::c_uint = 1; +#[doc = " @brief Logical line state."] +pub type _bindgen_ty_1 = ::std::os::raw::c_uint; +pub const GPIOD_LINE_DIRECTION_AS_IS: ::std::os::raw::c_uint = 1; +pub const GPIOD_LINE_DIRECTION_INPUT: ::std::os::raw::c_uint = 2; +pub const GPIOD_LINE_DIRECTION_OUTPUT: ::std::os::raw::c_uint = 3; +#[doc = " @brief Direction settings."] +pub type _bindgen_ty_2 = ::std::os::raw::c_uint; +pub const GPIOD_LINE_EDGE_NONE: ::std::os::raw::c_uint = 1; +pub const GPIOD_LINE_EDGE_RISING: ::std::os::raw::c_uint = 2; +pub const GPIOD_LINE_EDGE_FALLING: ::std::os::raw::c_uint = 3; +pub const GPIOD_LINE_EDGE_BOTH: ::std::os::raw::c_uint = 4; +#[doc = " @brief Edge detection settings."] +pub type _bindgen_ty_3 = ::std::os::raw::c_uint; +pub const GPIOD_LINE_BIAS_AS_IS: ::std::os::raw::c_uint = 1; +pub const GPIOD_LINE_BIAS_UNKNOWN: ::std::os::raw::c_uint = 2; +pub const GPIOD_LINE_BIAS_DISABLED: ::std::os::raw::c_uint = 3; +pub const GPIOD_LINE_BIAS_PULL_UP: ::std::os::raw::c_uint = 4; +pub const GPIOD_LINE_BIAS_PULL_DOWN: ::std::os::raw::c_uint = 5; +#[doc = " @brief Internal bias settings."] +pub type _bindgen_ty_4 = ::std::os::raw::c_uint; +pub const GPIOD_LINE_DRIVE_PUSH_PULL: ::std::os::raw::c_uint = 1; +pub const GPIOD_LINE_DRIVE_OPEN_DRAIN: ::std::os::raw::c_uint = 2; +pub const GPIOD_LINE_DRIVE_OPEN_SOURCE: ::std::os::raw::c_uint = 3; +#[doc = " @brief Drive settings."] +pub type _bindgen_ty_5 = ::std::os::raw::c_uint; +pub const GPIOD_LINE_EVENT_CLOCK_MONOTONIC: ::std::os::raw::c_uint = 1; +pub const GPIOD_LINE_EVENT_CLOCK_REALTIME: ::std::os::raw::c_uint = 2; +pub const GPIOD_LINE_EVENT_CLOCK_HTE: ::std::os::raw::c_uint = 3; +#[doc = " @brief Event clock settings."] +pub type _bindgen_ty_6 = ::std::os::raw::c_uint; +extern "C" { + #[doc = " @brief Free a line info object and release all associated resources."] + #[doc = " @param info GPIO line info object to free."] + pub fn gpiod_line_info_free(info: *mut gpiod_line_info); +} +extern "C" { + #[doc = " @brief Copy a line info object."] + #[doc = " @param info Line info to copy."] + #[doc = " @return Copy of the line info or NULL on error. The returned object must"] + #[doc = "\t be freed by the caller using :gpiod_line_info_free."] + pub fn gpiod_line_info_copy(info: *mut gpiod_line_info) -> *mut gpiod_line_info; +} +extern "C" { + #[doc = " @brief Get the offset of the line."] + #[doc = " @param info GPIO line info object."] + #[doc = " @return Offset of the line within the parent chip."] + #[doc = ""] + #[doc = " The offset uniquely identifies the line on the chip."] + #[doc = " The combination of the chip and offset uniquely identifies the line within"] + #[doc = " the system."] + pub fn gpiod_line_info_get_offset(info: *mut gpiod_line_info) -> ::std::os::raw::c_uint; +} +extern "C" { + #[doc = " @brief Get the name of the line."] + #[doc = " @param info GPIO line info object."] + #[doc = " @return Name of the GPIO line as it is represented in the kernel."] + #[doc = "\t This function returns a pointer to a null-terminated string"] + #[doc = "\t or NULL if the line is unnamed."] + pub fn gpiod_line_info_get_name(info: *mut gpiod_line_info) -> *const ::std::os::raw::c_char; +} +extern "C" { + #[doc = " @brief Check if the line is in use."] + #[doc = " @param info GPIO line object."] + #[doc = " @return True if the line is in use, false otherwise."] + #[doc = ""] + #[doc = " The exact reason a line is busy cannot be determined from user space."] + #[doc = " It may have been requested by another process or hogged by the kernel."] + #[doc = " It only matters that the line is used and can't be requested until"] + #[doc = " released by the existing consumer."] + pub fn gpiod_line_info_is_used(info: *mut gpiod_line_info) -> bool; +} +extern "C" { + #[doc = " @brief Get the name of the consumer of the line."] + #[doc = " @param info GPIO line info object."] + #[doc = " @return Name of the GPIO consumer as it is represented in the kernel."] + #[doc = "\t This function returns a pointer to a null-terminated string"] + #[doc = "\t or NULL if the consumer name is not set."] + pub fn gpiod_line_info_get_consumer( + info: *mut gpiod_line_info, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + #[doc = " @brief Get the direction setting of the line."] + #[doc = " @param info GPIO line info object."] + #[doc = " @return Returns ::GPIOD_LINE_DIRECTION_INPUT or"] + #[doc = "\t ::GPIOD_LINE_DIRECTION_OUTPUT."] + pub fn gpiod_line_info_get_direction(info: *mut gpiod_line_info) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Get the edge detection setting of the line."] + #[doc = " @param info GPIO line info object."] + #[doc = " @return Returns ::GPIOD_LINE_EDGE_NONE, ::GPIOD_LINE_EDGE_RISING,"] + #[doc = "\t ::GPIOD_LINE_EDGE_FALLING or ::GPIOD_LINE_EDGE_BOTH."] + pub fn gpiod_line_info_get_edge_detection(info: *mut gpiod_line_info) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Get the bias setting of the line."] + #[doc = " @param info GPIO line object."] + #[doc = " @return Returns ::GPIOD_LINE_BIAS_PULL_UP, ::GPIOD_LINE_BIAS_PULL_DOWN,"] + #[doc = "\t ::GPIOD_LINE_BIAS_DISABLED or ::GPIOD_LINE_BIAS_UNKNOWN."] + pub fn gpiod_line_info_get_bias(info: *mut gpiod_line_info) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Get the drive setting of the line."] + #[doc = " @param info GPIO line info object."] + #[doc = " @return Returns ::GPIOD_LINE_DRIVE_PUSH_PULL, ::GPIOD_LINE_DRIVE_OPEN_DRAIN"] + #[doc = "\t or ::GPIOD_LINE_DRIVE_OPEN_SOURCE."] + pub fn gpiod_line_info_get_drive(info: *mut gpiod_line_info) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Check if the logical value of the line is inverted compared to the"] + #[doc = "\t physical."] + #[doc = " @param info GPIO line object."] + #[doc = " @return True if the line is \"active-low\", false otherwise."] + pub fn gpiod_line_info_is_active_low(info: *mut gpiod_line_info) -> bool; +} +extern "C" { + #[doc = " @brief Check if the line is debounced (either by hardware or by the kernel"] + #[doc = "\t software debouncer)."] + #[doc = " @param info GPIO line info object."] + #[doc = " @return True if the line is debounced, false otherwise."] + pub fn gpiod_line_info_is_debounced(info: *mut gpiod_line_info) -> bool; +} +extern "C" { + #[doc = " @brief Get the debounce period of the line, in microseconds."] + #[doc = " @param info GPIO line info object."] + #[doc = " @return Debounce period in microseconds."] + #[doc = "\t 0 if the line is not debounced."] + pub fn gpiod_line_info_get_debounce_period_us( + info: *mut gpiod_line_info, + ) -> ::std::os::raw::c_ulong; +} +extern "C" { + #[doc = " @brief Get the event clock setting used for edge event timestamps for the"] + #[doc = "\t line."] + #[doc = " @param info GPIO line info object."] + #[doc = " @return Returns ::GPIOD_LINE_EVENT_CLOCK_MONOTONIC or"] + #[doc = "\t ::GPIOD_LINE_EVENT_CLOCK_REALTIME."] + pub fn gpiod_line_info_get_event_clock(info: *mut gpiod_line_info) -> ::std::os::raw::c_int; +} +pub const GPIOD_INFO_EVENT_LINE_REQUESTED: ::std::os::raw::c_uint = 1; +pub const GPIOD_INFO_EVENT_LINE_RELEASED: ::std::os::raw::c_uint = 2; +pub const GPIOD_INFO_EVENT_LINE_CONFIG_CHANGED: ::std::os::raw::c_uint = 3; +#[doc = " @brief Line status change event types."] +pub type _bindgen_ty_7 = ::std::os::raw::c_uint; +extern "C" { + #[doc = " @brief Free the info event object and release all associated resources."] + #[doc = " @param event Info event to free."] + pub fn gpiod_info_event_free(event: *mut gpiod_info_event); +} +extern "C" { + #[doc = " @brief Get the event type of the status change event."] + #[doc = " @param event Line status watch event."] + #[doc = " @return One of ::GPIOD_INFO_EVENT_LINE_REQUESTED,"] + #[doc = "\t ::GPIOD_INFO_EVENT_LINE_RELEASED or"] + #[doc = "\t ::GPIOD_INFO_EVENT_LINE_CONFIG_CHANGED."] + pub fn gpiod_info_event_get_event_type(event: *mut gpiod_info_event) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Get the timestamp of the event."] + #[doc = " @param event Line status watch event."] + #[doc = " @return Timestamp in nanoseconds, read from the monotonic clock."] + pub fn gpiod_info_event_get_timestamp_ns(event: *mut gpiod_info_event) -> u64; +} +extern "C" { + #[doc = " @brief Get the snapshot of line-info associated with the event."] + #[doc = " @param event Line info event object."] + #[doc = " @return Returns a pointer to the line-info object associated with the event"] + #[doc = "\t whose lifetime is tied to the event object. It must not be freed by"] + #[doc = "\t the caller."] + pub fn gpiod_info_event_get_line_info(event: *mut gpiod_info_event) -> *mut gpiod_line_info; +} +extern "C" { + #[doc = " @brief Create a new line settings object."] + #[doc = " @return New line settings object or NULL on error."] + pub fn gpiod_line_settings_new() -> *mut gpiod_line_settings; +} +extern "C" { + #[doc = " @brief Free the line settings object and release all associated resources."] + #[doc = " @param settings Line settings object."] + pub fn gpiod_line_settings_free(settings: *mut gpiod_line_settings); +} +extern "C" { + #[doc = " @brief Reset the line settings object to its default values."] + #[doc = " @param settings Line settings object."] + pub fn gpiod_line_settings_reset(settings: *mut gpiod_line_settings); +} +extern "C" { + #[doc = " @brief Copy the line settings object."] + #[doc = " @param settings Line settings object to copy."] + #[doc = " @return New line settings object that must be freed using"] + #[doc = " ::gpiod_line_settings_free or NULL on failure."] + pub fn gpiod_line_settings_copy(settings: *mut gpiod_line_settings) + -> *mut gpiod_line_settings; +} +extern "C" { + #[doc = " @brief Set direction."] + #[doc = " @param settings Line settings object."] + #[doc = " @param direction New direction."] + #[doc = " @return 0 on success, -1 on error."] + pub fn gpiod_line_settings_set_direction( + settings: *mut gpiod_line_settings, + direction: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Get direction."] + #[doc = " @param settings Line settings object."] + #[doc = " @return Current direction."] + pub fn gpiod_line_settings_get_direction( + settings: *mut gpiod_line_settings, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Set edge detection."] + #[doc = " @param settings Line settings object."] + #[doc = " @param edge New edge detection setting."] + #[doc = " @return 0 on success, -1 on failure."] + pub fn gpiod_line_settings_set_edge_detection( + settings: *mut gpiod_line_settings, + edge: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Get edge detection."] + #[doc = " @param settings Line settings object."] + #[doc = " @return Current edge detection setting."] + pub fn gpiod_line_settings_get_edge_detection( + settings: *mut gpiod_line_settings, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Set bias."] + #[doc = " @param settings Line settings object."] + #[doc = " @param bias New bias."] + #[doc = " @return 0 on success, -1 on failure."] + pub fn gpiod_line_settings_set_bias( + settings: *mut gpiod_line_settings, + bias: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Get bias."] + #[doc = " @param settings Line settings object."] + #[doc = " @return Current bias setting."] + pub fn gpiod_line_settings_get_bias( + settings: *mut gpiod_line_settings, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Set drive."] + #[doc = " @param settings Line settings object."] + #[doc = " @param drive New drive setting."] + #[doc = " @return 0 on success, -1 on failure."] + pub fn gpiod_line_settings_set_drive( + settings: *mut gpiod_line_settings, + drive: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Get drive."] + #[doc = " @param settings Line settings object."] + #[doc = " @return Current drive setting."] + pub fn gpiod_line_settings_get_drive( + settings: *mut gpiod_line_settings, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Set active-low setting."] + #[doc = " @param settings Line settings object."] + #[doc = " @param active_low New active-low setting."] + pub fn gpiod_line_settings_set_active_low(settings: *mut gpiod_line_settings, active_low: bool); +} +extern "C" { + #[doc = " @brief Get active-low setting."] + #[doc = " @param settings Line settings object."] + #[doc = " @return True if active-low is enabled, false otherwise."] + pub fn gpiod_line_settings_get_active_low(settings: *mut gpiod_line_settings) -> bool; +} +extern "C" { + #[doc = " @brief Set debounce period."] + #[doc = " @param settings Line settings object."] + #[doc = " @param period New debounce period in microseconds."] + pub fn gpiod_line_settings_set_debounce_period_us( + settings: *mut gpiod_line_settings, + period: ::std::os::raw::c_ulong, + ); +} +extern "C" { + #[doc = " @brief Get debounce period."] + #[doc = " @param settings Line settings object."] + #[doc = " @return Current debounce period in microseconds."] + pub fn gpiod_line_settings_get_debounce_period_us( + settings: *mut gpiod_line_settings, + ) -> ::std::os::raw::c_ulong; +} +extern "C" { + #[doc = " @brief Set event clock."] + #[doc = " @param settings Line settings object."] + #[doc = " @param event_clock New event clock."] + #[doc = " @return 0 on success, -1 on failure."] + pub fn gpiod_line_settings_set_event_clock( + settings: *mut gpiod_line_settings, + event_clock: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Get event clock setting."] + #[doc = " @param settings Line settings object."] + #[doc = " @return Current event clock setting."] + pub fn gpiod_line_settings_get_event_clock( + settings: *mut gpiod_line_settings, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Set the output value."] + #[doc = " @param settings Line settings object."] + #[doc = " @param value New output value."] + #[doc = " @return 0 on success, -1 on failure."] + pub fn gpiod_line_settings_set_output_value( + settings: *mut gpiod_line_settings, + value: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Get the output value."] + #[doc = " @param settings Line settings object."] + #[doc = " @return Current output value."] + pub fn gpiod_line_settings_get_output_value( + settings: *mut gpiod_line_settings, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Create a new line config object."] + #[doc = " @return New line config object or NULL on error."] + pub fn gpiod_line_config_new() -> *mut gpiod_line_config; +} +extern "C" { + #[doc = " @brief Free the line config object and release all associated resources."] + #[doc = " @param config Line config object to free."] + pub fn gpiod_line_config_free(config: *mut gpiod_line_config); +} +extern "C" { + #[doc = " @brief Reset the line config object."] + #[doc = " @param config Line config object to free."] + #[doc = ""] + #[doc = " Resets the entire configuration stored in the object. This is useful if"] + #[doc = " the user wants to reuse the object without reallocating it."] + pub fn gpiod_line_config_reset(config: *mut gpiod_line_config); +} +extern "C" { + #[doc = " @brief Add line settings for a set of offsets."] + #[doc = " @param config Line config object."] + #[doc = " @param offsets Array of offsets for which to apply the settings."] + #[doc = " @param num_offsets Number of offsets stored in the offsets array."] + #[doc = " @param settings Line settings to apply."] + #[doc = " @return 0 on success, -1 on failure."] + pub fn gpiod_line_config_add_line_settings( + config: *mut gpiod_line_config, + offsets: *const ::std::os::raw::c_uint, + num_offsets: size_t, + settings: *mut gpiod_line_settings, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Get line settings for offset."] + #[doc = " @param config Line config object."] + #[doc = " @param offset Offset for which to get line settings."] + #[doc = " @return New line settings object (must be freed by the caller) or NULL on"] + #[doc = " error."] + pub fn gpiod_line_config_get_line_settings( + config: *mut gpiod_line_config, + offset: ::std::os::raw::c_uint, + ) -> *mut gpiod_line_settings; +} +extern "C" { + #[doc = " @brief Get configured offsets."] + #[doc = " @param config Line config object."] + #[doc = " @param num_offsets Pointer to a variable in which the number of line offsets"] + #[doc = " will be stored."] + #[doc = " @param offsets Pointer to a pointer which will be set to point to an array"] + #[doc = " containing the configured offsets. The array will be allocated"] + #[doc = " using malloc() and must be freed using free()."] + #[doc = " @return 0 on success, -1 on failure."] + pub fn gpiod_line_config_get_offsets( + config: *mut gpiod_line_config, + num_offsets: *mut size_t, + offsets: *mut *mut ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Create a new request config object."] + #[doc = " @return New request config object or NULL on error."] + pub fn gpiod_request_config_new() -> *mut gpiod_request_config; +} +extern "C" { + #[doc = " @brief Free the request config object and release all associated resources."] + #[doc = " @param config Line config object."] + pub fn gpiod_request_config_free(config: *mut gpiod_request_config); +} +extern "C" { + #[doc = " @brief Set the consumer name for the request."] + #[doc = " @param config Request config object."] + #[doc = " @param consumer Consumer name."] + #[doc = " @note If the consumer string is too long, it will be truncated to the max"] + #[doc = " accepted length."] + pub fn gpiod_request_config_set_consumer( + config: *mut gpiod_request_config, + consumer: *const ::std::os::raw::c_char, + ); +} +extern "C" { + #[doc = " @brief Get the consumer name configured in the request config."] + #[doc = " @param config Request config object."] + #[doc = " @return Consumer name stored in the request config."] + pub fn gpiod_request_config_get_consumer( + config: *mut gpiod_request_config, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + #[doc = " @brief Set the size of the kernel event buffer for the request."] + #[doc = " @param config Request config object."] + #[doc = " @param event_buffer_size New event buffer size."] + #[doc = " @note The kernel may adjust the value if it's too high. If set to 0, the"] + #[doc = " default value will be used."] + #[doc = " @note The kernel buffer is distinct from and independent of the user space"] + #[doc = "\t buffer (::gpiod_edge_event_buffer_new)."] + pub fn gpiod_request_config_set_event_buffer_size( + config: *mut gpiod_request_config, + event_buffer_size: size_t, + ); +} +extern "C" { + #[doc = " @brief Get the edge event buffer size for the request config."] + #[doc = " @param config Request config object."] + #[doc = " @return Edge event buffer size setting from the request config."] + pub fn gpiod_request_config_get_event_buffer_size(config: *mut gpiod_request_config) -> size_t; +} +extern "C" { + #[doc = " @brief Release the requested lines and free all associated resources."] + #[doc = " @param request Line request object to release."] + pub fn gpiod_line_request_release(request: *mut gpiod_line_request); +} +extern "C" { + #[doc = " @brief Get the number of lines in the request."] + #[doc = " @param request Line request object."] + #[doc = " @return Number of requested lines."] + pub fn gpiod_line_request_get_num_lines(request: *mut gpiod_line_request) -> size_t; +} +extern "C" { + #[doc = " @brief Get the offsets of the lines in the request."] + #[doc = " @param request Line request object."] + #[doc = " @param offsets Array to store offsets. Must be sized to hold the number of"] + #[doc = "\t\t lines returned by ::gpiod_line_request_get_num_lines."] + pub fn gpiod_line_request_get_offsets( + request: *mut gpiod_line_request, + offsets: *mut ::std::os::raw::c_uint, + ); +} +extern "C" { + #[doc = " @brief Get the value of a single requested line."] + #[doc = " @param request Line request object."] + #[doc = " @param offset The offset of the line of which the value should be read."] + #[doc = " @return Returns 1 or 0 on success and -1 on error."] + pub fn gpiod_line_request_get_value( + request: *mut gpiod_line_request, + offset: ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Get the values of a subset of requested lines."] + #[doc = " @param request GPIO line request."] + #[doc = " @param num_values Number of lines for which to read values."] + #[doc = " @param offsets Array of offsets identifying the subset of requested lines"] + #[doc = "\t\t from which to read values."] + #[doc = " @param values Array in which the values will be stored. Must be sized"] + #[doc = "\t\t to hold \\p num_values entries. Each value is associated with the"] + #[doc = "\t\t line identified by the corresponding entry in \\p offsets."] + #[doc = " @return 0 on success, -1 on failure."] + pub fn gpiod_line_request_get_values_subset( + request: *mut gpiod_line_request, + num_values: size_t, + offsets: *const ::std::os::raw::c_uint, + values: *mut ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Get the values of all requested lines."] + #[doc = " @param request GPIO line request."] + #[doc = " @param values Array in which the values will be stored. Must be sized to"] + #[doc = "\t\t hold the number of lines returned by"] + #[doc = "\t\t ::gpiod_line_request_get_num_lines."] + #[doc = "\t\t Each value is associated with the line identified by the"] + #[doc = "\t\t corresponding entry in the offset array returned by"] + #[doc = "\t\t ::gpiod_line_request_get_offsets."] + #[doc = " @return 0 on success, -1 on failure."] + pub fn gpiod_line_request_get_values( + request: *mut gpiod_line_request, + values: *mut ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Set the value of a single requested line."] + #[doc = " @param request Line request object."] + #[doc = " @param offset The offset of the line for which the value should be set."] + #[doc = " @param value Value to set."] + pub fn gpiod_line_request_set_value( + request: *mut gpiod_line_request, + offset: ::std::os::raw::c_uint, + value: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Set the values of a subset of requested lines."] + #[doc = " @param request GPIO line request."] + #[doc = " @param num_values Number of lines for which to set values."] + #[doc = " @param offsets Array of offsets, containing the number of entries specified"] + #[doc = "\t\t by \\p num_values, identifying the requested lines for"] + #[doc = "\t\t which to set values."] + #[doc = " @param values Array of values to set, containing the number of entries"] + #[doc = "\t\t specified by \\p num_values. Each value is associated with the"] + #[doc = "\t\t line identified by the corresponding entry in \\p offsets."] + #[doc = " @return 0 on success, -1 on failure."] + pub fn gpiod_line_request_set_values_subset( + request: *mut gpiod_line_request, + num_values: size_t, + offsets: *const ::std::os::raw::c_uint, + values: *const ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Set the values of all lines associated with a request."] + #[doc = " @param request GPIO line request."] + #[doc = " @param values Array containing the values to set. Must be sized to"] + #[doc = "\t\t contain the number of lines returned by"] + #[doc = "\t\t ::gpiod_line_request_get_num_lines."] + #[doc = "\t\t Each value is associated with the line identified by the"] + #[doc = "\t\t corresponding entry in the offset array returned by"] + #[doc = "\t\t ::gpiod_line_request_get_offsets."] + pub fn gpiod_line_request_set_values( + request: *mut gpiod_line_request, + values: *const ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Update the configuration of lines associated with a line request."] + #[doc = " @param request GPIO line request."] + #[doc = " @param config New line config to apply."] + #[doc = " @return 0 on success, -1 on failure."] + #[doc = " @note The new line configuration completely replaces the old."] + #[doc = " @note Any requested lines without overrides are configured to the requested"] + #[doc = "\t defaults."] + #[doc = " @note Any configured overrides for lines that have not been requested"] + #[doc = "\t are silently ignored."] + pub fn gpiod_line_request_reconfigure_lines( + request: *mut gpiod_line_request, + config: *mut gpiod_line_config, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Get the file descriptor associated with a line request."] + #[doc = " @param request GPIO line request."] + #[doc = " @return The file descriptor associated with the request."] + #[doc = "\t This function never fails."] + #[doc = "\t The returned file descriptor must not be closed by the caller."] + #[doc = "\t Call ::gpiod_line_request_release to close the file."] + pub fn gpiod_line_request_get_fd(request: *mut gpiod_line_request) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Wait for edge events on any of the requested lines."] + #[doc = " @param request GPIO line request."] + #[doc = " @param timeout_ns Wait time limit in nanoseconds. If set to 0, the function"] + #[doc = "\t\t returns immediatelly. If set to a negative number, the"] + #[doc = "\t\t function blocks indefinitely until an event becomes"] + #[doc = "\t\t available."] + #[doc = " @return 0 if wait timed out, -1 if an error occurred, 1 if an event is"] + #[doc = "\t pending."] + #[doc = "q"] + #[doc = " Lines must have edge detection set for edge events to be emitted."] + #[doc = " By default edge detection is disabled."] + pub fn gpiod_line_request_wait_edge_event( + request: *mut gpiod_line_request, + timeout_ns: i64, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Read a number of edge events from a line request."] + #[doc = " @param request GPIO line request."] + #[doc = " @param buffer Edge event buffer, sized to hold at least \\p max_events."] + #[doc = " @param max_events Maximum number of events to read."] + #[doc = " @return On success returns the number of events read from the file"] + #[doc = "\t descriptor, on failure return -1."] + #[doc = " @note This function will block if no event was queued for the line request."] + #[doc = " @note Any exising events in the buffer are overwritten. This is not an"] + #[doc = " append operation."] + pub fn gpiod_line_request_read_edge_event( + request: *mut gpiod_line_request, + buffer: *mut gpiod_edge_event_buffer, + max_events: size_t, + ) -> ::std::os::raw::c_int; +} +pub const GPIOD_EDGE_EVENT_RISING_EDGE: ::std::os::raw::c_uint = 1; +pub const GPIOD_EDGE_EVENT_FALLING_EDGE: ::std::os::raw::c_uint = 2; +#[doc = " @brief Event types."] +pub type _bindgen_ty_8 = ::std::os::raw::c_uint; +extern "C" { + #[doc = " @brief Free the edge event object."] + #[doc = " @param event Edge event object to free."] + pub fn gpiod_edge_event_free(event: *mut gpiod_edge_event); +} +extern "C" { + #[doc = " @brief Copy the edge event object."] + #[doc = " @param event Edge event to copy."] + #[doc = " @return Copy of the edge event or NULL on error. The returned object must"] + #[doc = "\t be freed by the caller using ::gpiod_edge_event_free."] + pub fn gpiod_edge_event_copy(event: *mut gpiod_edge_event) -> *mut gpiod_edge_event; +} +extern "C" { + #[doc = " @brief Get the event type."] + #[doc = " @param event GPIO edge event."] + #[doc = " @return The event type (::GPIOD_EDGE_EVENT_RISING_EDGE or"] + #[doc = "\t ::GPIOD_EDGE_EVENT_FALLING_EDGE)."] + pub fn gpiod_edge_event_get_event_type(event: *mut gpiod_edge_event) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Get the timestamp of the event."] + #[doc = " @param event GPIO edge event."] + #[doc = " @return Timestamp in nanoseconds."] + #[doc = " @note The source clock for the timestamp depends on the event_clock"] + #[doc = "\t setting for the line."] + pub fn gpiod_edge_event_get_timestamp_ns(event: *mut gpiod_edge_event) -> u64; +} +extern "C" { + #[doc = " @brief Get the offset of the line which triggered the event."] + #[doc = " @param event GPIO edge event."] + #[doc = " @return Line offset."] + pub fn gpiod_edge_event_get_line_offset(event: *mut gpiod_edge_event) + -> ::std::os::raw::c_uint; +} +extern "C" { + #[doc = " @brief Get the global sequence number of the event."] + #[doc = " @param event GPIO edge event."] + #[doc = " @return Sequence number of the event in the series of events for all lines"] + #[doc = "\t in the associated line request."] + pub fn gpiod_edge_event_get_global_seqno( + event: *mut gpiod_edge_event, + ) -> ::std::os::raw::c_ulong; +} +extern "C" { + #[doc = " @brief Get the event sequence number specific to the line."] + #[doc = " @param event GPIO edge event."] + #[doc = " @return Sequence number of the event in the series of events only for this"] + #[doc = "\t line within the lifetime of the associated line request."] + pub fn gpiod_edge_event_get_line_seqno(event: *mut gpiod_edge_event) + -> ::std::os::raw::c_ulong; +} +extern "C" { + #[doc = " @brief Create a new edge event buffer."] + #[doc = " @param capacity Number of events the buffer can store (min = 1, max = 1024)."] + #[doc = " @return New edge event buffer or NULL on error."] + #[doc = " @note If capacity equals 0, it will be set to a default value of 64. If"] + #[doc = "\t capacity is larger than 1024, it will be limited to 1024."] + #[doc = " @note The user space buffer is independent of the kernel buffer"] + #[doc = "\t (::gpiod_request_config_set_event_buffer_size). As the user space"] + #[doc = "\t buffer is filled from the kernel buffer, there is no benefit making"] + #[doc = "\t the user space buffer larger than the kernel buffer."] + #[doc = "\t The default kernel buffer size for each request is 16*num_lines."] + pub fn gpiod_edge_event_buffer_new(capacity: size_t) -> *mut gpiod_edge_event_buffer; +} +extern "C" { + #[doc = " @brief Get the capacity (the max number of events that can be stored) of"] + #[doc = "\t the event buffer."] + #[doc = " @param buffer Edge event buffer."] + #[doc = " @return The capacity of the buffer."] + pub fn gpiod_edge_event_buffer_get_capacity(buffer: *mut gpiod_edge_event_buffer) -> size_t; +} +extern "C" { + #[doc = " @brief Free the edge event buffer and release all associated resources."] + #[doc = " @param buffer Edge event buffer to free."] + pub fn gpiod_edge_event_buffer_free(buffer: *mut gpiod_edge_event_buffer); +} +extern "C" { + #[doc = " @brief Get an event stored in the buffer."] + #[doc = " @param buffer Edge event buffer."] + #[doc = " @param index Index of the event in the buffer."] + #[doc = " @return Pointer to an event stored in the buffer. The lifetime of the"] + #[doc = "\t event is tied to the buffer object. Users must not free the event"] + #[doc = "\t returned by this function."] + pub fn gpiod_edge_event_buffer_get_event( + buffer: *mut gpiod_edge_event_buffer, + index: ::std::os::raw::c_ulong, + ) -> *mut gpiod_edge_event; +} +extern "C" { + #[doc = " @brief Get the number of events a buffer has stored."] + #[doc = " @param buffer Edge event buffer."] + #[doc = " @return Number of events stored in the buffer."] + pub fn gpiod_edge_event_buffer_get_num_events(buffer: *mut gpiod_edge_event_buffer) -> size_t; +} +extern "C" { + #[doc = " @brief Check if the file pointed to by path is a GPIO chip character device."] + #[doc = " @param path Path to check."] + #[doc = " @return True if the file exists and is either a GPIO chip character device"] + #[doc = "\t or a symbolic link to one."] + pub fn gpiod_is_gpiochip_device(path: *const ::std::os::raw::c_char) -> bool; +} +extern "C" { + #[doc = " @brief Get the API version of the library as a human-readable string."] + #[doc = " @return Human-readable string containing the library version."] + pub fn gpiod_version_string() -> *const ::std::os::raw::c_char; +} From patchwork Fri Oct 14 10:47:20 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 615143 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 17508C4332F for ; Fri, 14 Oct 2022 10:47:48 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229587AbiJNKrr (ORCPT ); Fri, 14 Oct 2022 06:47:47 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47504 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229586AbiJNKro (ORCPT ); Fri, 14 Oct 2022 06:47:44 -0400 Received: from mail-pf1-x435.google.com (mail-pf1-x435.google.com [IPv6:2607:f8b0:4864:20::435]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A354126F6 for ; Fri, 14 Oct 2022 03:47:41 -0700 (PDT) Received: by mail-pf1-x435.google.com with SMTP id i3so4530168pfc.11 for ; Fri, 14 Oct 2022 03: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=hNCt789s245RFjE6r4SbpI9VZNJkPyv0DvXQksWLcQw=; b=NXlsUQ/rLCJH9OQu3O8D3TE096uPnwF8kOmSGJZ2wdiusEU24K0Z7xlDSYn6WjCM3f vzIKEKsWpbaGsax8uKCekP6uEBNznEj+K+DWJDuyTvMTlfRbreCyXEkosMk3NJ4GQwGa W9KuWe9Ejj4K8ol927lmVUc7V+8J2y7AYXnG6XzVPtnTnZZ5ev2Vr10jL3V41byAAqZP CeqKO34Rhap13sJnGB6p6/V0Pa/VX1rQS7s6RDijJosKgjCBYV5Kx7q2rwoEXhZr/5NF bzZEn7hotF7r/fwl1CGZWj+DhcopASr37gL9l5NhcHcNfeB2kpEUGy7IJ9s376qGo+Ia A9NA== 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=hNCt789s245RFjE6r4SbpI9VZNJkPyv0DvXQksWLcQw=; b=dy2nxhx2POiXj7dbyhoq8H/dpXBJbTUJ288B+Y4VNuM1f8qAM3eJ7IV5h1bwoWMERn NiGAa8zDG3htFymNME4R7z8WW50flTmyi7MoKpK4I0r232BiZZjCt3ZLpOpn+DTmeIos 6rft2QGhPpZPve2d4iMz+iQ1LgJoEnzK/qsH7znw4Ro+MORwdsavsiZGCzIjtevRAy+f fGZz/dDzVmEG3skoVh5o7i3vgPo+t8RRf/wvMNomj+X03I1uwy7gQEFNtMeKewIxLheM Frz57o03920YsOi2RrYnVgu2pILS0m5ttwxW2V7cOEU1eipKetG+eiB2xd2E8ZUtZmzH pDlw== X-Gm-Message-State: ACrzQf2mhq3nrKMyuYMW4zg5+pMrQPCsaZefGFfNGZa0qNOPf2hHrKku MiXb0s9wVZ2D4I4jk7jSqln0xA== X-Google-Smtp-Source: AMsMyM57vjJSMkRh65YGN+p2v0xufiD0NgJWjGq7truMPK6CYAu//Sk33haiFWWeCN9XBTb/yNKy1g== X-Received: by 2002:a63:814a:0:b0:460:9025:6e4a with SMTP id t71-20020a63814a000000b0046090256e4amr4057258pgd.135.1665744459614; Fri, 14 Oct 2022 03:47:39 -0700 (PDT) Received: from localhost ([122.172.86.128]) by smtp.gmail.com with ESMTPSA id n14-20020a170902d2ce00b0017f36638010sm1414283plc.276.2022.10.14.03.47.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 14 Oct 2022 03:47:38 -0700 (PDT) From: Viresh Kumar To: Linus Walleij , Bartosz Golaszewski Cc: Viresh Kumar , Vincent Guittot , linux-gpio@vger.kernel.org, Kent Gibson , Miguel Ojeda , Wedson Almeida Filho , =?utf-8?q?Alex_Benn=C3=A9e?= , stratos-dev@op-lists.linaro.org, Gerard Ryan Subject: [PATCH V7 3/8] libgpiod: Add rust wrapper crate Date: Fri, 14 Oct 2022 16:17:20 +0530 Message-Id: <6a94249d2d69bc5c1907fea9ed80c9d7e34278aa.1665744170.git.viresh.kumar@linaro.org> X-Mailer: git-send-email 2.31.1.272.g89b43f80a514 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org Add rust wrapper crate, around the libpiod-sys crate added earlier, to provide a convenient interface for the users. Signed-off-by: Viresh Kumar --- bindings/rust/Cargo.toml | 1 + bindings/rust/libgpiod/Cargo.toml | 13 + bindings/rust/libgpiod/src/chip.rs | 253 ++++++++++ bindings/rust/libgpiod/src/edge_event.rs | 101 ++++ bindings/rust/libgpiod/src/event_buffer.rs | 66 +++ bindings/rust/libgpiod/src/info_event.rs | 68 +++ bindings/rust/libgpiod/src/lib.rs | 471 +++++++++++++++++++ bindings/rust/libgpiod/src/line_config.rs | 118 +++++ bindings/rust/libgpiod/src/line_info.rs | 180 +++++++ bindings/rust/libgpiod/src/line_request.rs | 246 ++++++++++ bindings/rust/libgpiod/src/line_settings.rs | 277 +++++++++++ bindings/rust/libgpiod/src/request_config.rs | 96 ++++ 12 files changed, 1890 insertions(+) create mode 100644 bindings/rust/libgpiod/Cargo.toml create mode 100644 bindings/rust/libgpiod/src/chip.rs create mode 100644 bindings/rust/libgpiod/src/edge_event.rs create mode 100644 bindings/rust/libgpiod/src/event_buffer.rs create mode 100644 bindings/rust/libgpiod/src/info_event.rs create mode 100644 bindings/rust/libgpiod/src/lib.rs create mode 100644 bindings/rust/libgpiod/src/line_config.rs create mode 100644 bindings/rust/libgpiod/src/line_info.rs create mode 100644 bindings/rust/libgpiod/src/line_request.rs create mode 100644 bindings/rust/libgpiod/src/line_settings.rs create mode 100644 bindings/rust/libgpiod/src/request_config.rs diff --git a/bindings/rust/Cargo.toml b/bindings/rust/Cargo.toml index d0b3a3c88ff1..1e57ef2c0002 100644 --- a/bindings/rust/Cargo.toml +++ b/bindings/rust/Cargo.toml @@ -2,4 +2,5 @@ members = [ "libgpiod-sys", + "libgpiod" ] diff --git a/bindings/rust/libgpiod/Cargo.toml b/bindings/rust/libgpiod/Cargo.toml new file mode 100644 index 000000000000..f25242abb153 --- /dev/null +++ b/bindings/rust/libgpiod/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "libgpiod" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +intmap = "2.0.0" +libc = ">=0.2.39" +libgpiod-sys = { path = "../libgpiod-sys" } +thiserror = "1.0" +vmm-sys-util = "=0.10.0" diff --git a/bindings/rust/libgpiod/src/chip.rs b/bindings/rust/libgpiod/src/chip.rs new file mode 100644 index 000000000000..4f52c3f141f4 --- /dev/null +++ b/bindings/rust/libgpiod/src/chip.rs @@ -0,0 +1,253 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use libc::strlen; +use std::os::raw::c_char; +use std::path::Path; +use std::sync::Arc; +use std::time::Duration; +use std::{slice, str}; + +use vmm_sys_util::errno::Error as Errno; + +use super::{gpiod, info, line, request, Error, Offset, OperationType, Result}; + +#[derive(Clone, Debug, Eq, PartialEq)] +pub(crate) struct Internal { + chip: *mut gpiod::gpiod_chip, +} + +impl Internal { + /// Find a chip by path. + pub(crate) fn open>(path: &P) -> Result { + // Null-terminate the string + let path = path.as_ref().to_string_lossy() + "\0"; + + let chip = unsafe { gpiod::gpiod_chip_open(path.as_ptr() as *const c_char) }; + if chip.is_null() { + return Err(Error::OperationFailed( + OperationType::ChipOpen, + Errno::last(), + )); + } + + Ok(Self { chip }) + } + + /// Private helper, Returns gpiod_chip + pub(crate) fn chip(&self) -> *mut gpiod::gpiod_chip { + self.chip + } +} + +impl Drop for Internal { + /// Close the chip and release all associated resources. + fn drop(&mut self) { + unsafe { gpiod::gpiod_chip_close(self.chip) } + } +} + +/// GPIO chip +/// +/// A GPIO chip object is associated with an open file descriptor to the GPIO +/// character device. It exposes basic information about the chip and allows +/// callers to retrieve information about each line, watch lines for state +/// changes and make line requests. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Chip { + ichip: Arc, + info: Info, +} + +unsafe impl Send for Chip {} +unsafe impl Sync for Chip {} + +impl Chip { + /// Find a chip by path. + pub fn open>(path: &P) -> Result { + let ichip = Arc::new(Internal::open(path)?); + let info = Info::new(ichip.clone())?; + + Ok(Self { ichip, info }) + } + + /// Get the chip name as represented in the kernel. + pub fn name(&self) -> Result<&str> { + self.info.name() + } + + /// Get the chip label as represented in the kernel. + pub fn label(&self) -> Result<&str> { + self.info.label() + } + + /// Get the number of GPIO lines exposed by the chip. + pub fn num_lines(&self) -> usize { + self.info.num_lines() + } + + /// Get the path used to find the chip. + pub fn path(&self) -> Result<&str> { + // SAFETY: The string returned by libgpiod is guaranteed to live as long + // as the `struct Chip`. + let path = unsafe { gpiod::gpiod_chip_get_path(self.ichip.chip()) }; + + // SAFETY: The string is guaranteed to be valid here by the C API. + str::from_utf8(unsafe { slice::from_raw_parts(path as *const u8, strlen(path) as usize) }) + .map_err(Error::StringNotUtf8) + } + + /// Get information about the chip. + pub fn info(&self) -> Result { + Info::new(self.ichip.clone()) + } + + /// Get a snapshot of information about the line. + pub fn line_info(&self, offset: Offset) -> Result { + line::Info::new(self.ichip.clone(), offset) + } + + /// Get the current snapshot of information about the line at given offset and start watching + /// it for future changes. + pub fn watch_line_info(&self, offset: Offset) -> Result { + line::Info::new_watch(self.ichip.clone(), offset) + } + + /// Stop watching a line + pub fn unwatch(&self, offset: Offset) { + unsafe { + gpiod::gpiod_chip_unwatch_line_info(self.ichip.chip(), offset); + } + } + + /// Get the file descriptor associated with the chip. + /// + /// The returned file descriptor must not be closed by the caller, else other methods for the + /// `struct Chip` may fail. + pub fn fd(&self) -> Result { + let fd = unsafe { gpiod::gpiod_chip_get_fd(self.ichip.chip()) }; + + if fd < 0 { + Err(Error::OperationFailed( + OperationType::ChipGetFd, + Errno::last(), + )) + } else { + Ok(fd as u32) + } + } + + /// Wait for line status events on any of the watched lines on the chip. + pub fn wait_info_event(&self, timeout: Option) -> Result { + let timeout = match timeout { + Some(x) => x.as_nanos() as i64, + // Block indefinitely + None => -1, + }; + + let ret = unsafe { gpiod::gpiod_chip_wait_info_event(self.ichip.chip(), timeout) }; + + match ret { + -1 => Err(Error::OperationFailed( + OperationType::ChipWaitInfoEvent, + Errno::last(), + )), + 0 => Ok(false), + _ => Ok(true), + } + } + + /// Read a single line status change event from the chip. If no events are + /// pending, this function will block. + pub fn read_info_event(&self) -> Result { + info::Event::new(&self.ichip) + } + + /// Map a GPIO line's name to its offset within the chip. + pub fn line_offset_from_name(&self, name: &str) -> Result { + // Null-terminate the string + let name = name.to_owned() + "\0"; + + let ret = unsafe { + gpiod::gpiod_chip_get_line_offset_from_name( + self.ichip.chip(), + name.as_ptr() as *const c_char, + ) + }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::ChipGetLine, + Errno::last(), + )) + } else { + Ok(ret as u32) + } + } + + /// Request a set of lines for exclusive usage. + pub fn request_lines( + &self, + rconfig: &request::Config, + lconfig: &line::Config, + ) -> Result { + line::Request::new(&self.ichip, rconfig, lconfig) + } +} + +/// GPIO chip Information +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Info { + info: *mut gpiod::gpiod_chip_info, +} + +impl Info { + /// Find a GPIO chip by path. + pub(crate) fn new(chip: Arc) -> Result { + let info = unsafe { gpiod::gpiod_chip_get_info(chip.chip()) }; + if info.is_null() { + return Err(Error::OperationFailed( + OperationType::ChipInfoGet, + Errno::last(), + )); + } + + Ok(Self { info }) + } + + /// Get the GPIO chip name as represented in the kernel. + pub(crate) fn name(&self) -> Result<&str> { + // SAFETY: The string returned by libgpiod is guaranteed to live as long + // as the `struct Chip`. + let name = unsafe { gpiod::gpiod_chip_info_get_name(self.info) }; + + // SAFETY: The string is guaranteed to be valid here by the C API. + str::from_utf8(unsafe { slice::from_raw_parts(name as *const u8, strlen(name) as usize) }) + .map_err(Error::StringNotUtf8) + } + + /// Get the GPIO chip label as represented in the kernel. + pub(crate) fn label(&self) -> Result<&str> { + // SAFETY: The string returned by libgpiod is guaranteed to live as long + // as the `struct Chip`. + let label = unsafe { gpiod::gpiod_chip_info_get_label(self.info) }; + + // SAFETY: The string is guaranteed to be valid here by the C API. + str::from_utf8(unsafe { slice::from_raw_parts(label as *const u8, strlen(label) as usize) }) + .map_err(Error::StringNotUtf8) + } + + /// Get the number of GPIO lines exposed by the chip. + pub(crate) fn num_lines(&self) -> usize { + unsafe { gpiod::gpiod_chip_info_get_num_lines(self.info) as usize } + } +} + +impl Drop for Info { + /// Close the GPIO chip info and release all associated resources. + fn drop(&mut self) { + unsafe { gpiod::gpiod_chip_info_free(self.info) } + } +} diff --git a/bindings/rust/libgpiod/src/edge_event.rs b/bindings/rust/libgpiod/src/edge_event.rs new file mode 100644 index 000000000000..12c0fd73f778 --- /dev/null +++ b/bindings/rust/libgpiod/src/edge_event.rs @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use std::time::Duration; + +use vmm_sys_util::errno::Error as Errno; + +use super::{edge::event::Buffer, gpiod, EdgeKind, Error, Offset, OperationType, Result}; + +/// Line edge events handling +/// +/// An edge event object contains information about a single line edge event. +/// It contains the event type, timestamp and the offset of the line on which +/// the event occurred as well as two sequence numbers (global for all lines +/// in the associated request and local for this line only). +/// +/// Edge events are stored into an edge-event buffer object to improve +/// performance and to limit the number of memory allocations when a large +/// number of events are being read. + +#[derive(Debug, Eq, PartialEq)] +pub struct Event<'b> { + buffer: Option<&'b Buffer>, + event: *mut gpiod::gpiod_edge_event, +} + +impl<'b> Event<'b> { + /// Get an event stored in the buffer. + pub(crate) fn new(buffer: &'b Buffer, index: u64) -> Result { + let event = unsafe { gpiod::gpiod_edge_event_buffer_get_event(buffer.buffer(), index) }; + if event.is_null() { + return Err(Error::OperationFailed( + OperationType::EdgeEventBufferGetEvent, + Errno::last(), + )); + } + + Ok(Self { + buffer: Some(buffer), + event, + }) + } + + pub fn event_clone(event: &Event) -> Result { + let event = unsafe { gpiod::gpiod_edge_event_copy(event.event) }; + if event.is_null() { + return Err(Error::OperationFailed( + OperationType::EdgeEventCopy, + Errno::last(), + )); + } + + Ok(Self { + buffer: None, + event, + }) + } + + /// Get the event type. + pub fn event_type(&self) -> Result { + EdgeKind::new(unsafe { gpiod::gpiod_edge_event_get_event_type(self.event) } as u32) + } + + /// Get the timestamp of the event. + pub fn timestamp(&self) -> Duration { + Duration::from_nanos(unsafe { gpiod::gpiod_edge_event_get_timestamp_ns(self.event) }) + } + + /// Get the offset of the line on which the event was triggered. + pub fn line_offset(&self) -> Offset { + unsafe { gpiod::gpiod_edge_event_get_line_offset(self.event) } + } + + /// Get the global sequence number of the event. + /// + /// Returns sequence number of the event relative to all lines in the + /// associated line request. + pub fn global_seqno(&self) -> u64 { + unsafe { gpiod::gpiod_edge_event_get_global_seqno(self.event) } + } + + /// Get the event sequence number specific to concerned line. + /// + /// Returns sequence number of the event relative to the line within the + /// lifetime of the associated line request. + pub fn line_seqno(&self) -> u64 { + unsafe { gpiod::gpiod_edge_event_get_line_seqno(self.event) } + } +} + +impl<'b> Drop for Event<'b> { + /// Free the edge event. + fn drop(&mut self) { + // Free the event only if a copy is made + if self.buffer.is_none() { + unsafe { gpiod::gpiod_edge_event_free(self.event) }; + } + } +} diff --git a/bindings/rust/libgpiod/src/event_buffer.rs b/bindings/rust/libgpiod/src/event_buffer.rs new file mode 100644 index 000000000000..11c8b5e1d7c9 --- /dev/null +++ b/bindings/rust/libgpiod/src/event_buffer.rs @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use std::os::raw::c_ulong; + +use vmm_sys_util::errno::Error as Errno; + +use super::{edge, gpiod, Error, OperationType, Result}; + +/// Line edge events buffer +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Buffer { + buffer: *mut gpiod::gpiod_edge_event_buffer, +} + +impl Buffer { + /// Create a new edge event buffer. + /// + /// If capacity equals 0, it will be set to a default value of 64. If + /// capacity is larger than 1024, it will be limited to 1024. + pub fn new(capacity: usize) -> Result { + let buffer = unsafe { gpiod::gpiod_edge_event_buffer_new(capacity as c_ulong) }; + if buffer.is_null() { + return Err(Error::OperationFailed( + OperationType::EdgeEventBufferNew, + Errno::last(), + )); + } + + Ok(Self { buffer }) + } + + /// Private helper, Returns gpiod_edge_event_buffer + pub(crate) fn buffer(&self) -> *mut gpiod::gpiod_edge_event_buffer { + self.buffer + } + + /// Get the capacity of the event buffer. + pub fn capacity(&self) -> usize { + unsafe { gpiod::gpiod_edge_event_buffer_get_capacity(self.buffer()) as usize } + } + + /// Read an event stored in the buffer. + pub fn event(&self, index: u64) -> Result { + edge::Event::new(self, index) + } + + /// Get the number of events the buffer contains. + pub fn len(&self) -> usize { + unsafe { gpiod::gpiod_edge_event_buffer_get_num_events(self.buffer()) as usize } + } + + /// Check if buffer is empty. + pub fn is_empty(&self) -> bool { + self.len() == 0 + } +} + +impl Drop for Buffer { + /// Free the edge event buffer and release all associated resources. + fn drop(&mut self) { + unsafe { gpiod::gpiod_edge_event_buffer_free(self.buffer) }; + } +} diff --git a/bindings/rust/libgpiod/src/info_event.rs b/bindings/rust/libgpiod/src/info_event.rs new file mode 100644 index 000000000000..d8be87df6679 --- /dev/null +++ b/bindings/rust/libgpiod/src/info_event.rs @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use std::sync::Arc; +use std::time::Duration; + +use vmm_sys_util::errno::Error as Errno; + +use super::{chip, gpiod, line, Error, InfoChangeKind, OperationType, Result}; + +/// Line status watch events +/// +/// Accessors for the info event objects allowing to monitor changes in GPIO +/// line state. +/// +/// Callers can be notified about changes in line's state using the interfaces +/// exposed by GPIO chips. Each info event contains information about the event +/// itself (timestamp, type) as well as a snapshot of line's state in the form +/// of a line-info object. + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Event { + event: *mut gpiod::gpiod_info_event, +} + +impl Event { + /// Get a single chip's line's status change event. + pub(crate) fn new(ichip: &Arc) -> Result { + let event = unsafe { gpiod::gpiod_chip_read_info_event(ichip.chip()) }; + if event.is_null() { + return Err(Error::OperationFailed( + OperationType::ChipReadInfoEvent, + Errno::last(), + )); + } + + Ok(Self { event }) + } + + /// Private helper, Returns gpiod_info_event + pub(crate) fn event(&self) -> *mut gpiod::gpiod_info_event { + self.event + } + + /// Get the event type of the status change event. + pub fn event_type(&self) -> Result { + InfoChangeKind::new(unsafe { gpiod::gpiod_info_event_get_event_type(self.event) } as u32) + } + + /// Get the timestamp of the event, read from the monotonic clock. + pub fn timestamp(&self) -> Duration { + Duration::from_nanos(unsafe { gpiod::gpiod_info_event_get_timestamp_ns(self.event) }) + } + + /// Get the line-info object associated with the event. + pub fn line_info(&self) -> Result { + line::Info::new_from_event(self) + } +} + +impl Drop for Event { + /// Free the info event object and release all associated resources. + fn drop(&mut self) { + unsafe { gpiod::gpiod_info_event_free(self.event) } + } +} diff --git a/bindings/rust/libgpiod/src/lib.rs b/bindings/rust/libgpiod/src/lib.rs new file mode 100644 index 000000000000..c5d974a9f4ea --- /dev/null +++ b/bindings/rust/libgpiod/src/lib.rs @@ -0,0 +1,471 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Rust wrappers for GPIOD APIs +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +//! libgpiod public API +//! +//! This is the complete documentation of the public Rust API made available to +//! users of libgpiod. +//! +//! The API is logically split into several parts such as: GPIO chip & line +//! operators, GPIO events handling etc. + +/// GPIO chip related definitions. +pub mod chip; + +mod edge_event; +mod event_buffer; + +/// GPIO chip edge event related definitions. +pub mod edge { + pub use crate::edge_event::*; + + /// GPIO chip edge event buffer related definitions. + pub mod event { + pub use crate::event_buffer::*; + } +} + +mod info_event; + +/// GPIO chip info event related definitions. +pub mod info { + pub use crate::info_event::*; +} + +mod line_config; +mod line_info; +mod line_request; +mod line_settings; + +/// GPIO chip line related definitions. +pub mod line { + pub use crate::line_config::*; + pub use crate::line_info::*; + pub use crate::line_request::*; + pub use crate::line_settings::*; +} + +mod request_config; + +/// GPIO chip request related definitions. +pub mod request { + pub use crate::request_config::*; +} + +use libgpiod_sys as gpiod; + +use intmap::IntMap; +use libc::strlen; +use std::fs; +use std::os::raw::c_char; +use std::path::Path; +use std::time::Duration; +use std::{fmt, slice, str}; + +use thiserror::Error as ThisError; +use vmm_sys_util::errno::Error as Errno; + +/// Operation types, used with OperationFailed() Error. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum OperationType { + ChipOpen, + ChipGetFd, + ChipWaitInfoEvent, + ChipGetLine, + ChipGetLineInfo, + ChipInfoGet, + ChipReadInfoEvent, + ChipWatchLineInfo, + EdgeEventBufferGetEvent, + EdgeEventCopy, + EdgeEventBufferNew, + InfoEventGetLineInfo, + LineConfigNew, + LineConfigAddSettings, + LineConfigGetOffsets, + LineConfigGetSetting, + LineRequest, + LineRequestReconfigLines, + LineRequestGetVal, + LineRequestGetValSubset, + LineRequestSetVal, + LineRequestSetValSubset, + LineRequestReadEdgeEvent, + LineRequestWaitEdgeEvent, + LineSettingsNew, + LineSettingsCopy, + LineSettingsGetOutVal, + LineSettingsSetDirection, + LineSettingsSetEdgeDetection, + LineSettingsSetBias, + LineSettingsSetDrive, + LineSettingsSetActiveLow, + LineSettingsSetDebouncePeriod, + LineSettingsSetEventClock, + LineSettingsSetOutputValue, + RequestConfigNew, + RequestConfigGetConsumer, + SimBankGetVal, + SimBankNew, + SimBankSetLabel, + SimBankSetNumLines, + SimBankSetLineName, + SimBankSetPull, + SimBankHogLine, + SimCtxNew, + SimDevNew, + SimDevEnable, + SimDevDisable, +} + +impl fmt::Display for OperationType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +/// Result of libgpiod operations. +pub type Result = std::result::Result; + +/// Error codes for libgpiod operations. +#[derive(Copy, Clone, Debug, PartialEq, ThisError)] +pub enum Error { + #[error("Failed to get {0}")] + NullString(&'static str), + #[error("String not utf8: {0:?}")] + StringNotUtf8(str::Utf8Error), + #[error("Invalid enum {0} value: {1}")] + InvalidEnumValue(&'static str, u32), + #[error("Operation {0} Failed: {1}")] + OperationFailed(OperationType, Errno), + #[error("Invalid Arguments")] + InvalidArguments, + #[error("Std Io Error")] + IoError, +} + +/// Value settings. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum Value { + /// Active + Active, + /// Inactive + InActive, +} + +/// Maps offset to Value. +pub type ValueMap = IntMap; + +impl Value { + pub fn new(val: i32) -> Result { + match val { + 0 => Ok(Value::InActive), + 1 => Ok(Value::Active), + _ => Err(Error::InvalidEnumValue("Value", val as u32)), + } + } + + fn value(&self) -> i32 { + match self { + Value::Active => 1, + Value::InActive => 0, + } + } +} + +/// Offset type. +pub type Offset = u32; + +/// Direction settings. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum Direction { + /// Request the line(s), but don't change direction. + AsIs, + /// Direction is input - for reading the value of an externally driven GPIO line. + Input, + /// Direction is output - for driving the GPIO line. + Output, +} + +impl Direction { + fn new(dir: u32) -> Result { + match dir { + gpiod::GPIOD_LINE_DIRECTION_AS_IS => Ok(Direction::AsIs), + gpiod::GPIOD_LINE_DIRECTION_INPUT => Ok(Direction::Input), + gpiod::GPIOD_LINE_DIRECTION_OUTPUT => Ok(Direction::Output), + _ => Err(Error::InvalidEnumValue("Direction", dir)), + } + } + + fn gpiod_direction(&self) -> u32 { + match self { + Direction::AsIs => gpiod::GPIOD_LINE_DIRECTION_AS_IS, + Direction::Input => gpiod::GPIOD_LINE_DIRECTION_INPUT, + Direction::Output => gpiod::GPIOD_LINE_DIRECTION_OUTPUT, + } + } +} + +/// Internal bias settings. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum Bias { + /// The internal bias is disabled. + Disabled, + /// The internal pull-up bias is enabled. + PullUp, + /// The internal pull-down bias is enabled. + PullDown, +} + +impl Bias { + fn new(bias: u32) -> Result> { + match bias { + gpiod::GPIOD_LINE_BIAS_UNKNOWN => Ok(None), + gpiod::GPIOD_LINE_BIAS_AS_IS => Ok(None), + gpiod::GPIOD_LINE_BIAS_DISABLED => Ok(Some(Bias::Disabled)), + gpiod::GPIOD_LINE_BIAS_PULL_UP => Ok(Some(Bias::PullUp)), + gpiod::GPIOD_LINE_BIAS_PULL_DOWN => Ok(Some(Bias::PullDown)), + _ => Err(Error::InvalidEnumValue("Bias", bias)), + } + } + + fn gpiod_bias(bias: Option) -> u32 { + match bias { + None => gpiod::GPIOD_LINE_BIAS_AS_IS, + Some(bias) => match bias { + Bias::Disabled => gpiod::GPIOD_LINE_BIAS_DISABLED, + Bias::PullUp => gpiod::GPIOD_LINE_BIAS_PULL_UP, + Bias::PullDown => gpiod::GPIOD_LINE_BIAS_PULL_DOWN, + }, + } + } +} + +/// Drive settings. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum Drive { + /// Drive setting is push-pull. + PushPull, + /// Line output is open-drain. + OpenDrain, + /// Line output is open-source. + OpenSource, +} + +impl Drive { + fn new(drive: u32) -> Result { + match drive { + gpiod::GPIOD_LINE_DRIVE_PUSH_PULL => Ok(Drive::PushPull), + gpiod::GPIOD_LINE_DRIVE_OPEN_DRAIN => Ok(Drive::OpenDrain), + gpiod::GPIOD_LINE_DRIVE_OPEN_SOURCE => Ok(Drive::OpenSource), + _ => Err(Error::InvalidEnumValue("Drive", drive)), + } + } + + fn gpiod_drive(&self) -> u32 { + match self { + Drive::PushPull => gpiod::GPIOD_LINE_DRIVE_PUSH_PULL, + Drive::OpenDrain => gpiod::GPIOD_LINE_DRIVE_OPEN_DRAIN, + Drive::OpenSource => gpiod::GPIOD_LINE_DRIVE_OPEN_SOURCE, + } + } +} + +/// Edge detection settings. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum Edge { + /// Line detects rising edge events. + Rising, + /// Line detects falling edge events. + Falling, + /// Line detects both rising and falling edge events. + Both, +} + +impl Edge { + fn new(edge: u32) -> Result> { + match edge { + gpiod::GPIOD_LINE_EDGE_NONE => Ok(None), + gpiod::GPIOD_LINE_EDGE_RISING => Ok(Some(Edge::Rising)), + gpiod::GPIOD_LINE_EDGE_FALLING => Ok(Some(Edge::Falling)), + gpiod::GPIOD_LINE_EDGE_BOTH => Ok(Some(Edge::Both)), + _ => Err(Error::InvalidEnumValue("Edge", edge)), + } + } + + fn gpiod_edge(edge: Option) -> u32 { + match edge { + None => gpiod::GPIOD_LINE_EDGE_NONE, + Some(edge) => match edge { + Edge::Rising => gpiod::GPIOD_LINE_EDGE_RISING, + Edge::Falling => gpiod::GPIOD_LINE_EDGE_FALLING, + Edge::Both => gpiod::GPIOD_LINE_EDGE_BOTH, + }, + } + } +} + +/// Line setting kind. +#[derive(Copy, Clone, Debug, 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 { + fn new(clock: u32) -> Result { + match clock { + gpiod::GPIOD_LINE_EVENT_CLOCK_MONOTONIC => Ok(EventClock::Monotonic), + gpiod::GPIOD_LINE_EVENT_CLOCK_REALTIME => Ok(EventClock::Realtime), + gpiod::GPIOD_LINE_EVENT_CLOCK_HTE => Ok(EventClock::HTE), + _ => Err(Error::InvalidEnumValue("Eventclock", clock)), + } + } + + fn gpiod_clock(&self) -> u32 { + match self { + EventClock::Monotonic => gpiod::GPIOD_LINE_EVENT_CLOCK_MONOTONIC, + EventClock::Realtime => gpiod::GPIOD_LINE_EVENT_CLOCK_REALTIME, + EventClock::HTE => gpiod::GPIOD_LINE_EVENT_CLOCK_HTE, + } + } +} + +/// Line status change event types. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum InfoChangeKind { + /// Line has been requested. + LineRequested, + /// Previously requested line has been released. + LineReleased, + /// Line configuration has changed. + LineConfigChanged, +} + +impl InfoChangeKind { + fn new(kind: u32) -> Result { + match kind { + gpiod::GPIOD_INFO_EVENT_LINE_REQUESTED => Ok(InfoChangeKind::LineRequested), + gpiod::GPIOD_INFO_EVENT_LINE_RELEASED => Ok(InfoChangeKind::LineReleased), + gpiod::GPIOD_INFO_EVENT_LINE_CONFIG_CHANGED => Ok(InfoChangeKind::LineConfigChanged), + _ => Err(Error::InvalidEnumValue("InfoChangeKind", kind)), + } + } +} + +/// Edge event types. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum EdgeKind { + /// Rising edge event. + Rising, + /// Falling edge event. + Falling, +} + +impl EdgeKind { + fn new(kind: u32) -> Result { + match kind { + gpiod::GPIOD_EDGE_EVENT_RISING_EDGE => Ok(EdgeKind::Rising), + gpiod::GPIOD_EDGE_EVENT_FALLING_EDGE => Ok(EdgeKind::Falling), + _ => Err(Error::InvalidEnumValue("EdgeEvent", kind)), + } + } +} + +/// Various libgpiod-related functions. + +/// Check if the file pointed to by path is a GPIO chip character device. +/// +/// Returns true if the file exists and is a GPIO chip character device or a +/// symbolic link to it. +pub fn is_gpiochip_device>(path: &P) -> bool { + // Null-terminate the string + let path = path.as_ref().to_string_lossy() + "\0"; + + unsafe { gpiod::gpiod_is_gpiochip_device(path.as_ptr() as *const c_char) } +} + +/// Iterator for GPIO devices. +pub fn gpiochip_devices>(path: &P) -> Result> { + let mut chips = Vec::new(); + + for entry in fs::read_dir(path).map_err(|_| Error::IoError)?.flatten() { + let path = entry.path(); + + if is_gpiochip_device(&path) { + chips.push(chip::Chip::open(&path)?); + } + } + + Ok(chips) +} + +/// Get the API version of the library as a human-readable string. +pub fn version_string() -> Result<&'static str> { + // SAFETY: The string returned by libgpiod is guaranteed to live forever. + let version = unsafe { gpiod::gpiod_version_string() }; + + if version.is_null() { + return Err(Error::NullString("GPIO library version")); + } + + // SAFETY: The string is guaranteed to be valid here by the C API. + str::from_utf8(unsafe { slice::from_raw_parts(version as *const u8, strlen(version) as usize) }) + .map_err(Error::StringNotUtf8) +} diff --git a/bindings/rust/libgpiod/src/line_config.rs b/bindings/rust/libgpiod/src/line_config.rs new file mode 100644 index 000000000000..ea25452fd1d6 --- /dev/null +++ b/bindings/rust/libgpiod/src/line_config.rs @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use std::os::raw::c_ulong; +use std::slice::from_raw_parts; + +use vmm_sys_util::errno::Error as Errno; + +use super::{gpiod, line::Settings, Error, Offset, OperationType, Result}; + +/// Line configuration objects. +/// +/// The line-config object contains the configuration for lines that can be +/// used in two cases: +/// - when making a line request +/// - when reconfiguring a set of already requested lines. +/// +/// A new line-config object is empty. Using it in a request will lead to an +/// error. In order to a line-config to become useful, it needs to be assigned +/// at least one offset-to-settings mapping by calling +/// ::gpiod_line_config_add_line_settings. +/// +/// When calling ::gpiod_chip_request_lines, the library will request all +/// offsets that were assigned settings in the order that they were assigned. + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Config { + config: *mut gpiod::gpiod_line_config, +} + +impl Config { + /// Create a new line config object. + pub fn new() -> Result { + let config = unsafe { gpiod::gpiod_line_config_new() }; + + if config.is_null() { + return Err(Error::OperationFailed( + OperationType::LineConfigNew, + Errno::last(), + )); + } + + Ok(Self { config }) + } + + /// Private helper, Returns gpiod_line_config + pub(crate) fn config(&self) -> *mut gpiod::gpiod_line_config { + self.config + } + + /// Resets the entire configuration stored in the object. This is useful if + /// the user wants to reuse the object without reallocating it. + pub fn reset(&mut self) { + unsafe { gpiod::gpiod_line_config_reset(self.config) } + } + + /// Add line settings for a set of offsets. + pub fn add_line_settings(&self, offsets: &[Offset], settings: Settings) -> Result<()> { + let ret = unsafe { + gpiod::gpiod_line_config_add_line_settings( + self.config, + offsets.as_ptr(), + offsets.len() as c_ulong, + settings.settings(), + ) + }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::LineConfigAddSettings, + Errno::last(), + )) + } else { + Ok(()) + } + } + + /// Get line settings for offset. + pub fn line_settings(&self, offset: Offset) -> Result { + let settings = unsafe { gpiod::gpiod_line_config_get_line_settings(self.config, offset) }; + + if settings.is_null() { + return Err(Error::OperationFailed( + OperationType::LineConfigGetSetting, + Errno::last(), + )); + } + + Ok(Settings::new_with_settings(settings)) + } + + /// Get configured offsets. + pub fn offsets(&self) -> Result> { + let mut num: u64 = 0; + let mut offsets: *mut Offset = std::ptr::null_mut(); + + let ret = + unsafe { gpiod::gpiod_line_config_get_offsets(self.config, &mut num, &mut offsets) }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::LineConfigGetOffsets, + Errno::last(), + )) + } else { + Ok(unsafe { from_raw_parts(offsets as *const Offset, num as usize).to_vec() }) + } + } +} + +impl Drop for Config { + /// Free the line config object and release all associated resources. + fn drop(&mut self) { + unsafe { gpiod::gpiod_line_config_free(self.config) } + } +} diff --git a/bindings/rust/libgpiod/src/line_info.rs b/bindings/rust/libgpiod/src/line_info.rs new file mode 100644 index 000000000000..9db51fc30efd --- /dev/null +++ b/bindings/rust/libgpiod/src/line_info.rs @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use libc::strlen; +use std::sync::Arc; +use std::time::Duration; +use std::{slice, str}; + +use vmm_sys_util::errno::Error as Errno; + +use super::{ + chip, gpiod, info, Bias, Direction, Drive, Edge, Error, EventClock, Offset, OperationType, + Result, +}; + +/// Line info +/// +/// Exposes functions for retrieving kernel information about both requested and +/// free lines. Line info object contains an immutable snapshot of a line's status. +/// +/// The line info contains all the publicly available information about a +/// line, which does not include the line value. The line must be requested +/// to access the line value. + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Info { + info: *mut gpiod::gpiod_line_info, + info_event: bool, +} + +impl Info { + /// Get a snapshot of information about the line and optionally start watching it for changes. + pub(crate) fn new(ichip: Arc, offset: Offset) -> Result { + let info = unsafe { gpiod::gpiod_chip_get_line_info(ichip.chip(), offset) }; + + if info.is_null() { + return Err(Error::OperationFailed( + OperationType::ChipGetLineInfo, + Errno::last(), + )); + } + + Ok(Self { + info, + info_event: false, + }) + } + + pub(crate) fn new_watch(ichip: Arc, offset: Offset) -> Result { + let info = unsafe { gpiod::gpiod_chip_watch_line_info(ichip.chip(), offset) }; + + if info.is_null() { + return Err(Error::OperationFailed( + OperationType::ChipWatchLineInfo, + Errno::last(), + )); + } + + Ok(Self { + info, + info_event: false, + }) + } + + /// Get the Line info object associated with an event. + pub(crate) fn new_from_event(event: &info::Event) -> Result { + let info = unsafe { gpiod::gpiod_info_event_get_line_info(event.event()) }; + + if info.is_null() { + return Err(Error::OperationFailed( + OperationType::InfoEventGetLineInfo, + Errno::last(), + )); + } + + Ok(Self { + info, + info_event: true, + }) + } + + /// Get the offset of the line within the GPIO chip. + /// + /// The offset uniquely identifies the line on the chip. The combination of the chip and offset + /// uniquely identifies the line within the system. + + pub fn offset(&self) -> Offset { + unsafe { gpiod::gpiod_line_info_get_offset(self.info) } + } + + /// Get GPIO line's name. + pub fn name(&self) -> Result<&str> { + // SAFETY: The string returned by libgpiod is guaranteed to live as long + // as the `struct Info`. + let name = unsafe { gpiod::gpiod_line_info_get_name(self.info) }; + if name.is_null() { + return Err(Error::NullString("GPIO line's name")); + } + + // SAFETY: The string is guaranteed to be valid here by the C API. + str::from_utf8(unsafe { slice::from_raw_parts(name as *const u8, strlen(name) as usize) }) + .map_err(Error::StringNotUtf8) + } + + /// Returns True if the line is in use, false otherwise. + /// + /// The user space can't know exactly why a line is busy. It may have been + /// requested by another process or hogged by the kernel. It only matters that + /// the line is used and we can't request it. + pub fn is_used(&self) -> bool { + unsafe { gpiod::gpiod_line_info_is_used(self.info) } + } + + /// Get the GPIO line's consumer name. + pub fn consumer(&self) -> Result<&str> { + // SAFETY: The string returned by libgpiod is guaranteed to live as long + // as the `struct Info`. + let name = unsafe { gpiod::gpiod_line_info_get_consumer(self.info) }; + if name.is_null() { + return Err(Error::NullString("GPIO line's consumer name")); + } + + // SAFETY: The string is guaranteed to be valid here by the C API. + str::from_utf8(unsafe { slice::from_raw_parts(name as *const u8, strlen(name) as usize) }) + .map_err(Error::StringNotUtf8) + } + + /// Get the GPIO line's direction. + pub fn direction(&self) -> Result { + Direction::new(unsafe { gpiod::gpiod_line_info_get_direction(self.info) } as u32) + } + + /// Returns true if the line is "active-low", false otherwise. + pub fn is_active_low(&self) -> bool { + unsafe { gpiod::gpiod_line_info_is_active_low(self.info) } + } + + /// Get the GPIO line's bias setting. + pub fn bias(&self) -> Result> { + Bias::new(unsafe { gpiod::gpiod_line_info_get_bias(self.info) } as u32) + } + + /// Get the GPIO line's drive setting. + pub fn drive(&self) -> Result { + Drive::new(unsafe { gpiod::gpiod_line_info_get_drive(self.info) } as u32) + } + + /// Get the current edge detection setting of the line. + pub fn edge_detection(&self) -> Result> { + Edge::new(unsafe { gpiod::gpiod_line_info_get_edge_detection(self.info) } as u32) + } + + /// Get the current event clock setting used for edge event timestamps. + pub fn event_clock(&self) -> Result { + EventClock::new(unsafe { gpiod::gpiod_line_info_get_event_clock(self.info) } as u32) + } + + /// Returns true if the line is debounced (either by hardware or by the + /// kernel software debouncer), false otherwise. + pub fn is_debounced(&self) -> bool { + unsafe { gpiod::gpiod_line_info_is_debounced(self.info) } + } + + /// Get the debounce period of the line. + pub fn debounce_period(&self) -> Duration { + Duration::from_micros(unsafe { gpiod::gpiod_line_info_get_debounce_period_us(self.info) }) + } +} + +impl Drop for Info { + fn drop(&mut self) { + // We must not free the Line info object created from `struct info::Event` by calling + // libgpiod API. + if !self.info_event { + unsafe { gpiod::gpiod_line_info_free(self.info) } + } + } +} diff --git a/bindings/rust/libgpiod/src/line_request.rs b/bindings/rust/libgpiod/src/line_request.rs new file mode 100644 index 000000000000..e4ff951aef29 --- /dev/null +++ b/bindings/rust/libgpiod/src/line_request.rs @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use libc::EINVAL; +use std::os::raw::c_ulong; +use std::sync::Arc; +use std::time::Duration; + +use vmm_sys_util::errno::Error as Errno; + +use super::{ + chip, edge, gpiod, line, request, Error, Offset, OperationType, Result, Value, ValueMap, +}; + +/// Line request operations +/// +/// Allows interaction with a set of requested lines. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Request { + request: *mut gpiod::gpiod_line_request, +} + +impl Request { + /// Request a set of lines for exclusive usage. + pub(crate) fn new( + ichip: &Arc, + rconfig: &request::Config, + lconfig: &line::Config, + ) -> Result { + let request = unsafe { + gpiod::gpiod_chip_request_lines(ichip.chip(), rconfig.config(), lconfig.config()) + }; + + if request.is_null() { + return Err(Error::OperationFailed( + OperationType::LineRequest, + Errno::last(), + )); + } + + Ok(Self { request }) + } + + /// Get the number of lines in the request. + pub fn num_lines(&self) -> usize { + unsafe { gpiod::gpiod_line_request_get_num_lines(self.request) as usize } + } + + /// Get the offsets of lines in the request. + pub fn offsets(&self) -> Vec { + let mut offsets = vec![0; self.num_lines() as usize]; + + unsafe { gpiod::gpiod_line_request_get_offsets(self.request, offsets.as_mut_ptr()) }; + offsets + } + + /// Get the value (0 or 1) of a single line associated with the request. + pub fn value(&self, offset: Offset) -> Result { + let value = unsafe { gpiod::gpiod_line_request_get_value(self.request, offset) }; + + if value != 0 && value != 1 { + Err(Error::OperationFailed( + OperationType::LineRequestGetVal, + Errno::last(), + )) + } else { + Value::new(value) + } + } + + /// Get values of a subset of lines associated with the request. + pub fn values_subset(&self, offsets: &[Offset]) -> Result { + let mut values = vec![0; offsets.len()]; + + let ret = unsafe { + gpiod::gpiod_line_request_get_values_subset( + self.request, + offsets.len() as c_ulong, + offsets.as_ptr(), + values.as_mut_ptr(), + ) + }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::LineRequestGetValSubset, + Errno::last(), + )) + } else { + let mut map = ValueMap::new(); + + for (i, val) in values.iter().enumerate() { + map.insert(offsets[i].into(), Value::new(*val)?); + } + + Ok(map) + } + } + + /// Get values of all lines associated with the request. + pub fn values(&self) -> Result { + self.values_subset(&self.offsets()) + } + + /// Set the value of a single line associated with the request. + pub fn set_value(&self, offset: Offset, value: Value) -> Result<()> { + let ret = + unsafe { gpiod::gpiod_line_request_set_value(self.request, offset, value.value()) }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::LineRequestSetVal, + Errno::last(), + )) + } else { + Ok(()) + } + } + + /// Get values of a subset of lines associated with the request. + pub fn set_values_subset(&self, map: ValueMap) -> Result<()> { + let mut offsets = Vec::new(); + let mut values = Vec::new(); + + for (offset, value) in map { + offsets.push(offset as u32); + values.push(value.value()); + } + + let ret = unsafe { + gpiod::gpiod_line_request_set_values_subset( + self.request, + offsets.len() as c_ulong, + offsets.as_ptr(), + values.as_ptr(), + ) + }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::LineRequestSetValSubset, + Errno::last(), + )) + } else { + Ok(()) + } + } + + /// Get values of all lines associated with the request. + pub fn set_values(&self, values: &[Value]) -> Result<()> { + if values.len() != self.num_lines() as usize { + return Err(Error::OperationFailed( + OperationType::LineRequestSetVal, + Errno::new(EINVAL), + )); + } + + let mut new_values = Vec::new(); + for value in values { + new_values.push(value.value()); + } + + let ret = + unsafe { gpiod::gpiod_line_request_set_values(self.request, new_values.as_ptr()) }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::LineRequestSetVal, + Errno::last(), + )) + } else { + Ok(()) + } + } + + /// Update the configuration of lines associated with the line request. + pub fn reconfigure_lines(&self, lconfig: &line::Config) -> Result<()> { + let ret = + unsafe { gpiod::gpiod_line_request_reconfigure_lines(self.request, lconfig.config()) }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::LineRequestReconfigLines, + Errno::last(), + )) + } else { + Ok(()) + } + } + + /// Get the file descriptor associated with the line request. + pub fn fd(&self) -> u32 { + unsafe { gpiod::gpiod_line_request_get_fd(self.request) as u32 } + } + + /// Wait for edge events on any of the lines associated with the request. + pub fn wait_edge_event(&self, timeout: Option) -> Result { + let timeout = match timeout { + Some(x) => x.as_nanos() as i64, + // Block indefinitely + None => -1, + }; + + let ret = unsafe { gpiod::gpiod_line_request_wait_edge_event(self.request, timeout) }; + + match ret { + -1 => Err(Error::OperationFailed( + OperationType::LineRequestWaitEdgeEvent, + Errno::last(), + )), + 0 => Ok(false), + _ => Ok(true), + } + } + + /// Get a number of edge events from a line request. + /// + /// This function will block if no event was queued for the line. + pub fn read_edge_events(&self, buffer: &mut edge::event::Buffer) -> Result { + let ret = unsafe { + gpiod::gpiod_line_request_read_edge_event( + self.request, + buffer.buffer(), + buffer.capacity() as u64, + ) + }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::LineRequestReadEdgeEvent, + Errno::last(), + )) + } else { + Ok(ret as u32) + } + } +} + +impl Drop for Request { + /// Release the requested lines and free all associated resources. + fn drop(&mut self) { + unsafe { gpiod::gpiod_line_request_release(self.request) } + } +} diff --git a/bindings/rust/libgpiod/src/line_settings.rs b/bindings/rust/libgpiod/src/line_settings.rs new file mode 100644 index 000000000000..2c3090132ea5 --- /dev/null +++ b/bindings/rust/libgpiod/src/line_settings.rs @@ -0,0 +1,277 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use std::time::Duration; + +use vmm_sys_util::errno::Error as Errno; + +use super::{ + gpiod, Bias, Direction, Drive, Edge, Error, EventClock, OperationType, Result, SettingKind, + SettingVal, Value, +}; + +/// Line settings objects. +/// +/// Line settings object contains a set of line properties that can be used +/// when requesting lines or reconfiguring an existing request. +/// +/// Mutators in general can only fail if the new property value is invalid. The +/// return values can be safely ignored - the object remains valid even after +/// a mutator fails and simply uses the sane default appropriate for given +/// property. + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Settings { + settings: *mut gpiod::gpiod_line_settings, +} + +impl Settings { + /// Create a new line settings object. + pub fn new() -> Result { + let settings = unsafe { gpiod::gpiod_line_settings_new() }; + + if settings.is_null() { + return Err(Error::OperationFailed( + OperationType::LineSettingsNew, + Errno::last(), + )); + } + + Ok(Self { settings }) + } + + pub fn new_with_settings(settings: *mut gpiod::gpiod_line_settings) -> Self { + Self { settings } + } + + /// Private helper, Returns gpiod_line_settings + pub(crate) fn settings(&self) -> *mut gpiod::gpiod_line_settings { + self.settings + } + + /// Resets the line settings object to its default values. + pub fn reset(&mut self) { + unsafe { gpiod::gpiod_line_settings_reset(self.settings) } + } + + /// Makes copy of the settings object. + pub fn settings_clone(&self) -> Result { + let settings = unsafe { gpiod::gpiod_line_settings_copy(self.settings) }; + if settings.is_null() { + return Err(Error::OperationFailed( + OperationType::LineSettingsCopy, + Errno::last(), + )); + } + + Ok(Self { settings }) + } + + /// Set line prop setting. + pub fn set_prop(&mut self, values: &[SettingVal]) -> Result<()> { + for value in values { + match value { + SettingVal::Direction(val) => self.set_direction(*val)?, + SettingVal::EdgeDetection(val) => self.set_edge_detection(*val)?, + SettingVal::Bias(val) => self.set_bias(*val)?, + SettingVal::Drive(val) => self.set_drive(*val)?, + SettingVal::ActiveLow(val) => self.set_active_low(*val), + SettingVal::DebouncePeriod(val) => self.set_debounce_period(*val), + SettingVal::EventClock(val) => self.set_event_clock(*val)?, + SettingVal::OutputValue(val) => self.set_output_value(*val)?, + } + } + + Ok(()) + } + + /// Get the line prop setting. + pub fn prop(&self, property: SettingKind) -> Result { + Ok(match property { + SettingKind::Direction => SettingVal::Direction(self.direction()?), + SettingKind::EdgeDetection => SettingVal::EdgeDetection(self.edge_detection()?), + SettingKind::Bias => SettingVal::Bias(self.bias()?), + SettingKind::Drive => SettingVal::Drive(self.drive()?), + SettingKind::ActiveLow => SettingVal::ActiveLow(self.active_low()), + SettingKind::DebouncePeriod => SettingVal::DebouncePeriod(self.debounce_period()?), + SettingKind::EventClock => SettingVal::EventClock(self.event_clock()?), + SettingKind::OutputValue => SettingVal::OutputValue(self.output_value()?), + }) + } + + /// Set the line direction. + fn set_direction(&mut self, direction: Direction) -> Result<()> { + let ret = unsafe { + gpiod::gpiod_line_settings_set_direction( + self.settings, + direction.gpiod_direction() as i32, + ) + }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::LineSettingsSetDirection, + Errno::last(), + )) + } else { + Ok(()) + } + } + + /// Get the direction setting. + fn direction(&self) -> Result { + Direction::new(unsafe { gpiod::gpiod_line_settings_get_direction(self.settings) } as u32) + } + + /// Set the edge event detection setting. + fn set_edge_detection(&mut self, edge: Option) -> Result<()> { + let ret = unsafe { + gpiod::gpiod_line_settings_set_edge_detection( + self.settings, + Edge::gpiod_edge(edge) as i32, + ) + }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::LineSettingsSetEdgeDetection, + Errno::last(), + )) + } else { + Ok(()) + } + } + + /// Get the edge event detection setting. + fn edge_detection(&self) -> Result> { + Edge::new(unsafe { gpiod::gpiod_line_settings_get_edge_detection(self.settings) } as u32) + } + + /// Set the bias setting. + fn set_bias(&mut self, bias: Option) -> Result<()> { + let ret = unsafe { + gpiod::gpiod_line_settings_set_bias(self.settings, Bias::gpiod_bias(bias) as i32) + }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::LineSettingsSetBias, + Errno::last(), + )) + } else { + Ok(()) + } + } + + /// Get the bias setting. + fn bias(&self) -> Result> { + Bias::new(unsafe { gpiod::gpiod_line_settings_get_bias(self.settings) } as u32) + } + + /// Set the drive setting. + fn set_drive(&mut self, drive: Drive) -> Result<()> { + let ret = unsafe { + gpiod::gpiod_line_settings_set_drive(self.settings, drive.gpiod_drive() as i32) + }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::LineSettingsSetDrive, + Errno::last(), + )) + } else { + Ok(()) + } + } + + /// Get the drive setting. + fn drive(&self) -> Result { + Drive::new(unsafe { gpiod::gpiod_line_settings_get_drive(self.settings) } as u32) + } + + /// Set active-low setting. + fn set_active_low(&mut self, active_low: bool) { + unsafe { gpiod::gpiod_line_settings_set_active_low(self.settings, active_low) } + } + + /// Check the active-low setting. + fn active_low(&self) -> bool { + unsafe { gpiod::gpiod_line_settings_get_active_low(self.settings) } + } + + /// Set the debounce period setting. + fn set_debounce_period(&mut self, period: Duration) { + unsafe { + gpiod::gpiod_line_settings_set_debounce_period_us( + self.settings, + period.as_micros() as u64, + ) + } + } + + /// Get the debounce period. + fn debounce_period(&self) -> Result { + Ok(Duration::from_micros(unsafe { + gpiod::gpiod_line_settings_get_debounce_period_us(self.settings) + })) + } + + /// Set the event clock setting. + fn set_event_clock(&mut self, clock: EventClock) -> Result<()> { + let ret = unsafe { + gpiod::gpiod_line_settings_set_event_clock(self.settings, clock.gpiod_clock() as i32) + }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::LineSettingsSetEventClock, + Errno::last(), + )) + } else { + Ok(()) + } + } + + /// Get the event clock setting. + fn event_clock(&self) -> Result { + EventClock::new(unsafe { gpiod::gpiod_line_settings_get_event_clock(self.settings) } as u32) + } + + /// Set the output value setting. + fn set_output_value(&mut self, value: Value) -> Result<()> { + let ret = + unsafe { gpiod::gpiod_line_settings_set_output_value(self.settings, value.value()) }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::LineSettingsSetOutputValue, + Errno::last(), + )) + } else { + Ok(()) + } + } + + /// Get the output value, 0 or 1. + fn output_value(&self) -> Result { + let value = unsafe { gpiod::gpiod_line_settings_get_output_value(self.settings) }; + + if value != 0 && value != 1 { + Err(Error::OperationFailed( + OperationType::LineSettingsGetOutVal, + Errno::last(), + )) + } else { + Value::new(value) + } + } +} + +impl Drop for Settings { + /// Free the line settings object and release all associated resources. + fn drop(&mut self) { + unsafe { gpiod::gpiod_line_settings_free(self.settings) } + } +} diff --git a/bindings/rust/libgpiod/src/request_config.rs b/bindings/rust/libgpiod/src/request_config.rs new file mode 100644 index 000000000000..760d9c755c86 --- /dev/null +++ b/bindings/rust/libgpiod/src/request_config.rs @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use libc::strlen; +use std::os::raw::{c_char, c_ulong}; +use std::{slice, str}; + +use vmm_sys_util::errno::Error as Errno; + +use super::{gpiod, Error, OperationType, Result}; + +/// Request configuration objects +/// +/// Request config objects are used to pass a set of options to the kernel at +/// the time of the line request. The mutators don't return error values. If the +/// values are invalid, in general they are silently adjusted to acceptable +/// ranges. + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Config { + config: *mut gpiod::gpiod_request_config, +} + +impl Config { + /// Create a new request config object. + pub fn new() -> Result { + let config = unsafe { gpiod::gpiod_request_config_new() }; + if config.is_null() { + return Err(Error::OperationFailed( + OperationType::RequestConfigNew, + Errno::last(), + )); + } + + Ok(Self { config }) + } + + /// Private helper, Returns gpiod_request_config + pub(crate) fn config(&self) -> *mut gpiod::gpiod_request_config { + self.config + } + + /// Set the consumer name for the request. + /// + /// If the consumer string is too long, it will be truncated to the max + /// accepted length. + pub fn set_consumer(&self, consumer: &str) { + // Null-terminate the string + let consumer = consumer.to_owned() + "\0"; + + unsafe { + gpiod::gpiod_request_config_set_consumer( + self.config, + consumer.as_ptr() as *const c_char, + ) + } + } + + /// Get the consumer name configured in the request config. + pub fn consumer(&self) -> Result<&str> { + // SAFETY: The string returned by libgpiod is guaranteed to live as long + // as the `struct Config`. + let consumer = unsafe { gpiod::gpiod_request_config_get_consumer(self.config) }; + if consumer.is_null() { + return Err(Error::OperationFailed( + OperationType::RequestConfigGetConsumer, + Errno::last(), + )); + } + + // SAFETY: The string is guaranteed to be valid here by the C API. + str::from_utf8(unsafe { + slice::from_raw_parts(consumer as *const u8, strlen(consumer) as usize) + }) + .map_err(Error::StringNotUtf8) + } + + /// Set the size of the kernel event buffer for the request. + pub fn set_event_buffer_size(&self, size: usize) { + unsafe { gpiod::gpiod_request_config_set_event_buffer_size(self.config, size as c_ulong) } + } + + /// Get the edge event buffer size setting for the request config. + pub fn event_buffer_size(&self) -> usize { + unsafe { gpiod::gpiod_request_config_get_event_buffer_size(self.config) as usize } + } +} + +impl Drop for Config { + /// Free the request config object and release all associated resources. + fn drop(&mut self) { + unsafe { gpiod::gpiod_request_config_free(self.config) } + } +} From patchwork Fri Oct 14 10:47:21 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 615332 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 A6053C433FE for ; Fri, 14 Oct 2022 10:47:48 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229586AbiJNKrr (ORCPT ); Fri, 14 Oct 2022 06:47:47 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47510 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229661AbiJNKrq (ORCPT ); Fri, 14 Oct 2022 06:47:46 -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 516D315F914 for ; Fri, 14 Oct 2022 03:47:43 -0700 (PDT) Received: by mail-pj1-x1031.google.com with SMTP id t12-20020a17090a3b4c00b0020b04251529so4424643pjf.5 for ; Fri, 14 Oct 2022 03:47:43 -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=p2seQsCe0Mv1nJHv/wuyNrWbZKGot6C6wp+PveofM58=; b=nNMxTxQ/faabZ5tt7j5d2oX42yb/Ex8Qcw5CskGd/Qb3ffSpSI5RxFggIByhRrGKU2 PW3GkkcKG7IEmFQNLaxL5RMWeRb3A2bnjltTH9PRwNwkD4N1wQhLD6c6HaRApDtwgtsn oi2v3im8J5xY39SRfunn+5pnOfeYmgEafYe/ywSeyL1zAA8TMsix50eKLY/tmTMqnHgC yGbkNZ3bcUzuala1wMVcXM1QFtzsjALqTj00Ir8d0O8sldC4TibcAG3Rp16uvUgl4OgN Ce5oOLLUVfLxD8lu2L+QupgZQQcnJ5Rx45JvZce8M+BoQ+c88jZApGpLo8ZTHZT0BSEi U6GQ== 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=p2seQsCe0Mv1nJHv/wuyNrWbZKGot6C6wp+PveofM58=; b=Yn8jdxmKHxeL54w/5BPMhOyFO9FVaTbBZ7foDkZySnQwnHKuNGJqk7nnWU+WSWMg1f HE4Z9uD63lfTENYX03LkggTJEKZIL4PoxNW1SyPyAYo6WrbO/wJWzOlx8XsysoW+FFDY 9dpUdtknBZNZ2/UR19eksha79kwXN7KPeM7aHFrvklzUvtHH0iznvfMvNAlZZ2zAMhhJ yP5hb0IepYCKikzySJlceXOGAcLDEkNeOp5DwGX3YrrTzJCV39kie7b0vuNaLvbFskMQ 2sdqoU88RY9bNQYlO8Njp9cyXgp+YPYR/+1cw4HYbpWEuKSt0oX1+3MXArAhowy6FHEs aRIQ== X-Gm-Message-State: ACrzQf1lTnmkW45M6AE99ADPtmfIQ9Ji+I3tc6fF54HpknrqTTSqYv1t rxDx4Yt9oygCupGkKh1P3yoBZg== X-Google-Smtp-Source: AMsMyM5/2uwPNabArFZ1+f1Aa/frszW/qBryFHM7m225efbyMGwYvFX8DyShi+ctHuRt3UHm0HS0rA== X-Received: by 2002:a17:902:9a49:b0:17a:6662:9334 with SMTP id x9-20020a1709029a4900b0017a66629334mr4355525plv.63.1665744462684; Fri, 14 Oct 2022 03:47:42 -0700 (PDT) Received: from localhost ([122.172.86.128]) by smtp.gmail.com with ESMTPSA id u6-20020a63ef06000000b00464aa9ea6fasm1184101pgh.20.2022.10.14.03.47.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 14 Oct 2022 03: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 Subject: [PATCH V7 4/8] libgpiod: Add rust examples Date: Fri, 14 Oct 2022 16:17:21 +0530 Message-Id: <2d5fe872fcbf3e817cf09944494252a0e1bc8979.1665744170.git.viresh.kumar@linaro.org> X-Mailer: git-send-email 2.31.1.272.g89b43f80a514 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org Add examples for the usage of the rust bindings, quite similar to the ones in cxx bindings. Signed-off-by: Viresh Kumar --- bindings/rust/libgpiod/examples/gpiodetect.rs | 30 ++++++ bindings/rust/libgpiod/examples/gpiofind.rs | 35 +++++++ bindings/rust/libgpiod/examples/gpioget.rs | 42 ++++++++ bindings/rust/libgpiod/examples/gpioinfo.rs | 95 +++++++++++++++++++ bindings/rust/libgpiod/examples/gpiomon.rs | 73 ++++++++++++++ bindings/rust/libgpiod/examples/gpioset.rs | 60 ++++++++++++ bindings/rust/libgpiod/examples/gpiowatch.rs | 54 +++++++++++ 7 files changed, 389 insertions(+) create mode 100644 bindings/rust/libgpiod/examples/gpiodetect.rs create mode 100644 bindings/rust/libgpiod/examples/gpiofind.rs create mode 100644 bindings/rust/libgpiod/examples/gpioget.rs create mode 100644 bindings/rust/libgpiod/examples/gpioinfo.rs create mode 100644 bindings/rust/libgpiod/examples/gpiomon.rs create mode 100644 bindings/rust/libgpiod/examples/gpioset.rs create mode 100644 bindings/rust/libgpiod/examples/gpiowatch.rs diff --git a/bindings/rust/libgpiod/examples/gpiodetect.rs b/bindings/rust/libgpiod/examples/gpiodetect.rs new file mode 100644 index 000000000000..f24ac72e2d48 --- /dev/null +++ b/bindings/rust/libgpiod/examples/gpiodetect.rs @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar +// +// Simplified Rust implementation of gpiodetect tool. + +use std::env; +use std::path::Path; + +use libgpiod::{gpiochip_devices, Error, Result}; + +fn main() -> Result<()> { + let args: Vec = env::args().collect(); + if args.len() > 1 { + println!("Usage: {}", args[0]); + return Err(Error::InvalidArguments); + } + + for chip in gpiochip_devices(&Path::new("/dev"))? { + println!( + "{} [{}] ({})", + chip.name()?, + chip.label()?, + chip.num_lines(), + ); + } + + Ok(()) +} diff --git a/bindings/rust/libgpiod/examples/gpiofind.rs b/bindings/rust/libgpiod/examples/gpiofind.rs new file mode 100644 index 000000000000..07e886bc3896 --- /dev/null +++ b/bindings/rust/libgpiod/examples/gpiofind.rs @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar +// +// Simplified Rust implementation of gpiofind tool. + +use std::env; +use std::path::Path; + +use libgpiod::{gpiochip_devices, Error, Result}; + +fn main() -> Result<()> { + let args: Vec = env::args().collect(); + if args.len() != 2 { + println!("Usage: {} ", args[0]); + return Err(Error::InvalidArguments); + } + + for chip in gpiochip_devices(&Path::new("/dev"))? { + let offset = chip.line_offset_from_name(&args[1]); + if offset.is_ok() { + println!( + "Line {} found: Chip: {}, offset: {}", + args[1], + chip.name()?, + offset? + ); + return Ok(()); + } + } + + println!("Failed to find line: {}", args[1]); + Ok(()) +} diff --git a/bindings/rust/libgpiod/examples/gpioget.rs b/bindings/rust/libgpiod/examples/gpioget.rs new file mode 100644 index 000000000000..f4c111987d96 --- /dev/null +++ b/bindings/rust/libgpiod/examples/gpioget.rs @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar +// +// Simplified Rust implementation of gpioget tool. + +use std::env; + +use libgpiod::{chip::Chip, line, request, Direction, Error, Offset, Result, SettingVal}; + +fn main() -> Result<()> { + let args: Vec = 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_prop(&[SettingVal::Direction(Direction::Input)])?; + lconfig.add_line_settings(&offsets, lsettings)?; + + let path = format!("/dev/gpiochip{}", args[1]); + let chip = Chip::open(&path)?; + + let rconfig = request::Config::new()?; + rconfig.set_consumer(&args[0]); + + let request = chip.request_lines(&rconfig, &lconfig)?; + let map = request.values()?; + + println!("{:?}", map); + Ok(()) +} diff --git a/bindings/rust/libgpiod/examples/gpioinfo.rs b/bindings/rust/libgpiod/examples/gpioinfo.rs new file mode 100644 index 000000000000..8b9fcb000f63 --- /dev/null +++ b/bindings/rust/libgpiod/examples/gpioinfo.rs @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar +// +// Simplified Rust implementation of gpioinfo tool. + +use std::env; +use std::path::Path; + +use libgpiod::{ + chip::Chip, gpiochip_devices, is_gpiochip_device, Direction, Error, Offset, Result, +}; + +fn line_info(chip: &Chip, offset: Offset) -> Result<()> { + let info = chip.line_info(offset)?; + let off = info.offset(); + + let name = match info.name() { + Ok(name) => name, + _ => "unused", + }; + + let consumer = match info.consumer() { + Ok(name) => name, + _ => "unnamed", + }; + + let low = if info.is_active_low() { + "active-low" + } else { + "active-high" + }; + + let dir = match info.direction()? { + Direction::AsIs => "None", + Direction::Input => "Input", + Direction::Output => "Output", + }; + + println!( + "\tline {:>3}\ + \t{:>10}\ + \t{:>10}\ + \t{:>6}\ + \t{:>14}", + off, name, consumer, dir, low + ); + + Ok(()) +} + +fn chip_info(chip: &Chip) -> Result<()> { + let ngpio = chip.num_lines(); + + println!("GPIO Chip name: {}", chip.name()?); + println!("\tlabel: {}", chip.label()?); + println!("\tpath: {}", chip.path()?); + println!("\tngpio: {}\n", ngpio); + + println!("\tLine information:"); + + for offset in 0..ngpio { + line_info(chip, offset as Offset)?; + } + println!("\n"); + + Ok(()) +} + +fn main() -> Result<()> { + let args: Vec = env::args().collect(); + if args.len() > 2 { + println!("Usage: {}", args[0]); + return Err(Error::InvalidArguments); + } + + if args.len() == 1 { + for chip in gpiochip_devices(&Path::new("/dev"))? { + chip_info(&chip)?; + } + } else { + let index = args[1] + .parse::() + .map_err(|_| Error::InvalidArguments)?; + let path = format!("/dev/gpiochip{}", index); + if is_gpiochip_device(&path) { + let chip = Chip::open(&path)?; + + chip_info(&chip)?; + } + } + + Ok(()) +} diff --git a/bindings/rust/libgpiod/examples/gpiomon.rs b/bindings/rust/libgpiod/examples/gpiomon.rs new file mode 100644 index 000000000000..2f3af2d2daa0 --- /dev/null +++ b/bindings/rust/libgpiod/examples/gpiomon.rs @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar +// +// Simplified Rust implementation of the gpiomon tool. + +use std::env; + +use libgpiod::{ + chip::Chip, edge, line, request, Edge, EdgeKind, Error, Offset, Result, SettingVal, +}; + +fn usage(name: &str) { + println!("Usage: {} ...", 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_prop(&[SettingVal::EdgeDetection(Some(Edge::Both))])?; + lconfig.add_line_settings(&offsets, lsettings)?; + + let path = format!("/dev/gpiochip{}", args[1]); + let chip = Chip::open(&path)?; + + let rconfig = request::Config::new()?; + + let mut buffer = edge::event::Buffer::new(1)?; + let request = chip.request_lines(&rconfig, &lconfig)?; + + loop { + match request.wait_edge_event(None) { + Err(x) => { + println!("{:?}", x); + return Err(Error::InvalidArguments); + } + + Ok(false) => { + // This shouldn't happen as the call is blocking. + panic!(); + } + Ok(true) => (), + } + + let count = request.read_edge_events(&mut buffer)?; + if count == 1 { + let event = buffer.event(0)?; + println!( + "line: {} type: {}, time: {:?}", + event.line_offset(), + match event.event_type()? { + EdgeKind::Rising => "Rising", + EdgeKind::Falling => "Falling", + }, + event.timestamp() + ); + } + } +} diff --git a/bindings/rust/libgpiod/examples/gpioset.rs b/bindings/rust/libgpiod/examples/gpioset.rs new file mode 100644 index 000000000000..56ed241ce9de --- /dev/null +++ b/bindings/rust/libgpiod/examples/gpioset.rs @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar +// +// Simplified Rust implementation of the gpioset tool. + +use std::env; +use std::io::{stdin, Read}; + +use libgpiod::{chip::Chip, line, request, Direction, Error, Offset, Result, SettingVal, Value}; + +fn usage(name: &str) { + println!("Usage: {} = ...", 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..030168ba9788 --- /dev/null +++ b/bindings/rust/libgpiod/examples/gpiowatch.rs @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar +// +// Simplified Rust implementation of the gpiowatch tool. + +use std::env; + +use libgpiod::{chip::Chip, Error, Offset, Result}; + +fn usage(name: &str) { + println!("Usage: {} ...", name); +} + +fn main() -> Result<()> { + let args: Vec = env::args().collect(); + if args.len() < 2 { + usage(&args[0]); + return Err(Error::InvalidArguments); + } + + let path = format!("/dev/gpiochip{}", args[1]); + let offset = args[2] + .parse::() + .map_err(|_| Error::InvalidArguments)?; + + let chip = Chip::open(&path)?; + let _info = chip.watch_line_info(offset)?; + + match chip.wait_info_event(None) { + Err(x) => { + println!("{:?}", x); + return Err(Error::InvalidArguments); + } + + Ok(false) => { + // This shouldn't happen as the call is blocking. + panic!(); + } + Ok(true) => (), + } + + let event = chip.read_info_event()?; + println!( + "line: {} type: {:?}, time: {:?}", + offset, + event.event_type()?, + event.timestamp() + ); + + chip.unwatch(offset); + Ok(()) +} From patchwork Fri Oct 14 10:47:22 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 615331 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 49D61C43219 for ; Fri, 14 Oct 2022 10:47:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229657AbiJNKrs (ORCPT ); Fri, 14 Oct 2022 06:47:48 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47528 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229666AbiJNKrr (ORCPT ); Fri, 14 Oct 2022 06:47:47 -0400 Received: from mail-pl1-x634.google.com (mail-pl1-x634.google.com [IPv6:2607:f8b0:4864:20::634]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 29C76166981 for ; Fri, 14 Oct 2022 03:47:46 -0700 (PDT) Received: by mail-pl1-x634.google.com with SMTP id l4so4370598plb.8 for ; Fri, 14 Oct 2022 03:47:46 -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=Cj3RZKURKt8Xu5v1/cctRzFsqTtNcw1t7dkd6bYhNrU=; b=E3wmD9+KbLAC1DTdsJEc2zZ6X0yW97o8+Hz4DlLrb68LnPUbUwOlVdbhg7aY1bfRJh qNKnwWgayR2kqtzUb+t1KoyHWFsfurTFVxk8w5NflOTv60nY/+Mk9IYBLCHVE8wUQlCL Op7j1pFxLWft/6chvwjAeBXVaDnKfzev1IyImeT9kvOJ23YAnsS7vQYUdp8KFKzBdEfJ jNXzVPfMMVQQ6Km/bKkcbW01MnOW/uNYZ+OraKfziBVrIEzsowI1XalvWM1l+us4Q1FP 3hWFPsLMA+kJ02tVRyI6CxRoqukbIY0/WMhsYLhAUtc1yDXsEYORNfJrMQZb73mDV2lT MbhQ== 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=Cj3RZKURKt8Xu5v1/cctRzFsqTtNcw1t7dkd6bYhNrU=; b=xLPn6LHbsRxlaIwlAjZcN006+33JkJDjx0YeE/W9pyL8nGaaHRt51MtsIG/XxzKhNV QnNFmvg+/m9cD+NM1QjXc/BxB/sA0t2OHH/+MtfrEYmyqzZseIsfYndH/Ylnc8xQS3yD ynnU3SKVtMV3EeB0w8b3UqBRl+tUzwdn37u+wOKizb1EtU9f95HuLao9rOnbxpC0c9Pt hmee7ww3fkQ7/Ut4x2dY9X9z35DAcmX2MsYiJI/i8stWRxBQlKFzio+7O6HIrib/PLvC HHLtENF5zUY7Y5eiUZUsTBQfpMPWpcasgcWM00KaTfdvLURsjd1gwz/YoPSNLjRNiQtf ShFQ== X-Gm-Message-State: ACrzQf1bhtHyIOj5OD0IVE6gptF4c+cnRKR82iFb81szqZBLXWPedDGN 7J7nSQ/A/yDrSJBSXLhM4T7Xtw== X-Google-Smtp-Source: AMsMyM7PHZXO/0W6MqoyslyXgeRjXOdjQWdeJJByzaJENhx+vkTbEjF9ZSLA6gEd1ZNouqgBouyumw== X-Received: by 2002:a17:90a:5308:b0:20b:1eae:c94e with SMTP id x8-20020a17090a530800b0020b1eaec94emr16629284pjh.88.1665744465745; Fri, 14 Oct 2022 03:47:45 -0700 (PDT) Received: from localhost ([122.172.86.128]) by smtp.gmail.com with ESMTPSA id e15-20020a170902784f00b00178b9c997e5sm1428785pln.138.2022.10.14.03.47.44 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 14 Oct 2022 03:47:45 -0700 (PDT) From: Viresh Kumar To: Linus Walleij , Bartosz Golaszewski Cc: Viresh Kumar , Vincent Guittot , linux-gpio@vger.kernel.org, Kent Gibson , Miguel Ojeda , Wedson Almeida Filho , =?utf-8?q?Alex_Benn=C3=A9e?= , stratos-dev@op-lists.linaro.org, Gerard Ryan Subject: [PATCH V7 5/8] libgpiod: Add gpiosim rust crate Date: Fri, 14 Oct 2022 16:17:22 +0530 Message-Id: <4484ba30efe5dbf09e9a200ec17185479282d1f2.1665744170.git.viresh.kumar@linaro.org> X-Mailer: git-send-email 2.31.1.272.g89b43f80a514 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org This adds gpiosim rust crate, which provides helpers to emulate GPIO chips. Signed-off-by: Viresh Kumar --- bindings/rust/Cargo.toml | 3 +- bindings/rust/gpiosim/Cargo.toml | 18 ++ bindings/rust/gpiosim/build.rs | 43 ++++ bindings/rust/gpiosim/src/lib.rs | 25 +++ bindings/rust/gpiosim/src/sim.rs | 323 +++++++++++++++++++++++++++++++ 5 files changed, 411 insertions(+), 1 deletion(-) create mode 100644 bindings/rust/gpiosim/Cargo.toml create mode 100644 bindings/rust/gpiosim/build.rs create mode 100644 bindings/rust/gpiosim/src/lib.rs create mode 100644 bindings/rust/gpiosim/src/sim.rs diff --git a/bindings/rust/Cargo.toml b/bindings/rust/Cargo.toml index 1e57ef2c0002..8721bc610b86 100644 --- a/bindings/rust/Cargo.toml +++ b/bindings/rust/Cargo.toml @@ -2,5 +2,6 @@ members = [ "libgpiod-sys", - "libgpiod" + "libgpiod", + "gpiosim" ] diff --git a/bindings/rust/gpiosim/Cargo.toml b/bindings/rust/gpiosim/Cargo.toml new file mode 100644 index 000000000000..d6b4cc34339b --- /dev/null +++ b/bindings/rust/gpiosim/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "gpiosim" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +libc = ">=0.2.39" +libgpiod = { path = "../libgpiod" } +vmm-sys-util = "=0.10.0" + +[features] +generate = [ "bindgen" ] + +[build-dependencies] +bindgen = { version = "0.59.1", optional = true } +cc = "1.0.46" diff --git a/bindings/rust/gpiosim/build.rs b/bindings/rust/gpiosim/build.rs new file mode 100644 index 000000000000..460fb8c092c3 --- /dev/null +++ b/bindings/rust/gpiosim/build.rs @@ -0,0 +1,43 @@ +#[cfg(feature = "generate")] +extern crate bindgen; +#[cfg(feature = "generate")] +use std::env; +#[cfg(feature = "generate")] +use std::path::PathBuf; + +#[cfg(feature = "generate")] +fn generate_bindings() { + // Tell cargo to invalidate the built crate whenever following files change + println!("cargo:rerun-if-changed=../../../tests/gpiosim/gpiosim.h"); + + // The bindgen::Builder is the main entry point + // to bindgen, and lets you build up options for + // the resulting bindings. + let bindings = bindgen::Builder::default() + // The input header we would like to generate + // bindings for. + .header("../../../tests/gpiosim/gpiosim.h") + // Tell cargo to invalidate the built crate whenever any of the + // included header files changed. + .parse_callbacks(Box::new(bindgen::CargoCallbacks)) + // Finish the builder and generate the bindings. + .generate() + // Unwrap the Result and panic on failure. + .expect("Unable to generate bindings"); + + // Write the bindings to the $OUT_DIR/bindings.rs file. + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Couldn't write bindings!"); +} + +fn main() { + #[cfg(feature = "generate")] + generate_bindings(); + + println!("cargo:rustc-link-lib=kmod"); + println!("cargo:rustc-link-lib=mount"); + println!("cargo:rustc-link-search=./../../tests/gpiosim/.libs/"); + println!("cargo:rustc-link-lib=static=gpiosim"); +} diff --git a/bindings/rust/gpiosim/src/lib.rs b/bindings/rust/gpiosim/src/lib.rs new file mode 100644 index 000000000000..94d0ddb38e0f --- /dev/null +++ b/bindings/rust/gpiosim/src/lib.rs @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0 + +#[allow(non_camel_case_types, non_upper_case_globals)] +#[cfg_attr(test, allow(deref_nullptr, non_snake_case))] +#[allow(dead_code)] +mod bindings_raw { + #[cfg(feature = "generate")] + include!(concat!(env!("OUT_DIR"), "/bindings.rs")); + + #[cfg(not(feature = "generate"))] + include!("bindings.rs"); +} + +use bindings_raw::*; + +pub use bindings_raw::{ + GPIOSIM_HOG_DIR_INPUT, GPIOSIM_HOG_DIR_OUTPUT_HIGH, GPIOSIM_HOG_DIR_OUTPUT_LOW, + GPIOSIM_PULL_DOWN, GPIOSIM_PULL_UP, GPIOSIM_VALUE_ACTIVE, GPIOSIM_VALUE_INACTIVE, +}; + +#[allow(dead_code)] +mod sim; + +#[allow(unused_imports)] +pub use sim::*; diff --git a/bindings/rust/gpiosim/src/sim.rs b/bindings/rust/gpiosim/src/sim.rs new file mode 100644 index 000000000000..50977ea54ebb --- /dev/null +++ b/bindings/rust/gpiosim/src/sim.rs @@ -0,0 +1,323 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use libc::strlen; +use std::os::raw::c_char; +use std::path::PathBuf; +use std::{slice, str}; + +use vmm_sys_util::errno::Error as Errno; + +use libgpiod::{Error, Offset, OperationType, Result}; + +use crate::*; + +/// Sim Ctx +#[derive(Debug)] +struct SimCtx { + ctx: *mut gpiosim_ctx, +} + +unsafe impl Send for SimCtx {} +unsafe impl Sync for SimCtx {} + +impl SimCtx { + fn new() -> Result { + let ctx = unsafe { gpiosim_ctx_new() }; + if ctx.is_null() { + return Err(Error::OperationFailed( + OperationType::SimCtxNew, + Errno::last(), + )); + } + + Ok(Self { ctx }) + } + + fn ctx(&self) -> *mut gpiosim_ctx { + self.ctx + } +} + +impl Drop for SimCtx { + fn drop(&mut self) { + unsafe { gpiosim_ctx_unref(self.ctx) } + } +} + +/// Sim Dev +#[derive(Debug)] +struct SimDev { + dev: *mut gpiosim_dev, +} + +unsafe impl Send for SimDev {} +unsafe impl Sync for SimDev {} + +impl SimDev { + fn new(ctx: &SimCtx) -> Result { + let dev = unsafe { gpiosim_dev_new(ctx.ctx()) }; + if dev.is_null() { + return Err(Error::OperationFailed( + OperationType::SimDevNew, + Errno::last(), + )); + } + + Ok(Self { dev }) + } + + fn dev(&self) -> *mut gpiosim_dev { + self.dev + } + + fn enable(&self) -> Result<()> { + let ret = unsafe { gpiosim_dev_enable(self.dev) }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::SimDevEnable, + Errno::last(), + )) + } else { + Ok(()) + } + } + + fn disable(&self) -> Result<()> { + let ret = unsafe { gpiosim_dev_disable(self.dev) }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::SimDevDisable, + Errno::last(), + )) + } else { + Ok(()) + } + } +} + +impl Drop for SimDev { + fn drop(&mut self) { + unsafe { gpiosim_dev_unref(self.dev) } + } +} + +/// Sim Bank +#[derive(Debug)] +struct SimBank { + bank: *mut gpiosim_bank, +} + +unsafe impl Send for SimBank {} +unsafe impl Sync for SimBank {} + +impl SimBank { + fn new(dev: &SimDev) -> Result { + let bank = unsafe { gpiosim_bank_new(dev.dev()) }; + if bank.is_null() { + return Err(Error::OperationFailed( + OperationType::SimBankNew, + Errno::last(), + )); + } + + Ok(Self { bank }) + } + + fn chip_name(&self) -> Result<&str> { + // SAFETY: The string returned by gpiosim is guaranteed to live as long + // as the `struct SimBank`. + let name = unsafe { gpiosim_bank_get_chip_name(self.bank) }; + + // SAFETY: The string is guaranteed to be valid here. + str::from_utf8(unsafe { slice::from_raw_parts(name as *const u8, strlen(name) as usize) }) + .map_err(Error::StringNotUtf8) + } + + fn dev_path(&self) -> Result { + // SAFETY: The string returned by gpiosim is guaranteed to live as long + // as the `struct SimBank`. + let path = unsafe { gpiosim_bank_get_dev_path(self.bank) }; + + // SAFETY: The string is guaranteed to be valid here. + let path = str::from_utf8(unsafe { + slice::from_raw_parts(path as *const u8, strlen(path) as usize) + }) + .map_err(Error::StringNotUtf8)?; + + Ok(PathBuf::from(path)) + } + + fn val(&self, offset: Offset) -> Result { + let ret = unsafe { gpiosim_bank_get_value(self.bank, offset) }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::SimBankGetVal, + Errno::last(), + )) + } else { + Ok(ret as u32) + } + } + + fn set_label(&self, label: &str) -> Result<()> { + // Null-terminate the string + let label = label.to_owned() + "\0"; + + let ret = unsafe { gpiosim_bank_set_label(self.bank, label.as_ptr() as *const c_char) }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::SimBankSetLabel, + Errno::last(), + )) + } else { + Ok(()) + } + } + + fn set_num_lines(&self, num: u64) -> Result<()> { + let ret = unsafe { gpiosim_bank_set_num_lines(self.bank, num) }; + if ret == -1 { + Err(Error::OperationFailed( + OperationType::SimBankSetNumLines, + Errno::last(), + )) + } else { + Ok(()) + } + } + + fn set_line_name(&self, offset: Offset, name: &str) -> Result<()> { + // Null-terminate the string + let name = name.to_owned() + "\0"; + + let ret = unsafe { + gpiosim_bank_set_line_name(self.bank, offset, name.as_ptr() as *const c_char) + }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::SimBankSetLineName, + Errno::last(), + )) + } else { + Ok(()) + } + } + + fn set_pull(&self, offset: Offset, pull: i32) -> Result<()> { + let ret = unsafe { gpiosim_bank_set_pull(self.bank, offset, pull) }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::SimBankSetPull, + Errno::last(), + )) + } else { + Ok(()) + } + } + + fn hog_line(&self, offset: Offset, name: &str, dir: i32) -> Result<()> { + // Null-terminate the string + let name = name.to_owned() + "\0"; + + let ret = unsafe { + gpiosim_bank_hog_line(self.bank, offset, name.as_ptr() as *const c_char, dir) + }; + + if ret == -1 { + Err(Error::OperationFailed( + OperationType::SimBankHogLine, + Errno::last(), + )) + } else { + Ok(()) + } + } +} + +impl Drop for SimBank { + fn drop(&mut self) { + unsafe { gpiosim_bank_unref(self.bank) } + } +} + +/// GPIO SIM +#[derive(Debug)] +pub struct Sim { + ctx: SimCtx, + dev: SimDev, + bank: SimBank, +} + +unsafe impl Send for Sim {} +unsafe impl Sync for Sim {} + +impl Sim { + pub fn new(ngpio: Option, label: Option<&str>, enable: bool) -> Result { + let ctx = SimCtx::new()?; + let dev = SimDev::new(&ctx)?; + let bank = SimBank::new(&dev)?; + + if let Some(ngpio) = ngpio { + bank.set_num_lines(ngpio)?; + } + + if let Some(label) = label { + bank.set_label(label)?; + } + + if enable { + dev.enable()?; + } + + Ok(Self { ctx, dev, bank }) + } + + pub fn chip_name(&self) -> &str { + self.bank.chip_name().unwrap() + } + + pub fn dev_path(&self) -> PathBuf { + self.bank.dev_path().unwrap() + } + + pub fn val(&self, offset: Offset) -> Result { + self.bank.val(offset) + } + + pub fn set_label(&self, label: &str) -> Result<()> { + self.bank.set_label(label) + } + + pub fn set_num_lines(&self, num: u64) -> Result<()> { + self.bank.set_num_lines(num) + } + + pub fn set_line_name(&self, offset: Offset, name: &str) -> Result<()> { + self.bank.set_line_name(offset, name) + } + + pub fn set_pull(&self, offset: Offset, pull: i32) -> Result<()> { + self.bank.set_pull(offset, pull) + } + + pub fn hog_line(&self, offset: Offset, name: &str, dir: i32) -> Result<()> { + self.bank.hog_line(offset, name, dir) + } + + pub fn enable(&self) -> Result<()> { + self.dev.enable() + } + + pub fn disable(&self) -> Result<()> { + self.dev.disable() + } +} From patchwork Fri Oct 14 10:47:23 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 615142 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 1B454C433FE for ; Fri, 14 Oct 2022 10:47:56 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229698AbiJNKrx (ORCPT ); Fri, 14 Oct 2022 06:47:53 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47564 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229666AbiJNKru (ORCPT ); Fri, 14 Oct 2022 06:47:50 -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 5639F166981 for ; Fri, 14 Oct 2022 03:47:49 -0700 (PDT) Received: by mail-pj1-x102c.google.com with SMTP id l1-20020a17090a72c100b0020a6949a66aso4452048pjk.1 for ; Fri, 14 Oct 2022 03:47:49 -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=EVXZXVUpE8sG5pGObsRXu/fRk0wYVfAWuN3lNI2R653SuQjg7/t1QxbPBFbCOJ4nJz 3ZaM3kS4MMMHwj51rgfOT2EUl2Awge1l/8RA7fe2LlnZndVShwczRrQd5ihI6Fll18mg OoC1J+/ug/eMhARmlfnvlUu9lfuZbdAXfYDiK9yHxjrVHJ535UxQ/23+NTWG9/dXkX+C Cd0ZQO11Mg4vLyAYeh2jkvljtP559Na7LHzPaypMFZSMhIdqcTRqGK3FPct5KF69SjcF oljI6WtNwOtF2WCK3HfBPE+KbvgbNZdv9FUCpS1rGLyl6XrgITKH3bqLdvyHl3o59psM TgrA== 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=mn9h1nFmR/HE6ULYzoprj8F6abneS856gl5ZifKluBo9ILBG9qbOKnnaTGyf4WxdST nUSH4uj71Q5l6nJ5UVsFUteO0YBAQMk/3oMEU0BbDeCNIro0no0rlnuhfhSpVoZHPXPI BxuM6zePIEALalhM2/RUu3elMkiwB2fomh1zpgr6dF61KZWv8GI2oCKEwffqUM1dzOOG XYiBodTPkTeZARKUZImSi7HDYpuZ9/2RNF26avWOzgIm+lX494b4vwxDXGCDkAdW4Vwf SmGWbo+RUznxT1BxB2g3u7Ckx1r24NiYAr76n+OPastZtRduFsvC3BzpMzFPcWQesO1c DVUQ== X-Gm-Message-State: ACrzQf08y3jcWF92pg4fLFSegWd+FTKE4Ixv7vvAd9LoD+tDAk+mVcmP V5q5R4H5oNUQX/Wej8bUW9+Ivg== X-Google-Smtp-Source: AMsMyM7/0RkqzkrnzeRTnsMML8qyS8icx6NeX2qRqEXj897/UxGqVau/ANmph7lIoc9V72YVsb4p5w== X-Received: by 2002:a17:902:db06:b0:178:24e7:639 with SMTP id m6-20020a170902db0600b0017824e70639mr4841394plx.46.1665744468824; Fri, 14 Oct 2022 03:47:48 -0700 (PDT) Received: from localhost ([122.172.86.128]) by smtp.gmail.com with ESMTPSA id d1-20020a170903230100b00177f25f8ab3sm1467984plh.89.2022.10.14.03.47.47 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 14 Oct 2022 03:47:48 -0700 (PDT) From: Viresh Kumar To: Linus Walleij , Bartosz Golaszewski Cc: Viresh Kumar , Vincent Guittot , linux-gpio@vger.kernel.org, Kent Gibson , Miguel Ojeda , Wedson Almeida Filho , =?utf-8?q?Alex_Benn=C3=A9e?= , stratos-dev@op-lists.linaro.org, Gerard Ryan Subject: [PATCH V7 6/8] gpiosim: Add pre generated rust bindings Date: Fri, 14 Oct 2022 16:17:23 +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 Fri Oct 14 10:47:24 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 615330 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 90429C4332F for ; Fri, 14 Oct 2022 10:47:57 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229674AbiJNKr4 (ORCPT ); Fri, 14 Oct 2022 06:47:56 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47610 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229665AbiJNKr4 (ORCPT ); Fri, 14 Oct 2022 06:47:56 -0400 Received: from mail-pj1-x102a.google.com (mail-pj1-x102a.google.com [IPv6:2607:f8b0:4864:20::102a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5EDD017C571 for ; Fri, 14 Oct 2022 03:47:53 -0700 (PDT) Received: by mail-pj1-x102a.google.com with SMTP id fw14so4584826pjb.3 for ; Fri, 14 Oct 2022 03: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=/PqqC7ow9t7jrTBJVzrcMldISkd15Fbtqq81Nn4JxIE=; b=wXDFLlnhemmmEJPZRluOyRD7nhTYJibLFZmalIy7PVlTw1nEab0NkPvBuy815P3fc/ 7zJLjE1i+4/0zXnESQcx7FSUJcwbQhONMaG/TZfgPIR1vCK5odU/kjBIAQwJX93mY6XR YetHVyTyCZLWpCkefjGkTF10u1di+Vl/dPWEvXJ6zSBcVXxMtBL9jq+h2ZjIUsaaj+sa 3qkiEgsvLAKf4Nw8rSo9Beap/ZPARmoypz8MJBEJmGplQmImJAS/i6RBNkdc6jgK+i7W t7TeGn+8kgUgfIe3w3XfmZy6sYdX5nKzef7kQDS/4D64bOaJWsOXYEpzTsnYfzXbwRzD yh9Q== 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=/PqqC7ow9t7jrTBJVzrcMldISkd15Fbtqq81Nn4JxIE=; b=bJRuYJXdmEe3rTHJt+cS72Io5NvNxcCfkQVScMiTgRDCT3gEy4ybresDQpSfbx12QU 8GHKLOdRs4QGMig6DSUcWQ/1TMCkDJqZ+3rfli7+oV/RAhF4ZVRWMZx1mh/8MlZdww4l m3TWcO3vqEyu4fXTsSFZVjBHplHBIoAzzQ87nY5ncNRdHN4PRzkQqbJ2/0Un6rXLk9P/ 5Yb1tt0WjIEhz8Zjx5q7Znga3l2eylzuxD64rNzC4Kvz07Nw8BIp7Nv8jZNzYAC62nh3 xGlMcodE18XJAdpUCN9Csj4RJpZOHT9SfFsjL2pzS0jtibDRwjEN6Gbg8bIPuqvc4fw+ zE2g== X-Gm-Message-State: ACrzQf3KeVMK3l87dmL7R4CaXuH9s8Ke7GeqN549l1fce4TQVk8JN198 7zuhq8na8WW6oJ5/U2cwo538iXOa7HEm4A== X-Google-Smtp-Source: AMsMyM6cWrastWgPe/lWUIgA88gAa0m13AkYrnvq/eln+nb2SLDHJ7OHf2vgGoH7UXFTTIvMG3+RCQ== X-Received: by 2002:a17:90a:bb06:b0:20d:9ac5:c227 with SMTP id u6-20020a17090abb0600b0020d9ac5c227mr5147674pjr.4.1665744472541; Fri, 14 Oct 2022 03:47:52 -0700 (PDT) Received: from localhost ([122.172.86.128]) by smtp.gmail.com with ESMTPSA id b65-20020a62cf44000000b0053e6d352ae4sm1398624pfg.24.2022.10.14.03.47.50 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 14 Oct 2022 03:47:51 -0700 (PDT) From: Viresh Kumar To: Linus Walleij , Bartosz Golaszewski Cc: Viresh Kumar , Vincent Guittot , linux-gpio@vger.kernel.org, Kent Gibson , Miguel Ojeda , Wedson Almeida Filho , =?utf-8?q?Alex_Benn=C3=A9e?= , stratos-dev@op-lists.linaro.org, Gerard Ryan Subject: [PATCH V7 7/8] libgpiod: Add rust tests Date: Fri, 14 Oct 2022 16:17:24 +0530 Message-Id: X-Mailer: git-send-email 2.31.1.272.g89b43f80a514 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org Add tests for the rust bindings, quite similar to the ones in cxx bindings. Signed-off-by: Viresh Kumar --- bindings/rust/libgpiod/Cargo.toml | 3 + bindings/rust/libgpiod/tests/chip.rs | 97 ++++ bindings/rust/libgpiod/tests/common/config.rs | 134 +++++ bindings/rust/libgpiod/tests/common/mod.rs | 10 + bindings/rust/libgpiod/tests/edge_event.rs | 296 ++++++++++ bindings/rust/libgpiod/tests/info_event.rs | 125 +++++ bindings/rust/libgpiod/tests/line_config.rs | 105 ++++ bindings/rust/libgpiod/tests/line_info.rs | 279 ++++++++++ bindings/rust/libgpiod/tests/line_request.rs | 519 ++++++++++++++++++ bindings/rust/libgpiod/tests/line_settings.rs | 226 ++++++++ .../rust/libgpiod/tests/request_config.rs | 38 ++ 11 files changed, 1832 insertions(+) create mode 100644 bindings/rust/libgpiod/tests/chip.rs create mode 100644 bindings/rust/libgpiod/tests/common/config.rs create mode 100644 bindings/rust/libgpiod/tests/common/mod.rs create mode 100644 bindings/rust/libgpiod/tests/edge_event.rs create mode 100644 bindings/rust/libgpiod/tests/info_event.rs create mode 100644 bindings/rust/libgpiod/tests/line_config.rs create mode 100644 bindings/rust/libgpiod/tests/line_info.rs create mode 100644 bindings/rust/libgpiod/tests/line_request.rs create mode 100644 bindings/rust/libgpiod/tests/line_settings.rs create mode 100644 bindings/rust/libgpiod/tests/request_config.rs diff --git a/bindings/rust/libgpiod/Cargo.toml b/bindings/rust/libgpiod/Cargo.toml index f25242abb153..8abcaacdee67 100644 --- a/bindings/rust/libgpiod/Cargo.toml +++ b/bindings/rust/libgpiod/Cargo.toml @@ -11,3 +11,6 @@ libc = ">=0.2.39" libgpiod-sys = { path = "../libgpiod-sys" } thiserror = "1.0" vmm-sys-util = "=0.10.0" + +[dev-dependencies] +gpiosim = { path = "../gpiosim" } diff --git a/bindings/rust/libgpiod/tests/chip.rs b/bindings/rust/libgpiod/tests/chip.rs new file mode 100644 index 000000000000..ce592331b5e8 --- /dev/null +++ b/bindings/rust/libgpiod/tests/chip.rs @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +mod common; + +mod chip { + use libc::{ENODEV, ENOENT, ENOTTY}; + use std::path::PathBuf; + + use vmm_sys_util::errno::Error as Errno; + + use gpiosim::Sim; + use libgpiod::{chip::Chip, Error as ChipError, OperationType}; + + mod open { + use super::*; + + #[test] + fn nonexistent_file() { + assert_eq!( + Chip::open(&PathBuf::from("/dev/nonexistent")).unwrap_err(), + ChipError::OperationFailed(OperationType::ChipOpen, Errno::new(ENOENT)) + ); + } + + #[test] + fn no_dev_file() { + assert_eq!( + Chip::open(&PathBuf::from("/tmp")).unwrap_err(), + ChipError::OperationFailed(OperationType::ChipOpen, Errno::new(ENOTTY)) + ); + } + + #[test] + fn non_gpio_char_dev_file() { + assert_eq!( + Chip::open(&PathBuf::from("/dev/null")).unwrap_err(), + ChipError::OperationFailed(OperationType::ChipOpen, Errno::new(ENODEV)) + ); + } + + #[test] + fn gpiosim_file() { + let sim = Sim::new(None, None, true).unwrap(); + assert!(Chip::open(&sim.dev_path()).is_ok()); + } + } + + mod verify { + use super::*; + const NGPIO: u64 = 16; + const LABEL: &str = "foobar"; + + #[test] + fn basic_helpers() { + let sim = Sim::new(Some(NGPIO), Some(LABEL), true).unwrap(); + let chip = Chip::open(&sim.dev_path()).unwrap(); + + assert_eq!(chip.label().unwrap(), LABEL); + assert_eq!(chip.name().unwrap(), sim.chip_name()); + assert_eq!(chip.path().unwrap(), sim.dev_path().to_str().unwrap()); + assert_eq!(chip.num_lines(), NGPIO as usize); + assert!(chip.fd().is_ok()); + } + + #[test] + fn find_line() { + let sim = Sim::new(Some(NGPIO), None, false).unwrap(); + sim.set_line_name(0, "zero").unwrap(); + sim.set_line_name(2, "two").unwrap(); + sim.set_line_name(3, "three").unwrap(); + sim.set_line_name(5, "five").unwrap(); + sim.set_line_name(10, "ten").unwrap(); + sim.set_line_name(11, "ten").unwrap(); + sim.enable().unwrap(); + + let chip = Chip::open(&sim.dev_path()).unwrap(); + + // Success case + assert_eq!(chip.line_offset_from_name("zero").unwrap(), 0); + assert_eq!(chip.line_offset_from_name("two").unwrap(), 2); + assert_eq!(chip.line_offset_from_name("three").unwrap(), 3); + assert_eq!(chip.line_offset_from_name("five").unwrap(), 5); + + // Success with duplicate names, should return first entry + assert_eq!(chip.line_offset_from_name("ten").unwrap(), 10); + + // Failure + assert_eq!( + chip.line_offset_from_name("nonexistent").unwrap_err(), + ChipError::OperationFailed(OperationType::ChipGetLine, Errno::new(ENOENT)) + ); + } + } +} diff --git a/bindings/rust/libgpiod/tests/common/config.rs b/bindings/rust/libgpiod/tests/common/config.rs new file mode 100644 index 000000000000..ef4285067ebf --- /dev/null +++ b/bindings/rust/libgpiod/tests/common/config.rs @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use std::sync::Arc; +use std::time::Duration; + +use gpiosim::Sim; +use libgpiod::{ + chip::Chip, line, request, Bias, Direction, Drive, Edge, EventClock, Offset, Result, + SettingVal, Value, +}; + +pub(crate) struct TestConfig { + sim: Arc, + chip: Option, + request: Option, + rconfig: request::Config, + lconfig: line::Config, + lsettings: Option, +} + +impl TestConfig { + pub(crate) fn new(ngpio: u64) -> Result { + Ok(Self { + sim: Arc::new(Sim::new(Some(ngpio), None, true)?), + chip: None, + request: None, + rconfig: request::Config::new().unwrap(), + lconfig: line::Config::new().unwrap(), + lsettings: Some(line::Settings::new().unwrap()), + }) + } + + pub(crate) fn set_pull(&self, offsets: &[Offset], pulls: &[u32]) { + for i in 0..pulls.len() { + self.sim.set_pull(offsets[i], pulls[i] as i32).unwrap(); + } + } + + pub(crate) fn rconfig_set_consumer(&self, consumer: &str) { + self.rconfig.set_consumer(consumer); + } + + pub(crate) fn lconfig_val(&mut self, dir: Option, 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.dev_path())?; + + self.request = Some(chip.request_lines(&self.rconfig, &self.lconfig)?); + self.chip = Some(chip); + + Ok(()) + } + + pub(crate) fn sim(&self) -> Arc { + self.sim.clone() + } + + pub(crate) fn chip(&self) -> &Chip { + self.chip.as_ref().unwrap() + } + + pub(crate) fn lsettings(&mut self) -> &mut line::Settings { + self.lsettings.as_mut().unwrap() + } + + pub(crate) fn request(&self) -> &line::Request { + self.request.as_ref().unwrap() + } +} + +impl Drop for TestConfig { + fn drop(&mut self) { + // Explicit freeing is important to make sure "request" get freed + // before "sim" and "chip". + self.request = None; + } +} diff --git a/bindings/rust/libgpiod/tests/common/mod.rs b/bindings/rust/libgpiod/tests/common/mod.rs new file mode 100644 index 000000000000..8904f7121b4c --- /dev/null +++ b/bindings/rust/libgpiod/tests/common/mod.rs @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +#[allow(dead_code)] +mod config; + +#[allow(unused_imports)] +pub(crate) use config::*; diff --git a/bindings/rust/libgpiod/tests/edge_event.rs b/bindings/rust/libgpiod/tests/edge_event.rs new file mode 100644 index 000000000000..7dc4b489103b --- /dev/null +++ b/bindings/rust/libgpiod/tests/edge_event.rs @@ -0,0 +1,296 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +mod common; + +mod edge_event { + use std::sync::Arc; + use std::thread::{sleep, spawn}; + use std::time::Duration; + + use crate::common::*; + use gpiosim::Sim; + use gpiosim::{GPIOSIM_PULL_DOWN, GPIOSIM_PULL_UP}; + use libgpiod::{edge, Edge, EdgeKind, Offset}; + + const NGPIO: u64 = 8; + + mod buffer_capacity { + use super::*; + + #[test] + fn default_capacity() { + assert_eq!(edge::event::Buffer::new(0).unwrap().capacity(), 64); + } + + #[test] + fn user_defined_capacity() { + assert_eq!(edge::event::Buffer::new(123).unwrap().capacity(), 123); + } + + #[test] + fn max_capacity() { + assert_eq!(edge::event::Buffer::new(1024 * 2).unwrap().capacity(), 1024); + } + } + + mod trigger { + use super::*; + + // Helpers to generate events + fn trigger_falling_and_rising_edge(sim: Arc, offset: Offset) { + spawn(move || { + sleep(Duration::from_millis(30)); + sim.set_pull(offset, GPIOSIM_PULL_UP as i32).unwrap(); + + sleep(Duration::from_millis(30)); + sim.set_pull(offset, GPIOSIM_PULL_DOWN as i32).unwrap(); + }); + } + + fn trigger_rising_edge_events_on_two_offsets(sim: Arc, offset: [Offset; 2]) { + spawn(move || { + sleep(Duration::from_millis(30)); + sim.set_pull(offset[0], GPIOSIM_PULL_UP as i32).unwrap(); + + sleep(Duration::from_millis(30)); + sim.set_pull(offset[1], GPIOSIM_PULL_UP as i32).unwrap(); + }); + } + + fn trigger_multiple_events(sim: Arc, offset: Offset) { + sim.set_pull(offset, GPIOSIM_PULL_UP as i32).unwrap(); + sleep(Duration::from_millis(10)); + + sim.set_pull(offset, GPIOSIM_PULL_DOWN as i32).unwrap(); + sleep(Duration::from_millis(10)); + + sim.set_pull(offset, GPIOSIM_PULL_UP as i32).unwrap(); + sleep(Duration::from_millis(10)); + } + + #[test] + fn both_edges() { + const GPIO: Offset = 2; + let mut buf = edge::event::Buffer::new(0).unwrap(); + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_edge(None, Some(Edge::Both)); + config.lconfig_add_settings(&[GPIO]); + config.request_lines().unwrap(); + + // Generate events + trigger_falling_and_rising_edge(config.sim(), GPIO); + + // Rising event + assert!(config + .request() + .wait_edge_event(Some(Duration::from_secs(1))) + .unwrap()); + + assert_eq!(config.request().read_edge_events(&mut buf).unwrap(), 1); + assert_eq!(buf.len(), 1); + + let event = buf.event(0).unwrap(); + let ts_rising = event.timestamp(); + assert_eq!(event.event_type().unwrap(), EdgeKind::Rising); + assert_eq!(event.line_offset(), GPIO); + drop(event); + + // Falling event + assert!(config + .request() + .wait_edge_event(Some(Duration::from_secs(1))) + .unwrap()); + + assert_eq!(config.request().read_edge_events(&mut buf).unwrap(), 1); + assert_eq!(buf.len(), 1); + + let event = buf.event(0).unwrap(); + let ts_falling = event.timestamp(); + assert_eq!(event.event_type().unwrap(), EdgeKind::Falling); + assert_eq!(event.line_offset(), GPIO); + + // No events available + assert!(!config + .request() + .wait_edge_event(Some(Duration::from_millis(100))) + .unwrap()); + + assert!(ts_falling > ts_rising); + } + + #[test] + fn rising_edge() { + const GPIO: Offset = 6; + let mut buf = edge::event::Buffer::new(0).unwrap(); + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_edge(None, Some(Edge::Rising)); + config.lconfig_add_settings(&[GPIO]); + config.request_lines().unwrap(); + + // Generate events + trigger_falling_and_rising_edge(config.sim(), GPIO); + + // Rising event + assert!(config + .request() + .wait_edge_event(Some(Duration::from_secs(1))) + .unwrap()); + + assert_eq!(config.request().read_edge_events(&mut buf).unwrap(), 1); + assert_eq!(buf.len(), 1); + + let event = buf.event(0).unwrap(); + assert_eq!(event.event_type().unwrap(), EdgeKind::Rising); + assert_eq!(event.line_offset(), GPIO); + + // No events available + assert!(!config + .request() + .wait_edge_event(Some(Duration::from_millis(100))) + .unwrap()); + } + + #[test] + fn falling_edge() { + const GPIO: Offset = 7; + let mut buf = edge::event::Buffer::new(0).unwrap(); + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_edge(None, Some(Edge::Falling)); + config.lconfig_add_settings(&[GPIO]); + config.request_lines().unwrap(); + + // Generate events + trigger_falling_and_rising_edge(config.sim(), GPIO); + + // Falling event + assert!(config + .request() + .wait_edge_event(Some(Duration::from_secs(1))) + .unwrap()); + + assert_eq!(config.request().read_edge_events(&mut buf).unwrap(), 1); + assert_eq!(buf.len(), 1); + + let event = buf.event(0).unwrap(); + assert_eq!(event.event_type().unwrap(), EdgeKind::Falling); + assert_eq!(event.line_offset(), GPIO); + + // No events available + assert!(!config + .request() + .wait_edge_event(Some(Duration::from_millis(100))) + .unwrap()); + } + + #[test] + fn edge_sequence() { + const GPIO: [u32; 2] = [0, 1]; + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_edge(None, Some(Edge::Both)); + config.lconfig_add_settings(&GPIO); + config.request_lines().unwrap(); + + // Generate events + trigger_rising_edge_events_on_two_offsets(config.sim(), GPIO); + + // Rising event GPIO 0 + let mut buf = edge::event::Buffer::new(0).unwrap(); + assert!(config + .request() + .wait_edge_event(Some(Duration::from_secs(1))) + .unwrap()); + + assert_eq!(config.request().read_edge_events(&mut buf).unwrap(), 1); + assert_eq!(buf.len(), 1); + + let event = buf.event(0).unwrap(); + assert_eq!(event.event_type().unwrap(), EdgeKind::Rising); + assert_eq!(event.line_offset(), GPIO[0]); + assert_eq!(event.global_seqno(), 1); + assert_eq!(event.line_seqno(), 1); + drop(event); + + // Rising event GPIO 1 + assert!(config + .request() + .wait_edge_event(Some(Duration::from_secs(1))) + .unwrap()); + + assert_eq!(config.request().read_edge_events(&mut buf).unwrap(), 1); + assert_eq!(buf.len(), 1); + + let event = buf.event(0).unwrap(); + assert_eq!(event.event_type().unwrap(), EdgeKind::Rising); + assert_eq!(event.line_offset(), GPIO[1]); + assert_eq!(event.global_seqno(), 2); + assert_eq!(event.line_seqno(), 1); + + // No events available + assert!(!config + .request() + .wait_edge_event(Some(Duration::from_millis(100))) + .unwrap()); + } + + #[test] + fn multiple_events() { + const GPIO: Offset = 1; + let mut buf = edge::event::Buffer::new(0).unwrap(); + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_edge(None, Some(Edge::Both)); + config.lconfig_add_settings(&[GPIO]); + config.request_lines().unwrap(); + + // Generate events + trigger_multiple_events(config.sim(), GPIO); + + // Read multiple events + assert!(config + .request() + .wait_edge_event(Some(Duration::from_secs(1))) + .unwrap()); + + assert_eq!(config.request().read_edge_events(&mut buf).unwrap(), 3); + assert_eq!(buf.len(), 3); + + let mut global_seqno = 1; + let mut line_seqno = 1; + + // Verify sequence number of events + for i in 0..3 { + let event = buf.event(i).unwrap(); + assert_eq!(event.line_offset(), GPIO); + assert_eq!(event.global_seqno(), global_seqno); + assert_eq!(event.line_seqno(), line_seqno); + + global_seqno += 1; + line_seqno += 1; + } + } + + #[test] + fn over_capacity() { + const GPIO: Offset = 2; + let mut buf = edge::event::Buffer::new(2).unwrap(); + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_edge(None, Some(Edge::Both)); + config.lconfig_add_settings(&[GPIO]); + config.request_lines().unwrap(); + + // Generate events + trigger_multiple_events(config.sim(), GPIO); + + // Read multiple events + assert!(config + .request() + .wait_edge_event(Some(Duration::from_secs(1))) + .unwrap()); + + assert_eq!(config.request().read_edge_events(&mut buf).unwrap(), 2); + assert_eq!(buf.len(), 2); + } + } +} diff --git a/bindings/rust/libgpiod/tests/info_event.rs b/bindings/rust/libgpiod/tests/info_event.rs new file mode 100644 index 000000000000..8c342992d74e --- /dev/null +++ b/bindings/rust/libgpiod/tests/info_event.rs @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +mod common; + +mod info_event { + use libc::EINVAL; + use std::sync::Arc; + use std::thread::{sleep, spawn}; + use std::time::Duration; + + use vmm_sys_util::errno::Error as Errno; + + use gpiosim::Sim; + use libgpiod::{ + chip::Chip, line, request, Direction, Error as ChipError, InfoChangeKind, Offset, + OperationType, SettingVal, + }; + + fn request_reconfigure_line(chip: Arc) { + spawn(move || { + sleep(Duration::from_millis(10)); + + let lconfig1 = line::Config::new().unwrap(); + let lsettings = line::Settings::new().unwrap(); + lconfig1.add_line_settings(&[7], lsettings).unwrap(); + let rconfig = request::Config::new().unwrap(); + + let request = chip.request_lines(&rconfig, &lconfig1).unwrap(); + + sleep(Duration::from_millis(10)); + + let lconfig2 = line::Config::new().unwrap(); + let mut lsettings = line::Settings::new().unwrap(); + lsettings + .set_prop(&[SettingVal::Direction(Direction::Output)]) + .unwrap(); + lconfig2.add_line_settings(&[7], lsettings).unwrap(); + + request.reconfigure_lines(&lconfig2).unwrap(); + + sleep(Duration::from_millis(10)); + }); + } + + mod watch { + use super::*; + const NGPIO: u64 = 8; + const GPIO: Offset = 7; + + #[test] + fn line_info() { + let sim = Sim::new(Some(NGPIO), None, true).unwrap(); + let chip = Chip::open(&sim.dev_path()).unwrap(); + + assert_eq!( + chip.watch_line_info(NGPIO as u32).unwrap_err(), + ChipError::OperationFailed(OperationType::ChipWatchLineInfo, Errno::new(EINVAL)) + ); + + let info = chip.watch_line_info(GPIO).unwrap(); + assert_eq!(info.offset(), GPIO); + + // No events available + assert!(!chip + .wait_info_event(Some(Duration::from_millis(100))) + .unwrap()); + } + + #[test] + fn reconfigure() { + let sim = Sim::new(Some(NGPIO), None, true).unwrap(); + let chip = Arc::new(Chip::open(&sim.dev_path()).unwrap()); + let info = chip.watch_line_info(GPIO).unwrap(); + + assert_eq!(info.direction().unwrap(), Direction::Input); + + // Generate events + request_reconfigure_line(chip.clone()); + + // Line requested event + assert!(chip.wait_info_event(Some(Duration::from_secs(1))).unwrap()); + let event = chip.read_info_event().unwrap(); + let ts_req = event.timestamp(); + + assert_eq!(event.event_type().unwrap(), InfoChangeKind::LineRequested); + assert_eq!( + event.line_info().unwrap().direction().unwrap(), + Direction::Input + ); + + // Line changed event + assert!(chip.wait_info_event(Some(Duration::from_secs(1))).unwrap()); + let event = chip.read_info_event().unwrap(); + let ts_rec = event.timestamp(); + + assert_eq!( + event.event_type().unwrap(), + InfoChangeKind::LineConfigChanged + ); + assert_eq!( + event.line_info().unwrap().direction().unwrap(), + Direction::Output + ); + + // Line released event + assert!(chip.wait_info_event(Some(Duration::from_secs(1))).unwrap()); + let event = chip.read_info_event().unwrap(); + let ts_rel = event.timestamp(); + + assert_eq!(event.event_type().unwrap(), InfoChangeKind::LineReleased); + + // No events available + assert!(!chip + .wait_info_event(Some(Duration::from_millis(100))) + .unwrap()); + + // Check timestamps are really monotonic. + assert!(ts_rel > ts_rec); + assert!(ts_rec > ts_req); + } + } +} diff --git a/bindings/rust/libgpiod/tests/line_config.rs b/bindings/rust/libgpiod/tests/line_config.rs new file mode 100644 index 000000000000..e65cd1d23f9b --- /dev/null +++ b/bindings/rust/libgpiod/tests/line_config.rs @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +mod common; + +mod line_config { + use libgpiod::{ + line, Bias, Direction, Drive, Edge, EventClock, SettingKind, SettingVal, Value, + }; + + #[test] + fn settings() { + let mut lsettings1 = line::Settings::new().unwrap(); + lsettings1 + .set_prop(&[SettingVal::Direction(Direction::Input)]) + .unwrap(); + lsettings1 + .set_prop(&[SettingVal::EdgeDetection(Some(Edge::Both))]) + .unwrap(); + lsettings1 + .set_prop(&[SettingVal::Bias(Some(Bias::PullDown))]) + .unwrap(); + lsettings1 + .set_prop(&[SettingVal::Drive(Drive::PushPull)]) + .unwrap(); + + let mut lsettings2 = line::Settings::new().unwrap(); + lsettings2 + .set_prop(&[SettingVal::Direction(Direction::Output)]) + .unwrap(); + lsettings2.set_prop(&[SettingVal::ActiveLow(true)]).unwrap(); + lsettings2 + .set_prop(&[SettingVal::EventClock(EventClock::Realtime)]) + .unwrap(); + lsettings2 + .set_prop(&[SettingVal::OutputValue(Value::Active)]) + .unwrap(); + + // Add settings for multiple lines + let lconfig = line::Config::new().unwrap(); + lconfig.add_line_settings(&[0, 1, 2], lsettings1).unwrap(); + lconfig.add_line_settings(&[4, 5], lsettings2).unwrap(); + + // Retrieve settings + let lsettings = lconfig.line_settings(1).unwrap(); + assert_eq!( + lsettings.prop(SettingKind::Direction).unwrap(), + SettingVal::Direction(Direction::Input) + ); + assert_eq!( + lsettings.prop(SettingKind::EdgeDetection).unwrap(), + SettingVal::EdgeDetection(Some(Edge::Both)) + ); + assert_eq!( + lsettings.prop(SettingKind::Bias).unwrap(), + SettingVal::Bias(Some(Bias::PullDown)) + ); + assert_eq!( + lsettings.prop(SettingKind::Drive).unwrap(), + SettingVal::Drive(Drive::PushPull) + ); + + let lsettings = lconfig.line_settings(5).unwrap(); + assert_eq!( + lsettings.prop(SettingKind::Direction).unwrap(), + SettingVal::Direction(Direction::Output) + ); + assert_eq!( + lsettings.prop(SettingKind::ActiveLow).unwrap(), + SettingVal::ActiveLow(true) + ); + assert_eq!( + lsettings.prop(SettingKind::EventClock).unwrap(), + SettingVal::EventClock(EventClock::Realtime) + ); + assert_eq!( + lsettings.prop(SettingKind::OutputValue).unwrap(), + SettingVal::OutputValue(Value::Active) + ); + } + + #[test] + fn offsets() { + let mut lsettings1 = line::Settings::new().unwrap(); + lsettings1 + .set_prop(&[SettingVal::Direction(Direction::Input)]) + .unwrap(); + + let mut lsettings2 = line::Settings::new().unwrap(); + lsettings2 + .set_prop(&[SettingVal::EventClock(EventClock::Realtime)]) + .unwrap(); + + // Add settings for multiple lines + let lconfig = line::Config::new().unwrap(); + lconfig.add_line_settings(&[0, 1, 2], lsettings1).unwrap(); + lconfig.add_line_settings(&[4, 5], lsettings2).unwrap(); + + // Verify offsets + let offsets = lconfig.offsets().unwrap(); + assert_eq!(offsets, [0, 1, 2, 4, 5]); + } +} diff --git a/bindings/rust/libgpiod/tests/line_info.rs b/bindings/rust/libgpiod/tests/line_info.rs new file mode 100644 index 000000000000..22edeb4da263 --- /dev/null +++ b/bindings/rust/libgpiod/tests/line_info.rs @@ -0,0 +1,279 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +mod common; + +mod line_info { + use libc::EINVAL; + use std::time::Duration; + + use vmm_sys_util::errno::Error as Errno; + + use crate::common::*; + use gpiosim::Sim; + use gpiosim::{GPIOSIM_HOG_DIR_INPUT, GPIOSIM_HOG_DIR_OUTPUT_HIGH, GPIOSIM_HOG_DIR_OUTPUT_LOW}; + use libgpiod::{ + chip::Chip, Bias, Direction, Drive, Edge, Error as ChipError, EventClock, OperationType, + }; + + const NGPIO: u64 = 8; + + mod properties { + use super::*; + + #[test] + fn default() { + let sim = Sim::new(Some(NGPIO), None, false).unwrap(); + sim.set_line_name(4, "four").unwrap(); + sim.hog_line(4, "hog4", GPIOSIM_HOG_DIR_OUTPUT_LOW as i32) + .unwrap(); + sim.enable().unwrap(); + + let chip = Chip::open(&sim.dev_path()).unwrap(); + + let info4 = chip.line_info(4).unwrap(); + assert_eq!(info4.offset(), 4); + assert_eq!(info4.name().unwrap(), "four"); + assert!(info4.is_used()); + assert_eq!(info4.consumer().unwrap(), "hog4"); + assert_eq!(info4.direction().unwrap(), Direction::Output); + assert!(!info4.is_active_low()); + assert_eq!(info4.bias().unwrap(), None); + assert_eq!(info4.drive().unwrap(), Drive::PushPull); + assert_eq!(info4.edge_detection().unwrap(), None); + assert_eq!(info4.event_clock().unwrap(), EventClock::Monotonic); + assert!(!info4.is_debounced()); + assert_eq!(info4.debounce_period(), Duration::from_millis(0)); + + assert_eq!( + chip.line_info(NGPIO as u32).unwrap_err(), + ChipError::OperationFailed(OperationType::ChipGetLineInfo, Errno::new(EINVAL)) + ); + } + + #[test] + fn name_and_offset() { + let sim = Sim::new(Some(NGPIO), None, false).unwrap(); + + // Line 0 has no name + for i in 1..NGPIO { + sim.set_line_name(i as u32, &i.to_string()).unwrap(); + } + sim.enable().unwrap(); + + let chip = Chip::open(&sim.dev_path()).unwrap(); + let info = chip.line_info(0).unwrap(); + + assert_eq!(info.offset(), 0); + assert_eq!( + info.name().unwrap_err(), + ChipError::NullString("GPIO line's name") + ); + + for i in 1..NGPIO { + let info = chip.line_info(i as u32).unwrap(); + + assert_eq!(info.offset(), i as u32); + assert_eq!(info.name().unwrap(), &i.to_string()); + } + } + + #[test] + fn is_used() { + let sim = Sim::new(Some(NGPIO), None, false).unwrap(); + sim.hog_line(0, "hog", GPIOSIM_HOG_DIR_OUTPUT_HIGH as i32) + .unwrap(); + sim.enable().unwrap(); + + let chip = Chip::open(&sim.dev_path()).unwrap(); + + let info = chip.line_info(0).unwrap(); + assert!(info.is_used()); + + let info = chip.line_info(1).unwrap(); + assert!(!info.is_used()); + } + + #[test] + fn consumer() { + let sim = Sim::new(Some(NGPIO), None, false).unwrap(); + sim.hog_line(0, "hog", GPIOSIM_HOG_DIR_OUTPUT_HIGH as i32) + .unwrap(); + sim.enable().unwrap(); + + let chip = Chip::open(&sim.dev_path()).unwrap(); + + let info = chip.line_info(0).unwrap(); + assert_eq!(info.consumer().unwrap(), "hog"); + + let info = chip.line_info(1).unwrap(); + assert_eq!( + info.consumer().unwrap_err(), + ChipError::NullString("GPIO line's consumer name") + ); + } + + #[test] + fn direction() { + let sim = Sim::new(Some(NGPIO), None, false).unwrap(); + sim.hog_line(0, "hog", GPIOSIM_HOG_DIR_INPUT as i32) + .unwrap(); + sim.hog_line(1, "hog", GPIOSIM_HOG_DIR_OUTPUT_HIGH as i32) + .unwrap(); + sim.hog_line(2, "hog", GPIOSIM_HOG_DIR_OUTPUT_LOW as i32) + .unwrap(); + sim.enable().unwrap(); + + let chip = Chip::open(&sim.dev_path()).unwrap(); + + let info = chip.line_info(0).unwrap(); + assert_eq!(info.direction().unwrap(), Direction::Input); + + let info = chip.line_info(1).unwrap(); + assert_eq!(info.direction().unwrap(), Direction::Output); + + let info = chip.line_info(2).unwrap(); + assert_eq!(info.direction().unwrap(), Direction::Output); + } + + #[test] + fn bias() { + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.bias().unwrap(), None); + + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_bias(Direction::Input, Some(Bias::PullUp)); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.bias().unwrap(), Some(Bias::PullUp)); + + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_bias(Direction::Input, Some(Bias::PullDown)); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.bias().unwrap(), Some(Bias::PullDown)); + + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_bias(Direction::Input, Some(Bias::Disabled)); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.bias().unwrap(), Some(Bias::Disabled)); + } + + #[test] + fn drive() { + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.drive().unwrap(), Drive::PushPull); + + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_drive(Direction::Input, Drive::PushPull); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.drive().unwrap(), Drive::PushPull); + + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_drive(Direction::Output, Drive::OpenDrain); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.drive().unwrap(), Drive::OpenDrain); + + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_drive(Direction::Output, Drive::OpenSource); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.drive().unwrap(), Drive::OpenSource); + } + + #[test] + fn edge() { + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.edge_detection().unwrap(), None); + + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_edge(Some(Direction::Input), Some(Edge::Both)); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.edge_detection().unwrap(), Some(Edge::Both)); + + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_edge(Some(Direction::Input), Some(Edge::Rising)); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.edge_detection().unwrap(), Some(Edge::Rising)); + + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_edge(Some(Direction::Input), Some(Edge::Falling)); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.edge_detection().unwrap(), Some(Edge::Falling)); + } + + #[test] + fn clock() { + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.event_clock().unwrap(), EventClock::Monotonic); + + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_clock(EventClock::Monotonic); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.event_clock().unwrap(), EventClock::Monotonic); + + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_clock(EventClock::Realtime); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.event_clock().unwrap(), EventClock::Realtime); + + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_clock(EventClock::HTE); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.event_clock().unwrap(), EventClock::HTE); + } + + #[test] + fn debounce() { + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert!(!info.is_debounced()); + assert_eq!(info.debounce_period(), Duration::from_millis(0)); + + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_debounce(Duration::from_millis(100)); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert!(info.is_debounced()); + assert_eq!(info.debounce_period(), Duration::from_millis(100)); + } + } +} diff --git a/bindings/rust/libgpiod/tests/line_request.rs b/bindings/rust/libgpiod/tests/line_request.rs new file mode 100644 index 000000000000..e933fe7cb74a --- /dev/null +++ b/bindings/rust/libgpiod/tests/line_request.rs @@ -0,0 +1,519 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +mod common; + +mod line_request { + use libc::{E2BIG, EINVAL}; + use std::time::Duration; + + use vmm_sys_util::errno::Error as Errno; + + use crate::common::*; + use gpiosim::{ + GPIOSIM_PULL_DOWN, GPIOSIM_PULL_UP, GPIOSIM_VALUE_ACTIVE, GPIOSIM_VALUE_INACTIVE, + }; + use libgpiod::{ + line, Bias, Direction, Drive, Edge, Error as ChipError, EventClock, Offset, OperationType, + SettingVal, Value, ValueMap, + }; + + const NGPIO: u64 = 8; + + mod invalid_arguments { + use super::*; + + #[test] + fn no_offsets() { + let mut config = TestConfig::new(NGPIO).unwrap(); + + assert_eq!( + config.request_lines().unwrap_err(), + ChipError::OperationFailed(OperationType::LineRequest, Errno::new(EINVAL)) + ); + } + + #[test] + fn out_of_bound_offsets() { + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_add_settings(&[2, 0, 8, 4]); + + assert_eq!( + config.request_lines().unwrap_err(), + ChipError::OperationFailed(OperationType::LineRequest, Errno::new(EINVAL)) + ); + } + + #[test] + fn dir_out_edge_failure() { + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_edge(Some(Direction::Output), Some(Edge::Both)); + config.lconfig_add_settings(&[0]); + + assert_eq!( + config.request_lines().unwrap_err(), + ChipError::OperationFailed(OperationType::LineRequest, Errno::new(EINVAL)) + ); + } + } + + mod verify { + use super::*; + + #[test] + fn custom_consumer() { + const GPIO: Offset = 2; + const CONSUMER: &str = "foobar"; + let mut config = TestConfig::new(NGPIO).unwrap(); + config.rconfig_set_consumer(CONSUMER); + config.lconfig_add_settings(&[GPIO]); + config.request_lines().unwrap(); + + let info = config.chip().line_info(GPIO).unwrap(); + + assert!(info.is_used()); + assert_eq!(info.consumer().unwrap(), CONSUMER); + } + + #[test] + fn empty_consumer() { + const GPIO: Offset = 2; + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_add_settings(&[GPIO]); + config.request_lines().unwrap(); + + let info = config.chip().line_info(GPIO).unwrap(); + + assert!(info.is_used()); + assert_eq!(info.consumer().unwrap(), "?"); + } + + #[test] + fn read_values() { + let offsets = [7, 1, 0, 6, 2]; + let pulls = [ + GPIOSIM_PULL_UP, + GPIOSIM_PULL_UP, + GPIOSIM_PULL_DOWN, + GPIOSIM_PULL_UP, + GPIOSIM_PULL_DOWN, + ]; + let mut config = TestConfig::new(NGPIO).unwrap(); + config.set_pull(&offsets, &pulls); + config.lconfig_val(Some(Direction::Input), None); + config.lconfig_add_settings(&offsets); + config.request_lines().unwrap(); + + let request = config.request(); + + // Single values read properly + assert_eq!(request.value(7).unwrap(), Value::Active); + + // Values read properly + let map = request.values().unwrap(); + for i in 0..offsets.len() { + assert_eq!( + *map.get(offsets[i].into()).unwrap(), + match pulls[i] { + GPIOSIM_PULL_DOWN => Value::InActive, + _ => Value::Active, + } + ); + } + + // Subset of values read properly + let map = request.values_subset(&[2, 0, 6]).unwrap(); + assert_eq!(*map.get(2).unwrap(), Value::InActive); + assert_eq!(*map.get(0).unwrap(), Value::InActive); + assert_eq!(*map.get(6).unwrap(), Value::Active); + + // Value read properly after reconfigure + let mut lsettings = line::Settings::new().unwrap(); + lsettings.set_prop(&[SettingVal::ActiveLow(true)]).unwrap(); + let lconfig = line::Config::new().unwrap(); + lconfig.add_line_settings(&offsets, lsettings).unwrap(); + request.reconfigure_lines(&lconfig).unwrap(); + assert_eq!(request.value(7).unwrap(), Value::InActive); + } + + #[test] + fn set_output_values() { + let offsets = [0, 1, 3, 4]; + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_val(Some(Direction::Output), Some(Value::Active)); + config.lconfig_add_settings(&offsets); + config.request_lines().unwrap(); + + assert_eq!(config.sim().val(0).unwrap(), GPIOSIM_VALUE_ACTIVE); + assert_eq!(config.sim().val(1).unwrap(), GPIOSIM_VALUE_ACTIVE); + assert_eq!(config.sim().val(3).unwrap(), GPIOSIM_VALUE_ACTIVE); + assert_eq!(config.sim().val(4).unwrap(), GPIOSIM_VALUE_ACTIVE); + + // Default + assert_eq!(config.sim().val(2).unwrap(), GPIOSIM_VALUE_INACTIVE); + } + + #[test] + fn update_output_values() { + let offsets = [0, 1, 3, 4]; + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_val(Some(Direction::Output), Some(Value::InActive)); + config.lconfig_add_settings(&offsets); + config.request_lines().unwrap(); + let request = config.request(); + + // Set single value + request.set_value(1, Value::Active).unwrap(); + assert_eq!(config.sim().val(0).unwrap(), GPIOSIM_VALUE_INACTIVE); + assert_eq!(config.sim().val(1).unwrap(), GPIOSIM_VALUE_ACTIVE); + assert_eq!(config.sim().val(3).unwrap(), GPIOSIM_VALUE_INACTIVE); + assert_eq!(config.sim().val(4).unwrap(), GPIOSIM_VALUE_INACTIVE); + request.set_value(1, Value::InActive).unwrap(); + assert_eq!(config.sim().val(1).unwrap(), GPIOSIM_VALUE_INACTIVE); + + // Set values of subset + let mut map = ValueMap::new(); + map.insert(4, Value::Active); + map.insert(3, Value::Active); + request.set_values_subset(map).unwrap(); + assert_eq!(config.sim().val(0).unwrap(), GPIOSIM_VALUE_INACTIVE); + assert_eq!(config.sim().val(1).unwrap(), GPIOSIM_VALUE_INACTIVE); + assert_eq!(config.sim().val(3).unwrap(), GPIOSIM_VALUE_ACTIVE); + assert_eq!(config.sim().val(4).unwrap(), GPIOSIM_VALUE_ACTIVE); + + let mut map = ValueMap::new(); + map.insert(4, Value::InActive); + map.insert(3, Value::InActive); + request.set_values_subset(map).unwrap(); + assert_eq!(config.sim().val(3).unwrap(), GPIOSIM_VALUE_INACTIVE); + assert_eq!(config.sim().val(4).unwrap(), GPIOSIM_VALUE_INACTIVE); + + // Set all values + request + .set_values(&[ + Value::Active, + Value::InActive, + Value::Active, + Value::InActive, + ]) + .unwrap(); + assert_eq!(config.sim().val(0).unwrap(), GPIOSIM_VALUE_ACTIVE); + assert_eq!(config.sim().val(1).unwrap(), GPIOSIM_VALUE_INACTIVE); + assert_eq!(config.sim().val(3).unwrap(), GPIOSIM_VALUE_ACTIVE); + assert_eq!(config.sim().val(4).unwrap(), GPIOSIM_VALUE_INACTIVE); + request + .set_values(&[ + Value::InActive, + Value::InActive, + Value::InActive, + Value::InActive, + ]) + .unwrap(); + assert_eq!(config.sim().val(0).unwrap(), GPIOSIM_VALUE_INACTIVE); + assert_eq!(config.sim().val(1).unwrap(), GPIOSIM_VALUE_INACTIVE); + assert_eq!(config.sim().val(3).unwrap(), GPIOSIM_VALUE_INACTIVE); + assert_eq!(config.sim().val(4).unwrap(), GPIOSIM_VALUE_INACTIVE); + } + + #[test] + fn set_bias() { + let offsets = [3]; + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_bias(Direction::Input, Some(Bias::PullUp)); + config.lconfig_add_settings(&offsets); + config.request_lines().unwrap(); + config.request(); + + // Set single value + assert_eq!(config.sim().val(3).unwrap(), GPIOSIM_VALUE_ACTIVE); + } + + #[test] + fn no_events() { + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_edge(None, Some(Edge::Both)); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + + // No events available + assert!(!config + .request() + .wait_edge_event(Some(Duration::from_millis(100))) + .unwrap()); + } + } + + mod reconfigure { + use super::*; + + #[test] + fn e2big() { + let offsets = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + let mut config = TestConfig::new(16).unwrap(); + config.lconfig_add_settings(&offsets); + config.request_lines().unwrap(); + + let request = config.request(); + + // Reconfigure + let mut lsettings = line::Settings::new().unwrap(); + lsettings + .set_prop(&[SettingVal::Direction(Direction::Input)]) + .unwrap(); + let lconfig = line::Config::new().unwrap(); + + // The uAPI config has only 10 attribute slots, this should pass. + for offset in offsets { + lsettings + .set_prop(&[SettingVal::DebouncePeriod(Duration::from_millis( + (100 + offset).into(), + ))]) + .unwrap(); + lconfig + .add_line_settings(&[offset as Offset], lsettings.settings_clone().unwrap()) + .unwrap(); + } + + assert!(request.reconfigure_lines(&lconfig).is_ok()); + + // The uAPI config has only 10 attribute slots, and this is the 11th entry. + // This should fail with E2BIG. + lsettings + .set_prop(&[SettingVal::DebouncePeriod(Duration::from_millis(100 + 11))]) + .unwrap(); + lconfig.add_line_settings(&[11], lsettings).unwrap(); + + assert_eq!( + request.reconfigure_lines(&lconfig).unwrap_err(), + ChipError::OperationFailed( + OperationType::LineRequestReconfigLines, + Errno::new(E2BIG), + ) + ); + } + + #[test] + fn bias() { + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.bias().unwrap(), None); + + let request = config.request(); + + // Reconfigure + let lconfig = line::Config::new().unwrap(); + let mut lsettings = line::Settings::new().unwrap(); + lsettings + .set_prop(&[ + SettingVal::Direction(Direction::Input), + SettingVal::Bias(Some(Bias::PullUp)), + ]) + .unwrap(); + lconfig.add_line_settings(&[0], lsettings).unwrap(); + request.reconfigure_lines(&lconfig).unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.bias().unwrap(), Some(Bias::PullUp)); + + let lconfig = line::Config::new().unwrap(); + let mut lsettings = line::Settings::new().unwrap(); + lsettings + .set_prop(&[ + SettingVal::Direction(Direction::Input), + SettingVal::Bias(Some(Bias::PullDown)), + ]) + .unwrap(); + lconfig.add_line_settings(&[0], lsettings).unwrap(); + request.reconfigure_lines(&lconfig).unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.bias().unwrap(), Some(Bias::PullDown)); + + let lconfig = line::Config::new().unwrap(); + let mut lsettings = line::Settings::new().unwrap(); + lsettings + .set_prop(&[ + SettingVal::Direction(Direction::Input), + SettingVal::Bias(Some(Bias::Disabled)), + ]) + .unwrap(); + lconfig.add_line_settings(&[0], lsettings).unwrap(); + request.reconfigure_lines(&lconfig).unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.bias().unwrap(), Some(Bias::Disabled)); + } + + #[test] + fn drive() { + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.drive().unwrap(), Drive::PushPull); + + let request = config.request(); + + // Reconfigure + let lconfig = line::Config::new().unwrap(); + let mut lsettings = line::Settings::new().unwrap(); + lsettings + .set_prop(&[ + SettingVal::Direction(Direction::Input), + SettingVal::Drive(Drive::PushPull), + ]) + .unwrap(); + lconfig.add_line_settings(&[0], lsettings).unwrap(); + request.reconfigure_lines(&lconfig).unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.drive().unwrap(), Drive::PushPull); + + let lconfig = line::Config::new().unwrap(); + let mut lsettings = line::Settings::new().unwrap(); + lsettings + .set_prop(&[ + SettingVal::Direction(Direction::Output), + SettingVal::Drive(Drive::OpenDrain), + ]) + .unwrap(); + lconfig.add_line_settings(&[0], lsettings).unwrap(); + request.reconfigure_lines(&lconfig).unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.drive().unwrap(), Drive::OpenDrain); + + let lconfig = line::Config::new().unwrap(); + let mut lsettings = line::Settings::new().unwrap(); + lsettings + .set_prop(&[ + SettingVal::Direction(Direction::Output), + SettingVal::Drive(Drive::OpenSource), + ]) + .unwrap(); + lconfig.add_line_settings(&[0], lsettings).unwrap(); + request.reconfigure_lines(&lconfig).unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.drive().unwrap(), Drive::OpenSource); + } + + #[test] + fn edge() { + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.edge_detection().unwrap(), None); + + let request = config.request(); + + // Reconfigure + let lconfig = line::Config::new().unwrap(); + let mut lsettings = line::Settings::new().unwrap(); + lsettings + .set_prop(&[ + SettingVal::Direction(Direction::Input), + SettingVal::EdgeDetection(Some(Edge::Both)), + ]) + .unwrap(); + lconfig.add_line_settings(&[0], lsettings).unwrap(); + request.reconfigure_lines(&lconfig).unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.edge_detection().unwrap(), Some(Edge::Both)); + + let lconfig = line::Config::new().unwrap(); + let mut lsettings = line::Settings::new().unwrap(); + lsettings + .set_prop(&[ + SettingVal::Direction(Direction::Input), + SettingVal::EdgeDetection(Some(Edge::Rising)), + ]) + .unwrap(); + lconfig.add_line_settings(&[0], lsettings).unwrap(); + request.reconfigure_lines(&lconfig).unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.edge_detection().unwrap(), Some(Edge::Rising)); + + let lconfig = line::Config::new().unwrap(); + let mut lsettings = line::Settings::new().unwrap(); + lsettings + .set_prop(&[ + SettingVal::Direction(Direction::Input), + SettingVal::EdgeDetection(Some(Edge::Falling)), + ]) + .unwrap(); + lconfig.add_line_settings(&[0], lsettings).unwrap(); + request.reconfigure_lines(&lconfig).unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.edge_detection().unwrap(), Some(Edge::Falling)); + } + + #[test] + fn event_clock() { + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.event_clock().unwrap(), EventClock::Monotonic); + + let request = config.request(); + + // Reconfigure + let lconfig = line::Config::new().unwrap(); + let mut lsettings = line::Settings::new().unwrap(); + lsettings + .set_prop(&[SettingVal::EventClock(EventClock::Monotonic)]) + .unwrap(); + lconfig.add_line_settings(&[0], lsettings).unwrap(); + request.reconfigure_lines(&lconfig).unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.event_clock().unwrap(), EventClock::Monotonic); + + let lconfig = line::Config::new().unwrap(); + let mut lsettings = line::Settings::new().unwrap(); + lsettings + .set_prop(&[SettingVal::EventClock(EventClock::Realtime)]) + .unwrap(); + lconfig.add_line_settings(&[0], lsettings).unwrap(); + request.reconfigure_lines(&lconfig).unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.event_clock().unwrap(), EventClock::Realtime); + + let lconfig = line::Config::new().unwrap(); + let mut lsettings = line::Settings::new().unwrap(); + lsettings + .set_prop(&[SettingVal::EventClock(EventClock::HTE)]) + .unwrap(); + lconfig.add_line_settings(&[0], lsettings).unwrap(); + request.reconfigure_lines(&lconfig).unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert_eq!(info.event_clock().unwrap(), EventClock::HTE); + } + + #[test] + fn debounce() { + let mut config = TestConfig::new(NGPIO).unwrap(); + config.lconfig_add_settings(&[0]); + config.request_lines().unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert!(!info.is_debounced()); + assert_eq!(info.debounce_period(), Duration::from_millis(0)); + + let request = config.request(); + + // Reconfigure + let lconfig = line::Config::new().unwrap(); + let mut lsettings = line::Settings::new().unwrap(); + lsettings + .set_prop(&[ + SettingVal::Direction(Direction::Input), + SettingVal::DebouncePeriod(Duration::from_millis(100)), + ]) + .unwrap(); + lconfig.add_line_settings(&[0], lsettings).unwrap(); + request.reconfigure_lines(&lconfig).unwrap(); + let info = config.chip().line_info(0).unwrap(); + assert!(info.is_debounced()); + assert_eq!(info.debounce_period(), Duration::from_millis(100)); + } + } +} diff --git a/bindings/rust/libgpiod/tests/line_settings.rs b/bindings/rust/libgpiod/tests/line_settings.rs new file mode 100644 index 000000000000..3f84bf328ab7 --- /dev/null +++ b/bindings/rust/libgpiod/tests/line_settings.rs @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +mod common; + +mod line_settings { + use std::time::Duration; + + use libgpiod::{ + line, Bias, Direction, Drive, Edge, EventClock, SettingKind, SettingVal, Value, + }; + + #[test] + fn direction() { + let mut lsettings = line::Settings::new().unwrap(); + assert_eq!( + lsettings.prop(SettingKind::Direction).unwrap(), + SettingVal::Direction(Direction::AsIs) + ); + + lsettings + .set_prop(&[SettingVal::Direction(Direction::Input)]) + .unwrap(); + assert_eq!( + lsettings.prop(SettingKind::Direction).unwrap(), + SettingVal::Direction(Direction::Input) + ); + + lsettings + .set_prop(&[SettingVal::Direction(Direction::Output)]) + .unwrap(); + assert_eq!( + lsettings.prop(SettingKind::Direction).unwrap(), + SettingVal::Direction(Direction::Output) + ); + } + + #[test] + fn edge_detection() { + let mut lsettings = line::Settings::new().unwrap(); + assert_eq!( + lsettings.prop(SettingKind::EdgeDetection).unwrap(), + SettingVal::EdgeDetection(None) + ); + + lsettings + .set_prop(&[SettingVal::EdgeDetection(Some(Edge::Both))]) + .unwrap(); + assert_eq!( + lsettings.prop(SettingKind::EdgeDetection).unwrap(), + SettingVal::EdgeDetection(Some(Edge::Both)) + ); + + lsettings + .set_prop(&[SettingVal::EdgeDetection(Some(Edge::Rising))]) + .unwrap(); + assert_eq!( + lsettings.prop(SettingKind::EdgeDetection).unwrap(), + SettingVal::EdgeDetection(Some(Edge::Rising)) + ); + + lsettings + .set_prop(&[SettingVal::EdgeDetection(Some(Edge::Falling))]) + .unwrap(); + assert_eq!( + lsettings.prop(SettingKind::EdgeDetection).unwrap(), + SettingVal::EdgeDetection(Some(Edge::Falling)) + ); + } + + #[test] + fn bias() { + let mut lsettings = line::Settings::new().unwrap(); + assert_eq!( + lsettings.prop(SettingKind::Bias).unwrap(), + SettingVal::Bias(None) + ); + + lsettings + .set_prop(&[SettingVal::Bias(Some(Bias::PullDown))]) + .unwrap(); + assert_eq!( + lsettings.prop(SettingKind::Bias).unwrap(), + SettingVal::Bias(Some(Bias::PullDown)) + ); + + lsettings + .set_prop(&[SettingVal::Bias(Some(Bias::PullUp))]) + .unwrap(); + assert_eq!( + lsettings.prop(SettingKind::Bias).unwrap(), + SettingVal::Bias(Some(Bias::PullUp)) + ); + } + + #[test] + fn drive() { + let mut lsettings = line::Settings::new().unwrap(); + assert_eq!( + lsettings.prop(SettingKind::Drive).unwrap(), + SettingVal::Drive(Drive::PushPull) + ); + + lsettings + .set_prop(&[SettingVal::Drive(Drive::PushPull)]) + .unwrap(); + assert_eq!( + lsettings.prop(SettingKind::Drive).unwrap(), + SettingVal::Drive(Drive::PushPull) + ); + + lsettings + .set_prop(&[SettingVal::Drive(Drive::OpenDrain)]) + .unwrap(); + assert_eq!( + lsettings.prop(SettingKind::Drive).unwrap(), + SettingVal::Drive(Drive::OpenDrain) + ); + + lsettings + .set_prop(&[SettingVal::Drive(Drive::OpenSource)]) + .unwrap(); + assert_eq!( + lsettings.prop(SettingKind::Drive).unwrap(), + SettingVal::Drive(Drive::OpenSource) + ); + } + + #[test] + fn active_low() { + let mut lsettings = line::Settings::new().unwrap(); + assert_eq!( + lsettings.prop(SettingKind::ActiveLow).unwrap(), + SettingVal::ActiveLow(false) + ); + + lsettings.set_prop(&[SettingVal::ActiveLow(true)]).unwrap(); + assert_eq!( + lsettings.prop(SettingKind::ActiveLow).unwrap(), + SettingVal::ActiveLow(true) + ); + + lsettings.set_prop(&[SettingVal::ActiveLow(false)]).unwrap(); + assert_eq!( + lsettings.prop(SettingKind::ActiveLow).unwrap(), + SettingVal::ActiveLow(false) + ); + } + + #[test] + fn debounce_period() { + let mut lsettings = line::Settings::new().unwrap(); + assert_eq!( + lsettings.prop(SettingKind::DebouncePeriod).unwrap(), + SettingVal::DebouncePeriod(Duration::from_millis(0)) + ); + + lsettings + .set_prop(&[SettingVal::DebouncePeriod(Duration::from_millis(5))]) + .unwrap(); + assert_eq!( + lsettings.prop(SettingKind::DebouncePeriod).unwrap(), + SettingVal::DebouncePeriod(Duration::from_millis(5)) + ); + } + + #[test] + fn event_clock() { + let mut lsettings = line::Settings::new().unwrap(); + assert_eq!( + lsettings.prop(SettingKind::EventClock).unwrap(), + SettingVal::EventClock(EventClock::Monotonic) + ); + + lsettings + .set_prop(&[SettingVal::EventClock(EventClock::Realtime)]) + .unwrap(); + assert_eq!( + lsettings.prop(SettingKind::EventClock).unwrap(), + SettingVal::EventClock(EventClock::Realtime) + ); + + lsettings + .set_prop(&[SettingVal::EventClock(EventClock::HTE)]) + .unwrap(); + assert_eq!( + lsettings.prop(SettingKind::EventClock).unwrap(), + SettingVal::EventClock(EventClock::HTE) + ); + + lsettings + .set_prop(&[SettingVal::EventClock(EventClock::Monotonic)]) + .unwrap(); + assert_eq!( + lsettings.prop(SettingKind::EventClock).unwrap(), + SettingVal::EventClock(EventClock::Monotonic) + ); + } + + #[test] + fn output_value() { + let mut lsettings = line::Settings::new().unwrap(); + assert_eq!( + lsettings.prop(SettingKind::OutputValue).unwrap(), + SettingVal::OutputValue(Value::InActive) + ); + + lsettings + .set_prop(&[SettingVal::OutputValue(Value::Active)]) + .unwrap(); + assert_eq!( + lsettings.prop(SettingKind::OutputValue).unwrap(), + SettingVal::OutputValue(Value::Active) + ); + + lsettings + .set_prop(&[SettingVal::OutputValue(Value::InActive)]) + .unwrap(); + assert_eq!( + lsettings.prop(SettingKind::OutputValue).unwrap(), + SettingVal::OutputValue(Value::InActive) + ); + } +} diff --git a/bindings/rust/libgpiod/tests/request_config.rs b/bindings/rust/libgpiod/tests/request_config.rs new file mode 100644 index 000000000000..d9892ee99101 --- /dev/null +++ b/bindings/rust/libgpiod/tests/request_config.rs @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +mod common; + +mod request_config { + use vmm_sys_util::errno::Error as Errno; + + use libgpiod::{request, Error as ChipError, OperationType}; + + mod verify { + use super::*; + + #[test] + fn default() { + let rconfig = request::Config::new().unwrap(); + + assert_eq!(rconfig.event_buffer_size(), 0); + assert_eq!( + rconfig.consumer().unwrap_err(), + ChipError::OperationFailed(OperationType::RequestConfigGetConsumer, Errno::new(0)) + ); + } + + #[test] + fn initialized() { + const CONSUMER: &str = "foobar"; + let rconfig = request::Config::new().unwrap(); + rconfig.set_consumer(CONSUMER); + rconfig.set_event_buffer_size(64); + + assert_eq!(rconfig.event_buffer_size(), 64); + assert_eq!(rconfig.consumer().unwrap(), CONSUMER); + } + } +} From patchwork Fri Oct 14 10:47:25 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 615141 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 63587C433FE for ; Fri, 14 Oct 2022 10:47:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229661AbiJNKr5 (ORCPT ); Fri, 14 Oct 2022 06:47:57 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47640 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229671AbiJNKr4 (ORCPT ); Fri, 14 Oct 2022 06:47:56 -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 DF64426F6 for ; Fri, 14 Oct 2022 03:47:55 -0700 (PDT) Received: by mail-pj1-x1031.google.com with SMTP id t12-20020a17090a3b4c00b0020b04251529so4425035pjf.5 for ; Fri, 14 Oct 2022 03:47:55 -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=5qGaiEFLaBnpGGmWZydBFvA7FCLNISeMhhUxsl1cBpk=; b=fFPo1MtW+NWvL31KHsHgwoGmezzRdq7gtfXgjuKWQvPgo9LOebCptEZn8Dke7oxkTK WLZOSTqn0zqp2pa3ih+99CODM70lfNwTVsnxmTuCwyheuRi9CCPAKJCEor7zLayJ17H9 s636wK6Is8vwZZD0ux19HPUd86Ob69P6rp5zsmqqMchpfh/bA8/+Nhw/aRU06MMY0rv5 GK9mkj+5cePo7c+7q3Cpth9rzKKGU/KO0Joqs0Ur0xAeE1WjYdhV1lMPd++8o9xr52l4 URGKate2pHYdg/mAUDWdWMw7+k0Z8Ml1PZ79quID/b3BMqTaHDd/v6F5RrPaXw768Lq5 kmzg== 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=5qGaiEFLaBnpGGmWZydBFvA7FCLNISeMhhUxsl1cBpk=; b=lpQW4cEJfz64vJNtZDL5oafekFgOpiqMeqpeUYZEGSoImAHojlUVlHIv3/BLuPAcDN AIKqmqg9/WBbIr3Xjf0KwWfEe7AN5spCRru1yUgwA3pSxyjQIVNgBakrz1BAtnwGqLaQ 0UItBvxpZU2CmNlnKlka0iCKSZd91+C2mCIJd2usKNwqVBGVLEmzr83ldDkeUrUBJAtU H/y6k6lW9D8fGDTEB+a39wxyX4hAYk5Jzqmxhg3kPMpHv4HRBxgbexDqgIz/sG/kHts5 syOplN/cSeukrb82AOX5HQCoze0g9GtHMotiSr9vzm3GpD7wC00wFkpZTCk3g/UXxHyO LVOQ== X-Gm-Message-State: ACrzQf213m+yABt4TlJ0yTzSJmeZk4qJT3CwEIUTDI9QfEnAcQE6iF2d SGihadeLQlOtLZ3IKI7/SM2zqw== X-Google-Smtp-Source: AMsMyM6aWKh0U8+bGw9RjAiwl0uqJ1Ve32B6sajjOKAAPX2ldrHx9JkfTvSR9cerE+A9X/CoC0OZ/Q== X-Received: by 2002:a17:90a:2bcb:b0:20a:ea55:32ef with SMTP id n11-20020a17090a2bcb00b0020aea5532efmr16618167pje.59.1665744475613; Fri, 14 Oct 2022 03:47:55 -0700 (PDT) Received: from localhost ([122.172.86.128]) by smtp.gmail.com with ESMTPSA id o125-20020a62cd83000000b00561c179e17dsm1375786pfg.76.2022.10.14.03.47.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 14 Oct 2022 03:47:54 -0700 (PDT) From: Viresh Kumar To: Linus Walleij , Bartosz Golaszewski Cc: Viresh Kumar , Vincent Guittot , linux-gpio@vger.kernel.org, Kent Gibson , Miguel Ojeda , Wedson Almeida Filho , =?utf-8?q?Alex_Benn=C3=A9e?= , stratos-dev@op-lists.linaro.org, Gerard Ryan Subject: [PATCH V7 8/8] libgpiod: Integrate building of rust bindings with make Date: Fri, 14 Oct 2022 16:17:25 +0530 Message-Id: <3e7fb99173856a3995360fc3fad51220c4b4e90e.1665744170.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 6ac1d8ed8a40..37cfdb3644f7 100644 --- a/configure.ac +++ b/configure.ac @@ -212,6 +212,21 @@ then [AC_SUBST(PYTHON_LIBS, [`$PYTHON-config --libs`])]) fi +AC_ARG_ENABLE([bindings-rust], + [AS_HELP_STRING([--enable-bindings-rust],[enable rust bindings [default=no]])], + [if test "x$enableval" = xyes; then with_bindings_rust=true; fi], + [with_bindings_rust=false]) +AM_CONDITIONAL([WITH_BINDINGS_RUST], [test "x$with_bindings_rust" = xtrue]) + +if test "x$with_bindings_rust" = xtrue +then + AC_CHECK_PROG([has_cargo], [cargo], [true], [false]) + if test "x$has_cargo" = xfalse + then + AC_MSG_ERROR([cargo not found - needed for rust bindings]) + fi +fi + AC_CHECK_PROG([has_doxygen], [doxygen], [true], [false]) AM_CONDITIONAL([HAS_DOXYGEN], [test "x$has_doxygen" = xtrue]) if test "x$has_doxygen" = xfalse @@ -246,6 +261,7 @@ AC_CONFIG_FILES([Makefile bindings/python/Makefile bindings/python/examples/Makefile bindings/python/tests/Makefile + bindings/rust/Makefile man/Makefile]) AC_OUTPUT