From patchwork Mon Dec 20 11:11:35 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 526182 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 E15CBC433EF for ; Mon, 20 Dec 2021 11:17:11 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231603AbhLTLRL (ORCPT ); Mon, 20 Dec 2021 06:17:11 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40946 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231171AbhLTLRL (ORCPT ); Mon, 20 Dec 2021 06:17:11 -0500 Received: from mail-pj1-x1036.google.com (mail-pj1-x1036.google.com [IPv6:2607:f8b0:4864:20::1036]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id CAAACC061574 for ; Mon, 20 Dec 2021 03:17:10 -0800 (PST) Received: by mail-pj1-x1036.google.com with SMTP id a11-20020a17090a854b00b001b11aae38d6so9829522pjw.2 for ; Mon, 20 Dec 2021 03:17:10 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=/rub05lOF1YPvV9qK3aV1FfJQJ3anYu0aofP9QN1U8Y=; b=X71YdgqWYtMJF6sa8WFamNrc4QNUJoy54BX9z23HJIdT3AqVS4EH2QpN2rjV2oc6T4 aRLPcNk0PMLDllNCpA8DjigtjR6K5S7x2KVQzgdCcs7qoHLq01XNzL2ZJMJMZGrKswNq XAnn9+UXvNtD16CJOrzYVx6YyM/CjPK8AGJz3oKU6M1x7hFyYfrFEPqpadFeRytmKin2 X4BXyVzsf/QHaC5aWlDuEjrDrohtqtoZu6PivzR/JyiX1LAsMVp3kgLlNWopLgw1QDJl uTMLn0c7pTUeitbdIFGdFNRpcaLr59QqSwHeT4/8uZTb4XQDa0AWKMC1joq9V3N9r5bV ii0Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=/rub05lOF1YPvV9qK3aV1FfJQJ3anYu0aofP9QN1U8Y=; b=QjIe8i198XFBHvRtoXTMF6gjDhStHjR1RPLcfbPx1UJ09zJQL8FzD/nN/iWqjggPx0 VCaqa/PHLydWMYtSb7wKqEI2uA/ryX40pSh2B8ceBhSEcI/On700q2oBTSKBznU2yYHV 3jmqzOBNY79TzfkcpbrM+4TOZvA2EYIlkDl1R/kTLuc/zdx3L7+1H0KlbBto8X1MvLHM +uGnJaSE3DFmelRlbS/Z37umd2aWt2sYDu/8auVWPTGlohLNlPjeb+yKsybTooP6JRad 5v4QRHodb5R5wNVVCRpv/a6uAcECB1LySwg9wPh3IwyqJ5cawIID1uA3uF1xaw/3jQkL 2AfQ== X-Gm-Message-State: AOAM531YP74ZKDRfaBVKsQz5B27J8/QaoYMdkmuAblNY0n/1+dj5pJ17 ypj0Ual1gciq5MN1A3EpRI2TrA== X-Google-Smtp-Source: ABdhPJxEADIBp07L0R+ruwelHH6TDj6Xg6PJ/AvX/L0OocVRDXjbFtRzzWNjqxLLlQcYPT27nCrlcA== X-Received: by 2002:a17:90a:578b:: with SMTP id g11mr10511282pji.76.1639999030334; Mon, 20 Dec 2021 03:17:10 -0800 (PST) Received: from localhost ([106.201.42.111]) by smtp.gmail.com with ESMTPSA id m67sm10970100pfb.36.2021.12.20.03.17.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 20 Dec 2021 03:17:09 -0800 (PST) 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 V3 1/4] libgpiod: Add libgpiod-sys rust crate Date: Mon, 20 Dec 2021 16:41:35 +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/libgpiod-sys/Cargo.toml | 12 ++++++ bindings/rust/libgpiod-sys/build.rs | 60 +++++++++++++++++++++++++++ bindings/rust/libgpiod-sys/src/lib.rs | 16 +++++++ bindings/rust/libgpiod-sys/wrapper.h | 2 + 5 files changed, 95 insertions(+) create mode 100644 bindings/rust/libgpiod-sys/Cargo.toml create mode 100644 bindings/rust/libgpiod-sys/build.rs create mode 100644 bindings/rust/libgpiod-sys/src/lib.rs create mode 100644 bindings/rust/libgpiod-sys/wrapper.h diff --git a/.gitignore b/.gitignore index 2d7cc7fc0758..3a80a65c087e 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,8 @@ libtool *-libtool m4/ stamp-h1 + +# Added by cargo + +target +Cargo.lock diff --git a/bindings/rust/libgpiod-sys/Cargo.toml b/bindings/rust/libgpiod-sys/Cargo.toml new file mode 100644 index 000000000000..a9c7b01365b2 --- /dev/null +++ b/bindings/rust/libgpiod-sys/Cargo.toml @@ -0,0 +1,12 @@ +[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] + +[build-dependencies] +bindgen = "0.59.1" +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..6e84378781ef --- /dev/null +++ b/bindings/rust/libgpiod-sys/build.rs @@ -0,0 +1,60 @@ +extern crate bindgen; + +use std::env; +use std::path::PathBuf; + +fn generate_bindings() { + // Tell cargo to invalidate the built crate whenever the wrapper changes + println!("cargo:rerun-if-changed=wrapper.h"); + + // The bindgen::Builder is the main entry point + // to bindgen, and lets you build up options for + // the resulting bindings. + let bindings = bindgen::Builder::default() + // The input header we would like to generate + // bindings for. + .header("wrapper.h") + // Tell cargo to invalidate the built crate whenever any of the + // included header files changed. + .parse_callbacks(Box::new(bindgen::CargoCallbacks)) + // Finish the builder and generate the bindings. + .generate() + // Unwrap the Result and panic on failure. + .expect("Unable to generate bindings"); + + // Write the bindings to the $OUT_DIR/bindings.rs file. + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Couldn't write bindings!"); +} + +fn build_gpiod() { + // Tell Cargo that if the given file changes, to rerun this build script. + println!("cargo:rerun-if-changed=../../../lib/"); + + let files = vec![ + "../../../lib/chip.c", + "../../../lib/edge-event.c", + "../../../lib/info-event.c", + "../../../lib/internal.c", + "../../../lib/line-config.c", + "../../../lib/line-info.c", + "../../../lib/line-request.c", + "../../../lib/misc.c", + "../../../lib/request-config.c", + ]; + + // Use the `cc` crate to build a C file and statically link it. + cc::Build::new() + .files(files) + .define("_GNU_SOURCE", None) + .define("GPIOD_VERSION_STR", "\"libgpio-sys\"") + .include("../../../include") + .compile("gpiod"); +} + +fn main() { + generate_bindings(); + build_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..7d6caa7d9c11 --- /dev/null +++ b/bindings/rust/libgpiod-sys/src/lib.rs @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0 + +#[allow( + clippy::all, + deref_nullptr, + dead_code, + non_camel_case_types, + non_upper_case_globals, + non_snake_case, + improper_ctypes +)] + +mod bindings_raw { + include!(concat!(env!("OUT_DIR"), "/bindings.rs")); +} +pub use bindings_raw::*; diff --git a/bindings/rust/libgpiod-sys/wrapper.h b/bindings/rust/libgpiod-sys/wrapper.h new file mode 100644 index 000000000000..7bc1158b7d90 --- /dev/null +++ b/bindings/rust/libgpiod-sys/wrapper.h @@ -0,0 +1,2 @@ +#include +#include "../../../include/gpiod.h" From patchwork Mon Dec 20 11:11:36 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 526181 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 E3E4BC433EF for ; Mon, 20 Dec 2021 11:17:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231588AbhLTLRP (ORCPT ); Mon, 20 Dec 2021 06:17:15 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40966 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231171AbhLTLRP (ORCPT ); Mon, 20 Dec 2021 06:17:15 -0500 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 D876BC061574 for ; Mon, 20 Dec 2021 03:17:14 -0800 (PST) Received: by mail-pj1-x102c.google.com with SMTP id o63-20020a17090a0a4500b001b1c2db8145so1684306pjo.5 for ; Mon, 20 Dec 2021 03:17:14 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=fijvLHazgyTfZOFwjW6pm1IiLIgkmRlPU9Rz3Rm3MJc=; b=u0JSh7qZEbVd7TTI9GlfKKahvg5OJkovmQ3Jn0fTsX8wVOpLOcyYmqF5xZipiYwnmU DnzPENVHoQyq8BDIV0/4wxm6HydYRhZifRSBFueSyF6JrF9pyYYA+KubbSIrbTOCik4e JFF9fCJwPkfm8XRdf1YD1kNhEvUILOHzK/+iIyaNo1d6jO60OrQ7JGvUWuB1HASZSSJZ 1YDRN7WoEy8JYIPYoX2S9mX1HiA8BF+KN3AKTrmF0ak6Tr2SZLjJ2rsHKiEc8qKkC1zn 3EtbCk+E1A/KNlXkCX1svbFT4gNVVKNlwW4DQf1UtGU3D/j8NCzb2b0+Ib1jgZRCY7k4 4lxw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=fijvLHazgyTfZOFwjW6pm1IiLIgkmRlPU9Rz3Rm3MJc=; b=zAyk2H8DC6s8YfZK8qPlIN5J93P+mgG4wygHz2+VJIg4E6QbWwC+BK+oClijM/nD1/ yRu2b7apUB0t56riJW8iimEDDn6nHMJ0RRQ28GVtpJ6w/eTatJSJy7LR1ojRWOmhwJ+x K1ZSMukfKHkMF9yssgRkCPclywPH+GOA6qu9URm8X0oyq68k33xj0sNBW6t+4y0JaKTz TS9ZwSgtn24WSnRWG80wfU1aXZYg5FeoE7iboI9CyuTPo3H++cuHtgNdHQCmRm1M2gyq Sl8rCmBUI2GYWONvXXwOj+N/5NZW8JIG4PyCEGjvOkS+eNFYZc27ztZxFffGJwQvG1Dn bJkA== X-Gm-Message-State: AOAM533Liho2xjQDmukhB+zN3/OxS7RCWuwf3OtZcYDm0QfyLsfTrYSU 9nYgMlu1Gv0tEGaHFG+G+mXWsw== X-Google-Smtp-Source: ABdhPJzB1q7A8thfRlBvXKmYJSAGuJXus+OYPs8zbxf658rQGSYSgIUjliIKuWCWVfMA4ab8WsOW+w== X-Received: by 2002:a17:90a:7802:: with SMTP id w2mr22970989pjk.148.1639999033012; Mon, 20 Dec 2021 03:17:13 -0800 (PST) Received: from localhost ([106.201.42.111]) by smtp.gmail.com with ESMTPSA id g6sm2027835pfj.156.2021.12.20.03.17.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 20 Dec 2021 03:17:12 -0800 (PST) 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 V3 2/4] libgpiod: Add rust wrapper crate Date: Mon, 20 Dec 2021 16:41:36 +0530 Message-Id: X-Mailer: git-send-email 2.31.1.272.g89b43f80a514 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org Add rust wrapper crate, around the libpiod-sys crate added earlier, to provide a convenient interface for the users. Signed-off-by: Viresh Kumar --- bindings/rust/Cargo.toml | 11 + bindings/rust/src/chip.rs | 183 +++++++++++++ bindings/rust/src/edge_event.rs | 103 ++++++++ bindings/rust/src/event_buffer.rs | 87 +++++++ bindings/rust/src/info_event.rs | 68 +++++ bindings/rust/src/lib.rs | 280 ++++++++++++++++++++ bindings/rust/src/line_config.rs | 391 ++++++++++++++++++++++++++++ bindings/rust/src/line_info.rs | 182 +++++++++++++ bindings/rust/src/line_request.rs | 214 +++++++++++++++ bindings/rust/src/request_config.rs | 119 +++++++++ 10 files changed, 1638 insertions(+) create mode 100644 bindings/rust/Cargo.toml create mode 100644 bindings/rust/src/chip.rs create mode 100644 bindings/rust/src/edge_event.rs create mode 100644 bindings/rust/src/event_buffer.rs create mode 100644 bindings/rust/src/info_event.rs create mode 100644 bindings/rust/src/lib.rs create mode 100644 bindings/rust/src/line_config.rs create mode 100644 bindings/rust/src/line_info.rs create mode 100644 bindings/rust/src/line_request.rs create mode 100644 bindings/rust/src/request_config.rs diff --git a/bindings/rust/Cargo.toml b/bindings/rust/Cargo.toml new file mode 100644 index 000000000000..948037258b82 --- /dev/null +++ b/bindings/rust/Cargo.toml @@ -0,0 +1,11 @@ +[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] +libgpiod-sys = { path = "libgpiod-sys" } +thiserror = "1.0" +vmm-sys-util = "=0.9.0" diff --git a/bindings/rust/src/chip.rs b/bindings/rust/src/chip.rs new file mode 100644 index 000000000000..7ed0a3812569 --- /dev/null +++ b/bindings/rust/src/chip.rs @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2021 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use std::os::raw::c_char; +use std::sync::Arc; +use std::time::Duration; +use std::{slice, str}; + +use vmm_sys_util::errno::Error as IoError; + +use super::{bindings, Error, InfoEvent, LineConfig, LineInfo, LineRequest, RequestConfig, Result}; + +/// GPIO chip +/// +/// A GPIO chip object is associated with an open file descriptor to the GPIO +/// character device. It exposes basic information about the chip and allows +/// callers to retrieve information about each line, watch lines for state +/// changes and make line requests. +pub(crate) struct ChipInternal { + chip: *mut bindings::gpiod_chip, +} + +impl ChipInternal { + /// Find a GPIO chip by path. + pub(crate) fn open(path: &str) -> Result { + // Null-terminate the string + let path = path.to_owned() + "\0"; + + let chip = unsafe { bindings::gpiod_chip_open(path.as_ptr() as *const c_char) }; + if chip.is_null() { + return Err(Error::OperationFailed("Gpio Chip open", IoError::last())); + } + + Ok(Self { chip }) + } + + /// Private helper, Returns gpiod_chip + pub(crate) fn chip(&self) -> *mut bindings::gpiod_chip { + self.chip + } +} + +impl Drop for ChipInternal { + /// Close the GPIO chip and release all associated resources. + fn drop(&mut self) { + unsafe { bindings::gpiod_chip_close(self.chip) } + } +} + +pub struct Chip { + ichip: Arc, +} + +impl Chip { + /// Find a GPIO chip by path. + pub fn open(path: &str) -> Result { + Ok(Self { + ichip: Arc::new(ChipInternal::open(path)?), + }) + } + + /// Get the GPIO chip name as represented in the kernel. + pub fn get_name(&self) -> Result<&str> { + // SAFETY: The string returned by libgpiod is guaranteed to live as long + // as the `struct Chip`. + let name = unsafe { bindings::gpiod_chip_get_name(self.ichip.chip()) }; + + // SAFETY: The string is guaranteed to be valid here. + str::from_utf8(unsafe { + slice::from_raw_parts(name as *const u8, bindings::strlen(name) as usize) + }) + .map_err(Error::InvalidString) + } + + /// Get the GPIO chip label as represented in the kernel. + pub fn get_label(&self) -> Result<&str> { + // SAFETY: The string returned by libgpiod is guaranteed to live as long + // as the `struct Chip`. + let label = unsafe { bindings::gpiod_chip_get_label(self.ichip.chip()) }; + + // SAFETY: The string is guaranteed to be valid here. + str::from_utf8(unsafe { + slice::from_raw_parts(label as *const u8, bindings::strlen(label) as usize) + }) + .map_err(Error::InvalidString) + } + + /// Get the path used to find this GPIO chip. + pub fn get_path(&self) -> Result<&str> { + // SAFETY: The string returned by libgpiod is guaranteed to live as long + // as the `struct Chip`. + let path = unsafe { bindings::gpiod_chip_get_path(self.ichip.chip()) }; + + // SAFETY: The string is guaranteed to be valid here. + str::from_utf8(unsafe { + slice::from_raw_parts(path as *const u8, bindings::strlen(path) as usize) + }) + .map_err(Error::InvalidString) + } + + /// Get the number of GPIO lines exposed by this chip. + pub fn get_num_lines(&self) -> u32 { + unsafe { bindings::gpiod_chip_get_num_lines(self.ichip.chip()) } + } + + /// Get the current snapshot of information about the line at given offset. + pub fn line_info(&self, offset: u32) -> Result { + LineInfo::new(self.ichip.clone(), offset, false) + } + + /// Get the current snapshot of information about the line at given offset + /// and optionally start watching it for future changes. + pub fn watch_line_info(&self, offset: u32) -> Result { + LineInfo::new(self.ichip.clone(), offset, true) + } + + /// Get the file descriptor associated with this chip. + /// + /// The returned file descriptor must not be closed by the caller, else other methods for the + /// `struct Chip` may fail. + pub fn get_fd(&self) -> Result { + let fd = unsafe { bindings::gpiod_chip_get_fd(self.ichip.chip()) }; + + if fd < 0 { + Err(Error::OperationFailed("Gpio Chip get-fd", IoError::last())) + } else { + Ok(fd as u32) + } + } + + /// Wait for line status events on any of the watched lines exposed by this + /// chip. + pub fn info_event_wait(&self, timeout: Duration) -> Result<()> { + let ret = unsafe { + bindings::gpiod_chip_info_event_wait(self.ichip.chip(), timeout.as_nanos() as u64) + }; + + match ret { + -1 => Err(Error::OperationFailed( + "Gpio Chip info-event-wait", + IoError::last(), + )), + 0 => Err(Error::OperationTimedOut), + _ => Ok(()), + } + } + + /// Read a single line status change event from this chip. If no events are + /// pending, this function will block. + pub fn info_event_read(&self) -> Result { + InfoEvent::new(&self.ichip) + } + + /// Map a GPIO line's name to its offset within the chip. + pub fn find_line(&self, name: &str) -> Result { + // Null-terminate the string + let name = name.to_owned() + "\0"; + + let ret = unsafe { + bindings::gpiod_chip_find_line(self.ichip.chip(), name.as_ptr() as *const c_char) + }; + + if ret == -1 { + Err(Error::OperationFailed( + "Gpio Chip find-line", + IoError::last(), + )) + } else { + Ok(ret as u32) + } + } + + /// Request a set of lines for exclusive usage. + pub fn request_lines( + &self, + rconfig: &RequestConfig, + lconfig: &LineConfig, + ) -> Result { + LineRequest::new(&self.ichip, rconfig, lconfig) + } +} diff --git a/bindings/rust/src/edge_event.rs b/bindings/rust/src/edge_event.rs new file mode 100644 index 000000000000..2f791b0f883d --- /dev/null +++ b/bindings/rust/src/edge_event.rs @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2021 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use std::sync::Arc; +use std::time::Duration; + +use vmm_sys_util::errno::Error as IoError; + +use super::{bindings, EdgeEventBufferInternal, Error, LineEdgeEvent, Result}; + +/// Line edge events handling +/// +/// An edge event object contains information about a single line event. It +/// contains the event type, timestamp and the offset of the line on which the +/// event occurred as well as two sequential numbers (global for all lines +/// associated with the parent chip and local for this line only). +/// +/// For performance and to limit the number of memory allocations when a lot of +/// events are being read, edge events are stored in an edge-event buffer object. + +pub struct EdgeEvent { + ibuffer: Option>, + event: *mut bindings::gpiod_edge_event, +} + +impl EdgeEvent { + /// Get an event stored in the buffer. + pub(crate) fn new( + ibuffer: &Arc, + index: u64, + copy: bool, + ) -> Result { + let event = unsafe { bindings::gpiod_edge_event_buffer_get_event(ibuffer.buffer(), index) }; + if event.is_null() { + return Err(Error::OperationFailed( + "Gpio EdgeEvent buffer-get-event", + IoError::last(), + )); + } + + if copy { + let event = unsafe { bindings::gpiod_edge_event_copy(event) }; + if event.is_null() { + return Err(Error::OperationFailed( + "Gpio EdgeEvent copy", + IoError::last(), + )); + } + + Ok(Self { + ibuffer: None, + event, + }) + } else { + Ok(Self { + ibuffer: Some(ibuffer.clone()), + event, + }) + } + } + + /// Get the event type. + pub fn get_event_type(&self) -> Result { + LineEdgeEvent::new(unsafe { bindings::gpiod_edge_event_get_event_type(self.event) } as u32) + } + + /// Get the timestamp of the event. + pub fn get_timestamp(&self) -> Duration { + Duration::from_nanos(unsafe { bindings::gpiod_edge_event_get_timestamp(self.event) }) + } + + /// Get the offset of the line on which the event was triggered. + pub fn get_line_offset(&self) -> u32 { + unsafe { bindings::gpiod_edge_event_get_line_offset(self.event) } + } + + /// Get the global sequence number of this event. + /// + /// Returns sequence number of the event relative to all lines in the + /// associated line request. + pub fn get_global_seqno(&self) -> u64 { + unsafe { bindings::gpiod_edge_event_get_global_seqno(self.event) } + } + + /// Get the event sequence number specific to concerned line. + /// + /// Returns sequence number of the event relative to this line within the + /// lifetime of the associated line request. + pub fn get_line_seqno(&self) -> u64 { + unsafe { bindings::gpiod_edge_event_get_line_seqno(self.event) } + } +} + +impl Drop for EdgeEvent { + /// Free the edge event. + fn drop(&mut self) { + if self.ibuffer.is_some() { + unsafe { bindings::gpiod_edge_event_free(self.event) }; + } + } +} diff --git a/bindings/rust/src/event_buffer.rs b/bindings/rust/src/event_buffer.rs new file mode 100644 index 000000000000..c1e9d5cac4ff --- /dev/null +++ b/bindings/rust/src/event_buffer.rs @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2021 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use std::sync::Arc; + +use vmm_sys_util::errno::Error as IoError; + +use super::{bindings, EdgeEvent, Error, Result}; + +/// Line edge events buffer +pub(crate) struct EdgeEventBufferInternal { + buffer: *mut bindings::gpiod_edge_event_buffer, +} + +impl EdgeEventBufferInternal { + /// Create a new edge event buffer. + /// + /// If capacity equals 0, it will be set to a default value of 64. If + /// capacity is larger than 1024, it will be limited to 1024. + pub fn new(capacity: u32) -> Result { + let buffer = unsafe { bindings::gpiod_edge_event_buffer_new(capacity) }; + if buffer.is_null() { + return Err(Error::OperationFailed( + "Gpio EdgeEventBuffer new", + IoError::last(), + )); + } + + Ok(Self { buffer }) + } + + /// Private helper, Returns gpiod_edge_event_buffer + pub(crate) fn buffer(&self) -> *mut bindings::gpiod_edge_event_buffer { + self.buffer + } +} + +impl Drop for EdgeEventBufferInternal { + /// Free the edge event buffer and release all associated resources. + fn drop(&mut self) { + unsafe { bindings::gpiod_edge_event_buffer_free(self.buffer) }; + } +} + +/// Line edge events buffer +pub struct EdgeEventBuffer { + ibuffer: Arc, +} + +impl EdgeEventBuffer { + /// Create a new edge event buffer. + /// + /// If capacity equals 0, it will be set to a default value of 64. If + /// capacity is larger than 1024, it will be limited to 1024. + pub fn new(capacity: u32) -> Result { + Ok(Self { + ibuffer: Arc::new(EdgeEventBufferInternal::new(capacity)?), + }) + } + + /// Private helper, Returns gpiod_edge_event_buffer + pub(crate) fn buffer(&self) -> *mut bindings::gpiod_edge_event_buffer { + self.ibuffer.buffer() + } + + /// Get the capacity of the event buffer. + pub fn get_capacity(&self) -> u32 { + unsafe { bindings::gpiod_edge_event_buffer_get_capacity(self.buffer()) } + } + + /// Read an event stored in the buffer. + pub fn get_event(&self, index: u64) -> Result { + EdgeEvent::new(&self.ibuffer, index, false) + } + + /// Make copy of an edge event stored in the buffer. + pub fn get_event_copy(&self, index: u64) -> Result { + EdgeEvent::new(&self.ibuffer, index, true) + } + + /// Get the number of events this buffers stores. + pub fn num_events(&self) -> u32 { + unsafe { bindings::gpiod_edge_event_buffer_num_events(self.buffer()) } + } +} diff --git a/bindings/rust/src/info_event.rs b/bindings/rust/src/info_event.rs new file mode 100644 index 000000000000..bce7c7e2586a --- /dev/null +++ b/bindings/rust/src/info_event.rs @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2021 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use std::convert::TryFrom; +use std::sync::Arc; +use std::time::Duration; + +use vmm_sys_util::errno::Error as IoError; + +use super::{bindings, ChipInternal, Error, Event, LineInfo, Result}; + +/// Line status watch events +/// +/// Accessors for the info event objects allowing to monitor changes in GPIO +/// line state. +/// +/// Callers can be notified about changes in line's state using the interfaces +/// exposed by GPIO chips. Each info event contains information about the event +/// itself (timestamp, type) as well as a snapshot of line's state in the form +/// of a line-info object. + +pub struct InfoEvent { + event: *mut bindings::gpiod_info_event, +} + +impl InfoEvent { + /// Get a single chip's line's status change event. + pub(crate) fn new(ichip: &Arc) -> Result { + let event = unsafe { bindings::gpiod_chip_info_event_read(ichip.chip()) }; + if event.is_null() { + return Err(Error::OperationFailed( + "Gpio InfoEvent event-read", + IoError::last(), + )); + } + + Ok(Self { event }) + } + + /// Private helper, Returns gpiod_info_event + pub(crate) fn event(&self) -> *mut bindings::gpiod_info_event { + self.event + } + + /// Get the event type of this status change event. + pub fn get_event_type(&self) -> Result { + Event::new(unsafe { bindings::gpiod_info_event_get_event_type(self.event) } as u32) + } + + /// Get the timestamp of the event. + pub fn get_timestamp(&self) -> Duration { + Duration::from_nanos(unsafe { bindings::gpiod_info_event_get_timestamp(self.event) }) + } + + /// Get the line-info object associated with this event. + pub fn line_info(&self) -> Result { + LineInfo::try_from(self) + } +} + +impl Drop for InfoEvent { + /// Free the info event object and release all associated resources. + fn drop(&mut self) { + unsafe { bindings::gpiod_info_event_free(self.event) } + } +} diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs new file mode 100644 index 000000000000..19cfad388dd3 --- /dev/null +++ b/bindings/rust/src/lib.rs @@ -0,0 +1,280 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Rust wrappers for GPIOD APIs +// +// Copyright 2021 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +//! libgpiod public API +//! +//! This is the complete documentation of the public Rust API made available to +//! users of libgpiod. +//! +//! The API is logically split into several parts such as: GPIO chip & line +//! operators, GPIO events handling etc. + +mod chip; +mod edge_event; +mod event_buffer; +mod info_event; +mod line_config; +mod line_info; +mod line_request; +mod request_config; + +use libgpiod_sys as bindings; + +pub use crate::chip::*; +pub use crate::edge_event::*; +pub use crate::event_buffer::*; +pub use crate::info_event::*; +pub use crate::line_config::*; +pub use crate::line_info::*; +pub use crate::line_request::*; +pub use crate::request_config::*; + +use std::os::raw::c_char; +use std::{slice, str}; + +use thiserror::Error as ThisError; +use vmm_sys_util::errno::Error as IoError; + +/// Result of libgpiod operations +pub type Result = std::result::Result; + +/// Error codes for libgpiod operations +#[derive(Copy, Clone, Debug, PartialEq, ThisError)] +pub enum Error { + #[error("Failed to find {0}")] + NameNotFound(&'static str), + #[error("Invalid String: {0:?}")] + InvalidString(str::Utf8Error), + #[error("Invalid {0} value: {1}")] + InvalidValue(&'static str, u32), + #[error("Operation {0} Failed: {1}")] + OperationFailed(&'static str, IoError), + #[error("Operation Timed-out")] + OperationTimedOut, +} + +/// Direction settings. +pub enum Direction { + /// Request the line(s), but didn't change current direction. + AsIs, + /// Direction is input - we're reading the state of a GPIO line. + Input, + /// Direction is output - we're driving the GPIO line. + Output, +} + +impl Direction { + fn new(dir: u32) -> Result { + match dir { + bindings::GPIOD_LINE_DIRECTION_AS_IS => Ok(Direction::AsIs), + bindings::GPIOD_LINE_DIRECTION_INPUT => Ok(Direction::Input), + bindings::GPIOD_LINE_DIRECTION_OUTPUT => Ok(Direction::Output), + _ => Err(Error::InvalidValue("direction", dir)), + } + } + + fn gpiod_direction(&self) -> u32 { + match self { + Direction::AsIs => bindings::GPIOD_LINE_DIRECTION_AS_IS, + Direction::Input => bindings::GPIOD_LINE_DIRECTION_INPUT, + Direction::Output => bindings::GPIOD_LINE_DIRECTION_OUTPUT, + } + } +} + +/// Internal bias settings. +pub enum Bias { + /// Don't change the bias setting when applying line config. + AsIs, + /// The internal bias state is unknown. + Unknown, + /// The internal bias is disabled. + Disabled, + /// The internal pull-up bias is enabled. + PullUp, + /// The internal pull-down bias is enabled. + PullDown, +} + +impl Bias { + fn new(bias: u32) -> Result { + match bias { + bindings::GPIOD_LINE_BIAS_AS_IS => Ok(Bias::AsIs), + bindings::GPIOD_LINE_BIAS_UNKNOWN => Ok(Bias::Unknown), + bindings::GPIOD_LINE_BIAS_DISABLED => Ok(Bias::Disabled), + bindings::GPIOD_LINE_BIAS_PULL_UP => Ok(Bias::PullUp), + bindings::GPIOD_LINE_BIAS_PULL_DOWN => Ok(Bias::PullDown), + _ => Err(Error::InvalidValue("bias", bias)), + } + } + + fn gpiod_bias(&self) -> u32 { + match self { + Bias::AsIs => bindings::GPIOD_LINE_BIAS_AS_IS, + Bias::Unknown => bindings::GPIOD_LINE_BIAS_UNKNOWN, + Bias::Disabled => bindings::GPIOD_LINE_BIAS_DISABLED, + Bias::PullUp => bindings::GPIOD_LINE_BIAS_PULL_UP, + Bias::PullDown => bindings::GPIOD_LINE_BIAS_PULL_DOWN, + } + } +} + +/// Drive settings. +pub enum Drive { + /// Drive setting is push-pull. + PushPull, + /// Line output is open-drain. + OpenDrain, + /// Line output is open-source. + OpenSource, +} + +impl Drive { + fn new(drive: u32) -> Result { + match drive { + bindings::GPIOD_LINE_DRIVE_PUSH_PULL => Ok(Drive::PushPull), + bindings::GPIOD_LINE_DRIVE_OPEN_DRAIN => Ok(Drive::OpenDrain), + bindings::GPIOD_LINE_DRIVE_OPEN_SOURCE => Ok(Drive::OpenSource), + _ => Err(Error::InvalidValue("drive", drive)), + } + } + + fn gpiod_drive(&self) -> u32 { + match self { + Drive::PushPull => bindings::GPIOD_LINE_DRIVE_PUSH_PULL, + Drive::OpenDrain => bindings::GPIOD_LINE_DRIVE_OPEN_DRAIN, + Drive::OpenSource => bindings::GPIOD_LINE_DRIVE_OPEN_SOURCE, + } + } +} + +/// Edge detection settings. +pub enum Edge { + /// Line edge detection is disabled. + None, + /// Line detects rising edge events. + Rising, + /// Line detect falling edge events. + Falling, + /// Line detects both rising and falling edge events. + Both, +} + +impl Edge { + fn new(edge: u32) -> Result { + match edge { + bindings::GPIOD_LINE_EDGE_NONE => Ok(Edge::None), + bindings::GPIOD_LINE_EDGE_RISING => Ok(Edge::Rising), + bindings::GPIOD_LINE_EDGE_FALLING => Ok(Edge::Falling), + bindings::GPIOD_LINE_EDGE_BOTH => Ok(Edge::Both), + _ => Err(Error::InvalidValue("edge", edge)), + } + } + + fn gpiod_edge(&self) -> u32 { + match self { + Edge::None => bindings::GPIOD_LINE_EDGE_NONE, + Edge::Rising => bindings::GPIOD_LINE_EDGE_RISING, + Edge::Falling => bindings::GPIOD_LINE_EDGE_FALLING, + Edge::Both => bindings::GPIOD_LINE_EDGE_BOTH, + } + } +} + +/// Event clock settings. +pub enum EventClock { + /// Line uses the monotonic clock for edge event timestamps. + Monotonic, + /// Line uses the realtime clock for edge event timestamps. + Realtime, +} + +impl EventClock { + fn new(clock: u32) -> Result { + match clock { + bindings::GPIOD_LINE_EVENT_CLOCK_MONOTONIC => Ok(EventClock::Monotonic), + bindings::GPIOD_LINE_EVENT_CLOCK_REALTIME => Ok(EventClock::Realtime), + _ => Err(Error::InvalidValue("event clock", clock)), + } + } + + fn gpiod_clock(&self) -> u32 { + match self { + EventClock::Monotonic => bindings::GPIOD_LINE_EVENT_CLOCK_MONOTONIC, + EventClock::Realtime => bindings::GPIOD_LINE_EVENT_CLOCK_REALTIME, + } + } +} + +/// Line status change event types. +pub enum Event { + /// Line has been requested. + LineRequested, + /// Previously requested line has been released. + LineReleased, + /// Line configuration has changed. + LineConfigChanged, +} + +impl Event { + fn new(event: u32) -> Result { + match event { + bindings::GPIOD_INFO_EVENT_LINE_REQUESTED => Ok(Event::LineRequested), + bindings::GPIOD_INFO_EVENT_LINE_RELEASED => Ok(Event::LineReleased), + bindings::GPIOD_INFO_EVENT_LINE_CONFIG_CHANGED => Ok(Event::LineConfigChanged), + _ => Err(Error::InvalidValue("event", event)), + } + } +} + +#[derive(Copy, Clone)] +/// Edge event types. +pub enum LineEdgeEvent { + /// Rising edge event. + Rising, + /// Falling edge event. + Falling, +} + +impl LineEdgeEvent { + fn new(event: u32) -> Result { + match event { + bindings::GPIOD_EDGE_EVENT_RISING_EDGE => Ok(LineEdgeEvent::Rising), + bindings::GPIOD_EDGE_EVENT_FALLING_EDGE => Ok(LineEdgeEvent::Falling), + _ => Err(Error::InvalidValue("edge event", event)), + } + } +} + +/// Various libgpiod-related functions. + +/// Check if the file pointed to by path is a GPIO chip character device. +/// +/// Returns true if the file exists and is a GPIO chip character device or a +/// symbolic link to it. +pub fn gpiod_is_gpiochip_device(path: &str) -> bool { + // Null-terminate the string + let path = path.to_owned() + "\0"; + + unsafe { bindings::gpiod_is_gpiochip_device(path.as_ptr() as *const c_char) } +} + +/// Get the API version of the library as a human-readable string. +pub fn gpiod_version_string() -> Result<&'static str> { + // SAFETY: The string returned by libgpiod is guaranteed to live forever. + let version = unsafe { bindings::gpiod_version_string() }; + + if version.is_null() { + return Err(Error::NameNotFound("GPIO library version")); + } + + // SAFETY: The string is guaranteed to be valid here. + str::from_utf8(unsafe { + slice::from_raw_parts(version as *const u8, bindings::strlen(version) as usize) + }) + .map_err(Error::InvalidString) +} diff --git a/bindings/rust/src/line_config.rs b/bindings/rust/src/line_config.rs new file mode 100644 index 000000000000..5ced30eaf5e4 --- /dev/null +++ b/bindings/rust/src/line_config.rs @@ -0,0 +1,391 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2021 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use std::time::Duration; + +use vmm_sys_util::errno::Error as IoError; + +use super::{bindings, Bias, Direction, Drive, Edge, Error, EventClock, Result}; + +/// Line configuration objects. +/// +/// The line-config object stores the configuration for lines that can be used +/// in two cases: when making a line request and when reconfiguring a set of +/// already requested lines. The mutators for the line request don't return +/// errors. If the set of options is too complex to be translated into kernel +/// uAPI structures - the error will be returned at the time of the request or +/// reconfiguration. If an invalid value was passed to any of the getters - the +/// default value will be silently used instead. Each option can be set +/// globally, for a single line offset or for multiple line offsets. +pub struct LineConfig { + config: *mut bindings::gpiod_line_config, +} + +impl LineConfig { + /// Create a new line config object. + pub fn new() -> Result { + let config = unsafe { bindings::gpiod_line_config_new() }; + + if config.is_null() { + return Err(Error::OperationFailed( + "Gpio LineConfig new", + IoError::last(), + )); + } + + Ok(Self { config }) + } + + /// Private helper, Returns gpiod_line_config + pub(crate) fn config(&self) -> *mut bindings::gpiod_line_config { + self.config + } + + /// Resets the entire configuration stored in this object. This is useful if + /// the user wants to reuse the object without reallocating it. + pub fn reset(&mut self) { + unsafe { bindings::gpiod_line_config_reset(self.config) } + } + + /// Set the direction for all lines. + pub fn set_direction(&mut self, direction: Direction) { + unsafe { + bindings::gpiod_line_config_set_direction( + self.config, + direction.gpiod_direction() as i32, + ) + } + } + + /// Set the direction for a single line at given offset. + pub fn set_direction_offset(&mut self, direction: Direction, offset: u32) { + unsafe { + bindings::gpiod_line_config_set_direction_offset( + self.config, + direction.gpiod_direction() as i32, + offset, + ) + } + } + + /// Set the direction for a subset of lines. + pub fn set_direction_subset(&mut self, direction: Direction, offsets: &[u32]) { + unsafe { + bindings::gpiod_line_config_set_direction_subset( + self.config, + direction.gpiod_direction() as i32, + offsets.len() as u32, + offsets.as_ptr(), + ) + } + } + + /// Get the direction of a given line. + /// + /// If an offset is used for which no config was provided, the function will + /// return the global default value. + pub fn get_direction(&self, offset: u32) -> Result { + Direction::new( + unsafe { bindings::gpiod_line_config_get_direction(self.config, offset) } as u32, + ) + } + + /// Set the edge event detection for all lines. + pub fn set_edge_detection(&mut self, edge: Edge) { + unsafe { + bindings::gpiod_line_config_set_edge_detection(self.config, edge.gpiod_edge() as i32) + } + } + + /// Set the edge event detection for a single line at given offset. + pub fn set_edge_detection_offset(&mut self, edge: Edge, offset: u32) { + unsafe { + bindings::gpiod_line_config_set_edge_detection_offset( + self.config, + edge.gpiod_edge() as i32, + offset, + ) + } + } + + /// Set the edge event detection for a subset of lines. + pub fn set_edge_detection_subset(&mut self, edge: Edge, offsets: &[u32]) { + unsafe { + bindings::gpiod_line_config_set_edge_detection_subset( + self.config, + edge.gpiod_edge() as i32, + offsets.len() as u32, + offsets.as_ptr(), + ) + } + } + + /// Get the edge event detection setting for a given line. + /// + /// Returns edge event detection setting that would have been used for given + /// offset if the config object was used in a request at the time of the + /// call. If an offset is used for which no config was provided, the + /// function will return the global default value. + pub fn get_edge_detection(&self, offset: u32) -> Result { + Edge::new( + unsafe { bindings::gpiod_line_config_get_edge_detection(self.config, offset) } as u32, + ) + } + + /// Set the bias of all lines. + pub fn set_bias(&mut self, bias: Bias) { + unsafe { bindings::gpiod_line_config_set_bias(self.config, bias.gpiod_bias() as i32) } + } + + /// Set the bias for a single line at given offset. + pub fn set_bias_offset(&mut self, bias: Bias, offset: u32) { + unsafe { + bindings::gpiod_line_config_set_bias_offset( + self.config, + bias.gpiod_bias() as i32, + offset, + ) + } + } + + /// Set the bias for a subset of lines. + pub fn set_bias_subset(&mut self, bias: Bias, offsets: &[u32]) { + unsafe { + bindings::gpiod_line_config_set_bias_subset( + self.config, + bias.gpiod_bias() as i32, + offsets.len() as u32, + offsets.as_ptr(), + ) + } + } + + /// Get the bias setting for a given line. + /// + /// Returns Bias setting that would have been used for given offset if the + /// config object was used in a request at the time of the call. If an + /// offset is used for which no config was provided, the function will + /// return the global default value. + pub fn get_bias(&self, offset: u32) -> Result { + Bias::new(unsafe { bindings::gpiod_line_config_get_bias(self.config, offset) } as u32) + } + + /// Set the drive of all lines. + pub fn set_drive(&mut self, drive: Drive) { + unsafe { bindings::gpiod_line_config_set_drive(self.config, drive.gpiod_drive() as i32) } + } + + /// Set the drive for a single line at given offset. + pub fn set_drive_offset(&mut self, drive: Drive, offset: u32) { + unsafe { + bindings::gpiod_line_config_set_drive_offset( + self.config, + drive.gpiod_drive() as i32, + offset, + ) + } + } + + /// Set the drive for a subset of lines. + pub fn set_drive_subset(&mut self, drive: Drive, offsets: &[u32]) { + unsafe { + bindings::gpiod_line_config_set_drive_subset( + self.config, + drive.gpiod_drive() as i32, + offsets.len() as u32, + offsets.as_ptr(), + ) + } + } + + /// Get the drive setting for a given line. + /// + /// Returns drive setting that would have been used for given offset if the + /// config object was used in a request at the time of the call. If an + /// offset is used for which no config was provided, the function will + /// return the global default value. + pub fn get_drive(&self, offset: u32) -> Result { + Drive::new(unsafe { bindings::gpiod_line_config_get_drive(self.config, offset) } as u32) + } + + /// Set all lines as active-low. + pub fn set_active_low(&mut self) { + unsafe { bindings::gpiod_line_config_set_active_low(self.config) } + } + + /// Set a single line as active-low. + pub fn set_active_low_offset(&mut self, offset: u32) { + unsafe { bindings::gpiod_line_config_set_active_low_offset(self.config, offset) } + } + + /// Set a subset of lines as active-low. + pub fn set_active_low_subset(&mut self, offsets: &[u32]) { + unsafe { + bindings::gpiod_line_config_set_active_low_subset( + self.config, + offsets.len() as u32, + offsets.as_ptr(), + ) + } + } + + /// Check if the line at given offset was configured as active-low. + /// + /// Returns active-low setting that would have been used for given offset if + /// the config object was used in a request at the time of the call. If an + /// offset is used for which no config was provided, the function will + /// return the global default value. + pub fn is_active_low(&self, offset: u32) -> bool { + unsafe { bindings::gpiod_line_config_get_active_low(self.config, offset) } + } + + /// Set all lines as active-high. + pub fn set_active_high(&mut self) { + unsafe { bindings::gpiod_line_config_set_active_high(self.config) } + } + + /// Set a single line as active-high. + pub fn set_active_high_offset(&mut self, offset: u32) { + unsafe { bindings::gpiod_line_config_set_active_high_offset(self.config, offset) } + } + + /// Set a subset of lines as active-high. + pub fn set_active_high_subset(&mut self, offsets: &[u32]) { + unsafe { + bindings::gpiod_line_config_set_active_high_subset( + self.config, + offsets.len() as u32, + offsets.as_ptr(), + ) + } + } + + /// Set the debounce period for all lines, disables debouncing if 0. + pub fn set_debounce_period_us(&mut self, period: Duration) { + unsafe { + bindings::gpiod_line_config_set_debounce_period_us( + self.config, + period.as_micros() as u64, + ) + } + } + + /// Set the debounce period for a single line at given offset, disables + /// debouncing if 0. + pub fn set_debounce_period_us_offset(&mut self, period: Duration, offset: u32) { + unsafe { + bindings::gpiod_line_config_set_debounce_period_us_offset( + self.config, + period.as_micros() as u64, + offset, + ) + } + } + + /// Set the debounce period for a subset of lines, disables debouncing if + /// `period` is 0. + pub fn set_debounce_period_us_subset(&mut self, period: Duration, offsets: &[u32]) { + unsafe { + bindings::gpiod_line_config_set_debounce_period_us_subset( + self.config, + period.as_micros() as u64, + offsets.len() as u32, + offsets.as_ptr(), + ) + } + } + + /// Get the debounce period for a given line. + /// + /// Returns debounce period that would have been used for given offset if + /// the config object was used in a request at the time of the call. If an + /// offset is used for which no config was provided, the function will + /// return the global default value. + pub fn get_debounce_period_us(&self, offset: u32) -> Result { + Ok(Duration::from_micros(unsafe { + bindings::gpiod_line_config_get_debounce_us_period(self.config, offset) + })) + } + + /// Set the event clock for all lines. + pub fn set_event_clock(&mut self, clock: EventClock) { + unsafe { + bindings::gpiod_line_config_set_event_clock(self.config, clock.gpiod_clock() as i32) + } + } + + /// Set the event clock for a single line at given offset. + pub fn set_event_clock_offset(&mut self, clock: EventClock, offset: u32) { + unsafe { + bindings::gpiod_line_config_set_event_clock_offset( + self.config, + clock.gpiod_clock() as i32, + offset, + ) + } + } + + /// Set the event clock for a subset of lines. + pub fn set_event_clock_subset(&mut self, clock: EventClock, offsets: &[u32]) { + unsafe { + bindings::gpiod_line_config_set_event_clock_subset( + self.config, + clock.gpiod_clock() as i32, + offsets.len() as u32, + offsets.as_ptr(), + ) + } + } + + /// Get the event clock setting for a given line. + /// + /// Returns event clock setting that would have been used for given offset + /// if the config object was used in a request at the time of the call. If + /// an offset is used for which no config was provided, the function will + /// return the global default value. + pub fn get_event_clock(&self, offset: u32) -> Result { + EventClock::new( + unsafe { bindings::gpiod_line_config_get_event_clock(self.config, offset) } as u32, + ) + } + + /// Set the output value for a single offset. + pub fn set_output_value(&mut self, offset: u32, value: u32) { + unsafe { bindings::gpiod_line_config_set_output_value(self.config, offset, value as i32) } + } + + /// Set the output values for a set of offsets. + pub fn set_output_values(&mut self, offsets: &[u32], values: &[i32]) { + unsafe { + bindings::gpiod_line_config_set_output_values( + self.config, + values.len() as u32, + offsets.as_ptr(), + values.as_ptr(), + ) + } + } + + /// Get the output value configured for a given line, 0 or 1. + pub fn get_output_value(&self, offset: u32) -> Result { + let value = unsafe { bindings::gpiod_line_config_get_output_value(self.config, offset) }; + + if value != 0 && value != 1 { + Err(Error::OperationFailed( + "Gpio LineConfig get-output-value", + IoError::last(), + )) + } else { + Ok(value as u32) + } + } +} + +impl Drop for LineConfig { + /// Free the line config object and release all associated resources. + fn drop(&mut self) { + unsafe { bindings::gpiod_line_config_free(self.config) } + } +} diff --git a/bindings/rust/src/line_info.rs b/bindings/rust/src/line_info.rs new file mode 100644 index 000000000000..c1cc3d2d95c5 --- /dev/null +++ b/bindings/rust/src/line_info.rs @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2021 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use std::convert::TryFrom; +use std::sync::Arc; +use std::time::Duration; +use std::{slice, str}; + +use vmm_sys_util::errno::Error as IoError; + +use super::{ + bindings, Bias, ChipInternal, Direction, Drive, Edge, Error, EventClock, InfoEvent, Result, +}; + +/// Line info +/// +/// Exposes functions for retrieving kernel information about both requested and +/// free lines. Line info object contains an immutable snapshot of the line's +/// state at the time when it was created. +pub struct LineInfo { + info: *mut bindings::gpiod_line_info, + ichip: Option>, + free: bool, +} + +impl LineInfo { + /// Get the current snapshot of information about the line at given offset + /// and optionally start watching it for changes. + pub(crate) fn new(ichip: Arc, offset: u32, watch: bool) -> Result { + let info = if watch { + unsafe { bindings::gpiod_chip_watch_line_info(ichip.chip(), offset) } + } else { + unsafe { bindings::gpiod_chip_get_line_info(ichip.chip(), offset) } + }; + + if info.is_null() { + return Err(Error::OperationFailed( + "Gpio LineInfo line-info", + IoError::last(), + )); + } + + Ok(Self { + info, + ichip: if watch { Some(ichip) } else { None }, + free: watch, + }) + } + + /// Stop watching the line + pub fn unwatch(&mut self) { + if let Some(ichip) = &self.ichip { + unsafe { + bindings::gpiod_chip_unwatch_line_info(ichip.chip(), self.get_offset()); + } + self.ichip = None; + } + } + + /// Get the offset of the line within the GPIO chip. + pub fn get_offset(&self) -> u32 { + unsafe { bindings::gpiod_line_info_get_offset(self.info) } + } + + /// Get GPIO line's name. + pub fn get_name(&self) -> Result<&str> { + // SAFETY: The string returned by libgpiod is guaranteed to live as long + // as the `struct LineInfo`. + let name = unsafe { bindings::gpiod_line_info_get_name(self.info) }; + if name.is_null() { + return Err(Error::NameNotFound("GPIO line's name")); + } + + // SAFETY: The string is guaranteed to be valid here. + str::from_utf8(unsafe { + slice::from_raw_parts(name as *const u8, bindings::strlen(name) as usize) + }) + .map_err(Error::InvalidString) + } + + /// Returns True if the line is in use, false otherwise. + /// + /// The user space can't know exactly why a line is busy. It may have been + /// requested by another process or hogged by the kernel. It only matters that + /// the line is used and we can't request it. + pub fn is_used(&self) -> bool { + unsafe { bindings::gpiod_line_info_is_used(self.info) } + } + + /// Get the GPIO line's consumer name. + pub fn get_consumer(&self) -> Result<&str> { + // SAFETY: The string returned by libgpiod is guaranteed to live as long + // as the `struct LineInfo`. + let name = unsafe { bindings::gpiod_line_info_get_consumer(self.info) }; + if name.is_null() { + return Err(Error::NameNotFound("GPIO line's consumer name")); + } + + // SAFETY: The string is guaranteed to be valid here. + str::from_utf8(unsafe { + slice::from_raw_parts(name as *const u8, bindings::strlen(name) as usize) + }) + .map_err(Error::InvalidString) + } + + /// Get the GPIO line's direction. + pub fn get_direction(&self) -> Result { + Direction::new(unsafe { bindings::gpiod_line_info_get_direction(self.info) } as u32) + } + + /// Returns true if this line is "active-low", false otherwise. + pub fn is_active_low(&self) -> bool { + unsafe { bindings::gpiod_line_info_is_active_low(self.info) } + } + + /// Get the GPIO line's bias setting. + pub fn get_bias(&self) -> Result { + Bias::new(unsafe { bindings::gpiod_line_info_get_bias(self.info) } as u32) + } + + /// Get the GPIO line's drive setting. + pub fn get_drive(&self) -> Result { + Drive::new(unsafe { bindings::gpiod_line_info_get_drive(self.info) } as u32) + } + + /// Get the current edge detection setting of this line. + pub fn get_edge_detection(&self) -> Result { + Edge::new(unsafe { bindings::gpiod_line_info_get_edge_detection(self.info) } as u32) + } + + /// Get the current event clock setting used for edge event timestamps. + pub fn get_event_clock(&self) -> Result { + EventClock::new(unsafe { bindings::gpiod_line_info_get_event_clock(self.info) } as u32) + } + + /// Returns true if the line is debounced (either by hardware or by the + /// kernel software debouncer), false otherwise. + pub fn is_debounced(&self) -> bool { + unsafe { bindings::gpiod_line_info_is_debounced(self.info) } + } + + /// Get the current debounce period. + pub fn get_debounce_period_us(&self) -> Duration { + Duration::from_micros(unsafe { + bindings::gpiod_line_info_get_debounce_period_us(self.info) + }) + } +} + +impl TryFrom<&InfoEvent> for LineInfo { + type Error = Error; + + /// Get the Line info object associated with a event. + fn try_from(event: &InfoEvent) -> Result { + let info = unsafe { bindings::gpiod_info_event_get_line_info(event.event()) }; + if info.is_null() { + return Err(Error::OperationFailed( + "Gpio LineInfo try-from", + IoError::last(), + )); + } + + Ok(Self { + info, + ichip: None, + free: false, + }) + } +} + +impl Drop for LineInfo { + fn drop(&mut self) { + // We must not free the Line info object created from `struct InfoEvent` by calling + // libgpiod API. + if self.free { + self.unwatch(); + unsafe { bindings::gpiod_line_info_free(self.info) } + } + } +} diff --git a/bindings/rust/src/line_request.rs b/bindings/rust/src/line_request.rs new file mode 100644 index 000000000000..0a464c5fa824 --- /dev/null +++ b/bindings/rust/src/line_request.rs @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2021 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use std::sync::Arc; +use std::time::Duration; + +use vmm_sys_util::errno::Error as IoError; + +use super::{bindings, ChipInternal, EdgeEventBuffer, Error, LineConfig, RequestConfig, Result}; + +/// Line request operations +/// +/// Allows interaction with a set of requested lines. +pub struct LineRequest { + request: *mut bindings::gpiod_line_request, +} + +impl LineRequest { + /// Request a set of lines for exclusive usage. + pub(crate) fn new( + ichip: &Arc, + rconfig: &RequestConfig, + lconfig: &LineConfig, + ) -> Result { + let request = unsafe { + bindings::gpiod_chip_request_lines(ichip.chip(), rconfig.config(), lconfig.config()) + }; + + if request.is_null() { + return Err(Error::OperationFailed( + "Gpio LineRequest request-lines", + IoError::last(), + )); + } + + Ok(Self { request }) + } + + /// Get the number of lines in this request. + pub fn get_num_lines(&self) -> u32 { + unsafe { bindings::gpiod_line_request_get_num_lines(self.request) } + } + + /// Get the offsets of lines in this request. + pub fn get_offsets(&self) -> Vec { + let mut offsets = vec![0; self.get_num_lines() as usize]; + + unsafe { bindings::gpiod_line_request_get_offsets(self.request, offsets.as_mut_ptr()) }; + offsets + } + + /// Get values of all lines associated with this request. + pub fn get_values(&self, values: &mut Vec) -> Result<()> { + let ret = + unsafe { bindings::gpiod_line_request_get_values(self.request, values.as_mut_ptr()) }; + + if ret == -1 { + Err(Error::OperationFailed( + "Gpio LineRequest get-values", + IoError::last(), + )) + } else { + Ok(()) + } + } + + /// Get the value (0 or 1) of a single line associated with this request. + pub fn get_value(&self, offset: u32) -> Result { + let value = unsafe { bindings::gpiod_line_request_get_value(self.request, offset) }; + + if value != 0 && value != 1 { + Err(Error::OperationFailed( + "Gpio LineRequest get-value", + IoError::last(), + )) + } else { + Ok(value as u32) + } + } + + /// Get values of a subset of lines associated with this request. + pub fn get_values_subset(&self, offsets: &[u32], values: &mut Vec) -> Result<()> { + let ret = unsafe { + bindings::gpiod_line_request_get_values_subset( + self.request, + offsets.len() as u32, + offsets.as_ptr(), + values.as_mut_ptr(), + ) + }; + + if ret == -1 { + Err(Error::OperationFailed( + "Gpio LineRequest get-values-subset", + IoError::last(), + )) + } else { + Ok(()) + } + } + + /// Get values of all lines associated with this request. + pub fn set_values(&self, values: &[i32]) -> Result<()> { + let ret = unsafe { bindings::gpiod_line_request_set_values(self.request, values.as_ptr()) }; + + if ret == -1 { + Err(Error::OperationFailed( + "Gpio LineRequest set-values", + IoError::last(), + )) + } else { + Ok(()) + } + } + + /// Set the value of a single line associated with this request. + pub fn set_value(&self, offset: u32, value: i32) -> Result<()> { + let ret = unsafe { bindings::gpiod_line_request_set_value(self.request, offset, !!value) }; + + if ret == -1 { + Err(Error::OperationFailed( + "Gpio LineRequest set-value", + IoError::last(), + )) + } else { + Ok(()) + } + } + + /// Get values of a subset of lines associated with this request. + pub fn set_values_subset(&self, offsets: &[u32], values: &[i32]) -> Result<()> { + let ret = unsafe { + bindings::gpiod_line_request_set_values_subset( + self.request, + offsets.len() as u32, + offsets.as_ptr(), + values.as_ptr(), + ) + }; + + if ret == -1 { + Err(Error::OperationFailed( + "Gpio LineRequest set-values-subset", + IoError::last(), + )) + } else { + Ok(()) + } + } + + /// Update the configuration of lines associated with this line request. + pub fn reconfigure_lines(&self, lconfig: &LineConfig) -> Result<()> { + let ret = unsafe { + bindings::gpiod_line_request_reconfigure_lines(self.request, lconfig.config()) + }; + + if ret == -1 { + Err(Error::OperationFailed( + "Gpio LineRequest reconfigure-lines", + IoError::last(), + )) + } else { + Ok(()) + } + } + + /// Get the file descriptor associated with this line request. + pub fn get_fd(&self) -> u32 { + unsafe { bindings::gpiod_line_request_get_fd(self.request) as u32 } + } + + /// Wait for edge events on any of the lines associated with this request. + pub fn edge_event_wait(&self, timeout: Duration) -> Result<()> { + let ret = unsafe { + bindings::gpiod_line_request_edge_event_wait(self.request, timeout.as_nanos() as u64) + }; + + match ret { + -1 => Err(Error::OperationFailed( + "Gpio LineRequest edge-event-wait", + IoError::last(), + )), + 0 => Err(Error::OperationTimedOut), + _ => Ok(()), + } + } + + /// Get a number of edge events from a line request. + /// + /// This function will block if no event was queued for this line. + pub fn edge_event_read(&self, buffer: &EdgeEventBuffer, max_events: u32) -> Result { + let ret = unsafe { + bindings::gpiod_line_request_edge_event_read(self.request, buffer.buffer(), max_events) + }; + + if ret == -1 { + Err(Error::OperationFailed( + "Gpio LineRequest edge-event-read", + IoError::last(), + )) + } else { + Ok(ret as u32) + } + } +} + +impl Drop for LineRequest { + /// Release the requested lines and free all associated resources. + fn drop(&mut self) { + unsafe { bindings::gpiod_line_request_release(self.request) } + } +} diff --git a/bindings/rust/src/request_config.rs b/bindings/rust/src/request_config.rs new file mode 100644 index 000000000000..aff8f94d962b --- /dev/null +++ b/bindings/rust/src/request_config.rs @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2021 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use std::os::raw::c_char; +use std::{slice, str}; + +use vmm_sys_util::errno::Error as IoError; + +use super::{bindings, Error, Result}; + +/// Request configuration objects +/// +/// Request config object is used to pass a set of options to the kernel at the +/// time of the line request. Similarly to the line-config - the mutators don't +/// return error values. If the values are invalid, in general they are silently +/// adjusted to acceptable ranges. +pub struct RequestConfig { + config: *mut bindings::gpiod_request_config, +} + +impl RequestConfig { + /// Create a new request config object. + pub fn new() -> Result { + let config = unsafe { bindings::gpiod_request_config_new() }; + if config.is_null() { + return Err(Error::OperationFailed( + "Gpio RequestConfig new", + IoError::last(), + )); + } + + Ok(Self { config }) + } + + /// Private helper, Returns gpiod_request_config + pub(crate) fn config(&self) -> *mut bindings::gpiod_request_config { + self.config + } + + /// Set the consumer string. + /// + /// If the consumer string is too long, it will be truncated to the max + /// accepted length. + pub fn set_consumer(&self, consumer: &str) { + // Null-terminate the string + let consumer = consumer.to_owned() + "\0"; + + unsafe { + bindings::gpiod_request_config_set_consumer( + self.config, + consumer.as_ptr() as *const c_char, + ) + } + } + + /// Get the consumer string. + pub fn get_consumer(&self) -> Result<&str> { + // SAFETY: The string returned by libgpiod is guaranteed to live as long + // as the `struct RequestConfig`. + let consumer = unsafe { bindings::gpiod_request_config_get_consumer(self.config) }; + if consumer.is_null() { + return Err(Error::OperationFailed( + "Gpio RequestConfig get-consumer", + IoError::last(), + )); + } + + // SAFETY: The string is guaranteed to be valid here. + str::from_utf8(unsafe { + slice::from_raw_parts(consumer as *const u8, bindings::strlen(consumer) as usize) + }) + .map_err(Error::InvalidString) + } + + /// Set line offsets for this request. + /// + /// If too many offsets were specified, the offsets above the limit accepted + /// by the kernel (64 lines) are silently dropped. + pub fn set_offsets(&self, offsets: &[u32]) { + unsafe { + bindings::gpiod_request_config_set_offsets( + self.config, + offsets.len() as u32, + offsets.as_ptr(), + ) + } + } + + /// Get the offsets of lines in this request config. + pub fn get_num_offsets(&self) -> Vec { + let num = unsafe { bindings::gpiod_request_config_get_num_offsets(self.config) }; + let mut offsets = vec![0, num]; + + unsafe { bindings::gpiod_request_config_get_offsets(self.config, offsets.as_mut_ptr()) }; + offsets + } + + /// Set the size of the kernel event buffer. + /// + /// The kernel may adjust the value if it's too high. If set to 0, the + /// default value will be used. + pub fn set_event_buffer_size(&self, size: u32) { + unsafe { bindings::gpiod_request_config_set_event_buffer_size(self.config, size) } + } + + /// Get the edge event buffer size from this request config. + pub fn get_event_buffer_size(&self) -> u32 { + unsafe { bindings::gpiod_request_config_get_event_buffer_size(self.config) } + } +} + +impl Drop for RequestConfig { + /// Free the request config object and release all associated resources. + fn drop(&mut self) { + unsafe { bindings::gpiod_request_config_free(self.config) } + } +} From patchwork Mon Dec 20 11:11:37 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 526715 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 DC12AC433F5 for ; Mon, 20 Dec 2021 11:17:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231613AbhLTLRQ (ORCPT ); Mon, 20 Dec 2021 06:17:16 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40972 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231171AbhLTLRQ (ORCPT ); Mon, 20 Dec 2021 06:17:16 -0500 Received: from mail-pf1-x430.google.com (mail-pf1-x430.google.com [IPv6:2607:f8b0:4864:20::430]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id F1383C061574 for ; Mon, 20 Dec 2021 03:17:15 -0800 (PST) Received: by mail-pf1-x430.google.com with SMTP id c2so8439687pfc.1 for ; Mon, 20 Dec 2021 03:17:15 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=dFl/9CfI0v3IUZFEwsW2+WxeLJI+0xP6cCS9S/x45aU=; b=RClDW+oaQ9qYJkzflzuz0qjgWMZhiaJLWbMawHnRHXD2DhgfvjuiYEmmRknIVZq580 hcRYDs6NVmLqMmW1dT0ifISLNjUegWCzr74Es6fDczIEXetR84C0pFoCg0ONRdtRfxwj 7CxptjhOsu3bq0SWRfZkFu8Wz3rvYHTLP6kSVn9kp/evpY5MWh9INKwaxTTb7NWkt7o2 22oaRRxVwsEpqjEUd5lxIt1Ud7Oqi08ZmTcATe4WrUywxh8ATWpxM+okawwIcP57lvv5 YpsruOn6yvFENPvmzX765KBLCAwO/LXbe0KscrnEHsLb24y9x2K8f+gvcFTNa1MRAOVO EvCg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=dFl/9CfI0v3IUZFEwsW2+WxeLJI+0xP6cCS9S/x45aU=; b=pFjsRrNb9Zu+w3ruYZRubG9qrlZXtYbhPSLDkesClXPRUjX+FWHwiLeygrkquw8x5s hhU4plr5GBzgT9GfaECMNFVSHjewPbm5bQB/8ix7sriRtu8qHCkK+sapV+9otvc+aLmF feCDJmyVfbk1EYmurfFUt5u7b23UtTzST4ajMXTBCTeZ7t23bWaMQYT0VIItPITYzpKr vL2yJ4qSxXXx3RWVZNkFhn5j6kERMn3TYaPfTn2isKxKY5OrRwiwIMqWu4Hz+UZe68m/ T8sSdpb/Kg3IYuoya5a1CUamWDJ2USPcR2WI/dFrs3BnNPLA/cky8CIvwUupSxJxXou+ jPrQ== X-Gm-Message-State: AOAM531FjAEmu/lJoua+WRimQY48t2RbjDRK4e2uxw1g5gqS7U8zFoQb NIIcMU6ia5BDs2HqO1fjInLgRA== X-Google-Smtp-Source: ABdhPJwcQy0l3u095xqsjlvLdGA3MlJRwlnsXVnog4mM2K7MGBNKcQy6Hs0vKNEB7WA9EuIcn5Qe6Q== X-Received: by 2002:a62:b40a:0:b0:4ad:55cb:1766 with SMTP id h10-20020a62b40a000000b004ad55cb1766mr15829069pfn.47.1639999035435; Mon, 20 Dec 2021 03:17:15 -0800 (PST) Received: from localhost ([106.201.42.111]) by smtp.gmail.com with ESMTPSA id d10sm15100260pfj.51.2021.12.20.03.17.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 20 Dec 2021 03:17:15 -0800 (PST) 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 V3 3/4] libgpiod: Add rust examples Date: Mon, 20 Dec 2021 16:41:37 +0530 Message-Id: <2c8660feb33219eb52c7f53c8c161b3955b3ba8b.1639997241.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/examples/gpiodetect.rs | 37 ++++++++++++ bindings/rust/examples/gpiofind.rs | 42 +++++++++++++ bindings/rust/examples/gpioget.rs | 42 +++++++++++++ bindings/rust/examples/gpioinfo.rs | 89 ++++++++++++++++++++++++++++ bindings/rust/examples/gpiomon.rs | 68 +++++++++++++++++++++ bindings/rust/examples/gpioset.rs | 52 ++++++++++++++++ 6 files changed, 330 insertions(+) create mode 100644 bindings/rust/examples/gpiodetect.rs create mode 100644 bindings/rust/examples/gpiofind.rs create mode 100644 bindings/rust/examples/gpioget.rs create mode 100644 bindings/rust/examples/gpioinfo.rs create mode 100644 bindings/rust/examples/gpiomon.rs create mode 100644 bindings/rust/examples/gpioset.rs diff --git a/bindings/rust/examples/gpiodetect.rs b/bindings/rust/examples/gpiodetect.rs new file mode 100644 index 000000000000..505ad1e3420d --- /dev/null +++ b/bindings/rust/examples/gpiodetect.rs @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2021 Linaro Ltd. All Rights Reserved. +// Viresh Kumar +// +// Simplified Rust implementation of gpiodetect tool. + +use std::env; +use std::fs; +use std::path::Path; + +use libgpiod::{gpiod_is_gpiochip_device, Chip}; + +fn main() { + let args: Vec = env::args().collect(); + if args.len() > 1 { + println!("Usage: {}", args[0]); + return; + } + + for entry in fs::read_dir(Path::new("/dev")).unwrap() { + let pathbuf = entry.unwrap().path(); + let path = pathbuf.to_str().unwrap(); + + if gpiod_is_gpiochip_device(path) { + let chip = Chip::open(path).unwrap(); + let ngpio = chip.get_num_lines(); + + println!( + "{} [{}] ({})", + chip.get_name().unwrap(), + chip.get_label().unwrap(), + ngpio + ); + } + } +} diff --git a/bindings/rust/examples/gpiofind.rs b/bindings/rust/examples/gpiofind.rs new file mode 100644 index 000000000000..7c20d71acb9f --- /dev/null +++ b/bindings/rust/examples/gpiofind.rs @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2021 Linaro Ltd. All Rights Reserved. +// Viresh Kumar +// +// Simplified Rust implementation of gpiofind tool. + +use std::env; +use std::fs; +use std::path::Path; + +use libgpiod::{gpiod_is_gpiochip_device, Chip}; + +fn main() { + let args: Vec = env::args().collect(); + if args.len() != 2 { + println!("Usage: {} ", args[0]); + return; + } + + for entry in fs::read_dir(Path::new("/dev")).unwrap() { + let pathbuf = entry.unwrap().path(); + let path = pathbuf.to_str().unwrap(); + + if gpiod_is_gpiochip_device(path) { + let chip = Chip::open(path).unwrap(); + + let offset = chip.find_line(&args[1]); + if offset.is_ok() { + println!( + "Line {} found: Chip: {}, offset: {}", + args[1], + chip.get_name().unwrap(), + offset.unwrap() + ); + return; + } + } + } + + println!("Failed to find line: {}", args[1]); +} diff --git a/bindings/rust/examples/gpioget.rs b/bindings/rust/examples/gpioget.rs new file mode 100644 index 000000000000..0105cefdc119 --- /dev/null +++ b/bindings/rust/examples/gpioget.rs @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2021 Linaro Ltd. All Rights Reserved. +// Viresh Kumar +// +// Simplified Rust implementation of gpioget tool. + +use std::env; + +use libgpiod::{Chip, Direction, LineConfig, RequestConfig}; + +fn main() { + let args: Vec = env::args().collect(); + if args.len() < 3 { + println!("Usage: {} ...", args[0]); + return; + } + + let mut config = LineConfig::new().unwrap(); + let mut offsets = Vec::::new(); + + for arg in &args[2..] { + let offset = arg.parse::().unwrap(); + + offsets.push(offset); + config.set_direction_offset(Direction::Input, offset); + } + + let path = format!("/dev/gpiochip{}", args[1]); + let chip = Chip::open(&path).unwrap(); + + let rconfig = RequestConfig::new().unwrap(); + rconfig.set_consumer(&args[0]); + rconfig.set_offsets(&offsets); + + let request = chip.request_lines(&rconfig, &config).unwrap(); + + let mut values: Vec = vec![0; offsets.len()]; + request.get_values(&mut values).unwrap(); + + println!("{:?}", values); +} diff --git a/bindings/rust/examples/gpioinfo.rs b/bindings/rust/examples/gpioinfo.rs new file mode 100644 index 000000000000..1d74a17e3c90 --- /dev/null +++ b/bindings/rust/examples/gpioinfo.rs @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2021 Linaro Ltd. All Rights Reserved. +// Viresh Kumar +// +// Simplified Rust implementation of gpioinfo tool. + +use std::env; +use std::fs; +use std::path::Path; + +use libgpiod::{gpiod_is_gpiochip_device, Chip, Direction}; + +fn line_info(chip: &Chip, offset: u32) { + let info = chip.line_info(offset).unwrap(); + let off = info.get_offset(); + + let name = match info.get_name() { + Ok(name) => name, + _ => "unused", + }; + + let consumer = match info.get_consumer() { + Ok(name) => name, + _ => "unnamed", + }; + + let low = if info.is_active_low() { + "active-low" + } else { + "active-high" + }; + + let dir = match info.get_direction().unwrap() { + Direction::AsIs => "None", + Direction::Input => "Input", + Direction::Output => "Output", + }; + + println!( + "\tline {:>3}\ + \t{:>10}\ + \t{:>10}\ + \t{:>6}\ + \t{:>14}", + off, name, consumer, dir, low + ); +} + +fn chip_info(path: &str) { + if gpiod_is_gpiochip_device(path) { + let chip = Chip::open(path).unwrap(); + let ngpio = chip.get_num_lines(); + + println!("GPIO Chip name: {}", chip.get_name().unwrap()); + println!("\tlabel: {}", chip.get_label().unwrap()); + println!("\tpath: {}", chip.get_path().unwrap()); + println!("\tngpio: {}\n", ngpio); + + println!("\tLine information:"); + + for offset in 0..ngpio { + line_info(&chip, offset); + } + println!("\n"); + } +} + +fn main() { + let args: Vec = env::args().collect(); + if args.len() > 2 { + println!("Usage: {}", args[0]); + return; + } + + if args.len() == 1 { + for entry in fs::read_dir(Path::new("/dev")).unwrap() { + let pathbuf = entry.unwrap().path(); + let path = pathbuf.to_str().unwrap(); + + chip_info(path); + } + } else { + let index = args[1].parse::().unwrap(); + let path = format!("/dev/gpiochip{}", index); + + chip_info(&path); + } +} diff --git a/bindings/rust/examples/gpiomon.rs b/bindings/rust/examples/gpiomon.rs new file mode 100644 index 000000000000..cf6700c5a960 --- /dev/null +++ b/bindings/rust/examples/gpiomon.rs @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2021 Linaro Ltd. All Rights Reserved. +// Viresh Kumar +// +// Simplified Rust implementation of the gpiomon tool. + +use std::env; +use std::time::Duration; + +use libgpiod::{Chip, Edge, EdgeEventBuffer, Error, LineConfig, LineEdgeEvent, RequestConfig}; + +fn usage(name: &str) { + println!("Usage: {} ...", name); +} + +fn main() { + let args: Vec = env::args().collect(); + if args.len() < 3 { + usage(&args[0]); + return; + } + + let mut config = LineConfig::new().unwrap(); + let mut offsets = Vec::::new(); + + for arg in &args[2..] { + let offset = arg.parse::().unwrap(); + + offsets.push(offset); + } + + config.set_edge_detection(Edge::Both); + + let path = format!("/dev/gpiochip{}", args[1]); + let chip = Chip::open(&path).unwrap(); + + let rconfig = RequestConfig::new().unwrap(); + rconfig.set_offsets(&offsets); + + let buffer = EdgeEventBuffer::new(1).unwrap(); + let request = chip.request_lines(&rconfig, &config).unwrap(); + + loop { + match request.edge_event_wait(Duration::new(1, 0)) { + Err(Error::OperationTimedOut) => continue, + Err(x) => { + println!("{:?}", x); + return; + } + Ok(()) => (), + } + + let count = request.edge_event_read(&buffer, 1).unwrap(); + if count == 1 { + let event = buffer.get_event(0).unwrap(); + println!( + "line: {} type: {}, time: {:?}", + event.get_line_offset(), + match event.get_event_type().unwrap() { + LineEdgeEvent::Rising => "Rising", + LineEdgeEvent::Falling => "Falling", + }, + event.get_timestamp() + ); + } + } +} diff --git a/bindings/rust/examples/gpioset.rs b/bindings/rust/examples/gpioset.rs new file mode 100644 index 000000000000..3b8f9d227181 --- /dev/null +++ b/bindings/rust/examples/gpioset.rs @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2021 Linaro Ltd. All Rights Reserved. +// Viresh Kumar +// +// Simplified Rust implementation of the gpioset tool. + +use std::env; + +use libgpiod::{Chip, Direction, LineConfig, RequestConfig}; + +fn usage(name: &str) { + println!("Usage: {} = ...", name); +} + +fn main() { + let args: Vec = env::args().collect(); + if args.len() < 3 { + usage(&args[0]); + return; + } + + let mut config = LineConfig::new().unwrap(); + let mut offsets = Vec::::new(); + let mut values = Vec::::new(); + + for arg in &args[2..] { + let pair: Vec<&str> = arg.split('=').collect(); + if pair.len() != 2 { + usage(&args[0]); + return; + } + + let offset = pair[0].parse::().unwrap(); + let value = pair[1].parse::().unwrap(); + + offsets.push(offset); + values.push(value as i32); + } + + config.set_direction(Direction::Output); + config.set_output_values(&offsets, &values); + + let path = format!("/dev/gpiochip{}", args[1]); + let chip = Chip::open(&path).unwrap(); + + let rconfig = RequestConfig::new().unwrap(); + rconfig.set_consumer(&args[0]); + rconfig.set_offsets(&offsets); + + chip.request_lines(&rconfig, &config).unwrap(); +} From patchwork Mon Dec 20 11:11:38 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 526714 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 12CCCC433FE for ; Mon, 20 Dec 2021 11:17:20 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231615AbhLTLRT (ORCPT ); Mon, 20 Dec 2021 06:17:19 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40988 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231171AbhLTLRT (ORCPT ); Mon, 20 Dec 2021 06:17:19 -0500 Received: from mail-pl1-x62c.google.com (mail-pl1-x62c.google.com [IPv6:2607:f8b0:4864:20::62c]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1EB7AC061574 for ; Mon, 20 Dec 2021 03:17:19 -0800 (PST) Received: by mail-pl1-x62c.google.com with SMTP id o14so7825714plg.5 for ; Mon, 20 Dec 2021 03:17:19 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=QMVe28OO0OCZDw3TcthevdePGi3h09b4OI/egNb3mqw=; b=Sp3kx5lZi9lCfpWhLPFSAR5rlzGqha74FZkn4tVLP/dp+r/kSSCe8goVD0Ii3dfT4y zhv2BmDmAmLrlRkKaNYtnZ7I/B/SXa6jwtdBTXWSaGMMMngEPQMkW8Zq9Qlp7ins+Q6K OCuSr3QVgNRInXjDPPXU2eKTgmWFAFh/es8bRzI9o7TluM4z3RsQdxT1p92MqBaxrB5I qJtJ27uet1N0qaUsWa1vvP1cn25oG1kMfALkIR7RbdMhWYr244CCDHDuAEUQxic4OFGl xbMuqYMscnqTpzi2Bxe3dHkXzMdEkKD5kL5tWwAPk69hybkttsryT05/0vBHOofkNROe jK3g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=QMVe28OO0OCZDw3TcthevdePGi3h09b4OI/egNb3mqw=; b=ISFs5G5bTuehjf/89BktcPpVhrMsmVnDH14508Eo/j/lqtJ1MphL93/eC8PPtaUpmb df7+N+d+we0nJMDTjia9fnErAvLq/PMi/Xi2s1/NmQj7oEqxKSCzB1sWzaYLqjZC+2UX 6fmtpRFylzPMXFW/cFOgg57/a/Do4e99gLz1/sa4PA1zaqFw/teQLn0XuwEM0CYp3RHF BTG51rLUNniFeCMYZjTAwOw8qVAGQc2LLuSeY+dzK5dygF8rt/04qE5bkF6rBJbW7wBa xo0OeTV/R+W4enKva8H6NVtgmPmdX209vr0WyTXw6FE79RoxjVwY0VTVltV5Qoa6OVRT 5l1A== X-Gm-Message-State: AOAM5325WY2ZZ26aQKO1pv286GrGoaoiS1+k24I50WHrB/xP4YBo08ms KvKboKMdLFikJh1gRpcSkE6FkQ== X-Google-Smtp-Source: ABdhPJyiXw23mEPeZk+HiporFRnPz3GIKZI/nSTvJH+3q67w8RTcn68vJSAFV8Y5r2X242D8/s6Ypw== X-Received: by 2002:a17:90a:a083:: with SMTP id r3mr27162124pjp.55.1639999037818; Mon, 20 Dec 2021 03:17:17 -0800 (PST) Received: from localhost ([106.201.42.111]) by smtp.gmail.com with ESMTPSA id l22sm12065153pfc.167.2021.12.20.03.17.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 20 Dec 2021 03:17:17 -0800 (PST) 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 V3 4/4] libgpiod: Integrate building of rust bindings with make Date: Mon, 20 Dec 2021 16:41:38 +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 Let make build rust bindings as well. Signed-off-by: Viresh Kumar --- README | 8 +++++--- TODO | 8 -------- bindings/Makefile.am | 6 ++++++ bindings/rust/Makefile.am | 27 +++++++++++++++++++++++++++ configure.ac | 16 ++++++++++++++++ 5 files changed, 54 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..9799f0036e2f --- /dev/null +++ b/bindings/rust/Makefile.am @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# SPDX-FileCopyrightText: 2021 Viresh Kumar + +LIBGPIOD_SRC = \ + Cargo.toml \ + src/chip.rs \ + src/edge_event.rs \ + src/event_buffer.rs \ + src/info_event.rs \ + src/lib.rs \ + src/line_config.rs \ + src/line_info.rs \ + src/line_request.rs \ + src/request_config.rs \ + examples/gpiodetect.rs \ + examples/gpiofind.rs \ + examples/gpioget.rs \ + examples/gpioinfo.rs \ + examples/gpiomon.rs \ + examples/gpioset.rs + + +all: $(LIBGPIOD_SRC) + cargo build --lib --examples + +clean: + cargo clean diff --git a/configure.ac b/configure.ac index ce6de99c1edd..e83eca2a5e14 100644 --- a/configure.ac +++ b/configure.ac @@ -199,6 +199,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 @@ -232,6 +247,7 @@ AC_CONFIG_FILES([Makefile bindings/python/Makefile bindings/python/examples/Makefile bindings/python/tests/Makefile + bindings/rust/Makefile man/Makefile]) AC_OUTPUT