From patchwork Fri Jul 8 11:34:57 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 1654079 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: bilbo.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=linaro.org header.i=@linaro.org header.a=rsa-sha256 header.s=google header.b=hTX9sIb0; dkim-atps=neutral Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org (client-ip=2620:137:e000::1:20; helo=out1.vger.email; envelope-from=linux-gpio-owner@vger.kernel.org; receiver=) Received: from out1.vger.email (out1.vger.email [IPv6:2620:137:e000::1:20]) by bilbo.ozlabs.org (Postfix) with ESMTP id 4LfWRZ6YD8z9s09 for ; Fri, 8 Jul 2022 21:35:22 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237694AbiGHLfW (ORCPT ); Fri, 8 Jul 2022 07:35:22 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33624 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237365AbiGHLfU (ORCPT ); Fri, 8 Jul 2022 07:35:20 -0400 Received: from mail-pg1-x533.google.com (mail-pg1-x533.google.com [IPv6:2607:f8b0:4864:20::533]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id CD63F205C4 for ; Fri, 8 Jul 2022 04:35:17 -0700 (PDT) Received: by mail-pg1-x533.google.com with SMTP id g4so22151307pgc.1 for ; Fri, 08 Jul 2022 04:35:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=yaJ7M9xEih1NlLzmbZYxOftLVrTwB5WYxjjCMOrFf20=; b=hTX9sIb0lUXIou6u4nKGwEfMXJ196fx5ASZL0oFn+Rczk9DYDjcJLSRZiR/ghPtakk HBr6YDQuOIFOFRgsTDhzPQvA0f/T+2ZZtRuryM48Nxm7rg4JdbryJh0jZm1yu1XSFbZK bdtRSjk0x9N+9lp7sgUVk14UYTcphcanJtJttHn2jQ8U1r1xCBuM0T4cwaN3mC3epBQW 2poPv2AgwJzhdXzRNO8S1B7IVs6jrdAX5TzWp/p+TWAEtBDwbLGy4rUudRYDbDG/DNie FBC4rnARmmvHoXpuNLkIyjdkIKAsWLqb7Sc5IKgV3widxTffbjPGFoDkzOEjGwRaJKrL Tj4w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=yaJ7M9xEih1NlLzmbZYxOftLVrTwB5WYxjjCMOrFf20=; b=Fhg2CM9kUyI0aCSTE5rpGH8DlNSEsJwhk8359eeU3XWLDYbJe2UeTm1uRcGWuGlu/k pt3iwELwIWRotSJYx6cps+czDzcT50qEUzZpPKohX7aXgkriRD/ol9MJeE7/QT3KKava haKU1UZdd32iULtysjcQRjrUutZ4z/c/jjsBhxN4i+i3Bng5QZ3//H0FlWipdSPzu21d tZbKuEyVLPP5ZgbMVYDvQmaOXh4uDW+DViM3tBUEnbPxmu5oovcLcCseenbPmY3WX8nz jn4L6LqRlIDH+rtyRuJugcTvAKxk49k6+S1r1irqg6hhZA2Vx8e6HBQjcNF8rCvKwelp LB2w== X-Gm-Message-State: AJIora8O/OhmJP3vqHsb38/+RleVR7scyghY0S82CVeDsp4bjvaipvZ3 algO98vTRsdJ9Ex88xVAoGqxTQ== X-Google-Smtp-Source: AGRyM1sHZmod47icYCjgCKhvvjo+aVY3O0LwuSaE7zChokjljbkiESmEiI3qYcpvtTWrxy19En6pow== X-Received: by 2002:a63:5c5e:0:b0:412:a2f1:d0dd with SMTP id n30-20020a635c5e000000b00412a2f1d0ddmr2979617pgm.251.1657280116001; Fri, 08 Jul 2022 04:35:16 -0700 (PDT) Received: from localhost ([122.171.18.80]) by smtp.gmail.com with ESMTPSA id r3-20020a170903020300b0016a11b7472csm29533964plh.166.2022.07.08.04.35.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 08 Jul 2022 04:35:15 -0700 (PDT) From: Viresh Kumar To: Linus Walleij , Bartosz Golaszewski Cc: Viresh Kumar , Vincent Guittot , linux-gpio@vger.kernel.org, Kent Gibson , Miguel Ojeda , Wedson Almeida Filho , =?utf-8?q?Alex_Benn=C3=A9e?= , stratos-dev@op-lists.linaro.org, Gerard Ryan Subject: [PATCH V4 4/8] libgpiod: Add rust wrapper crate Date: Fri, 8 Jul 2022 17:04:57 +0530 Message-Id: X-Mailer: git-send-email 2.31.1.272.g89b43f80a514 In-Reply-To: References: MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_NONE, SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org Add rust wrapper crate, around the libpiod-sys crate added earlier, to provide a convenient interface for the users. Signed-off-by: Viresh Kumar --- bindings/rust/Cargo.toml | 12 + bindings/rust/src/chip.rs | 179 ++++++++++ bindings/rust/src/chip_info.rs | 69 ++++ bindings/rust/src/edge_event.rs | 105 ++++++ bindings/rust/src/event_buffer.rs | 88 +++++ bindings/rust/src/info_event.rs | 68 ++++ bindings/rust/src/lib.rs | 317 ++++++++++++++++++ bindings/rust/src/line_config.rs | 493 ++++++++++++++++++++++++++++ bindings/rust/src/line_info.rs | 189 +++++++++++ bindings/rust/src/line_request.rs | 248 ++++++++++++++ bindings/rust/src/request_config.rs | 122 +++++++ 11 files changed, 1890 insertions(+) create mode 100644 bindings/rust/Cargo.toml create mode 100644 bindings/rust/src/chip.rs create mode 100644 bindings/rust/src/chip_info.rs create mode 100644 bindings/rust/src/edge_event.rs create mode 100644 bindings/rust/src/event_buffer.rs create mode 100644 bindings/rust/src/info_event.rs create mode 100644 bindings/rust/src/lib.rs create mode 100644 bindings/rust/src/line_config.rs create mode 100644 bindings/rust/src/line_info.rs create mode 100644 bindings/rust/src/line_request.rs create mode 100644 bindings/rust/src/request_config.rs diff --git a/bindings/rust/Cargo.toml b/bindings/rust/Cargo.toml new file mode 100644 index 000000000000..d5d81486fa2f --- /dev/null +++ b/bindings/rust/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "libgpiod" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +libc = ">=0.2.39" +libgpiod-sys = { path = "libgpiod-sys" } +thiserror = "1.0" +vmm-sys-util = "=0.9.0" diff --git a/bindings/rust/src/chip.rs b/bindings/rust/src/chip.rs new file mode 100644 index 000000000000..50b5d6102f96 --- /dev/null +++ b/bindings/rust/src/chip.rs @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use std::os::raw::c_char; +use std::sync::Arc; +use std::time::Duration; +use std::{slice, str}; + +use vmm_sys_util::errno::Error as IoError; + +use super::{ + bindings, chip_info::ChipInfo, Error, InfoEvent, LineConfig, LineInfo, LineRequest, + RequestConfig, Result, +}; + +/// GPIO chip +/// +/// A GPIO chip object is associated with an open file descriptor to the GPIO +/// character device. It exposes basic information about the chip and allows +/// callers to retrieve information about each line, watch lines for state +/// changes and make line requests. +pub(crate) struct ChipInternal { + chip: *mut bindings::gpiod_chip, +} + +impl ChipInternal { + /// Find a chip by path. + pub(crate) fn open(path: &str) -> Result { + // Null-terminate the string + let path = path.to_owned() + "\0"; + + let chip = unsafe { bindings::gpiod_chip_open(path.as_ptr() as *const c_char) }; + if chip.is_null() { + return Err(Error::OperationFailed("Gpio Chip open", IoError::last())); + } + + Ok(Self { chip }) + } + + /// Private helper, Returns gpiod_chip + pub(crate) fn chip(&self) -> *mut bindings::gpiod_chip { + self.chip + } +} + +impl Drop for ChipInternal { + /// Close the chip and release all associated resources. + fn drop(&mut self) { + unsafe { bindings::gpiod_chip_close(self.chip) } + } +} + +pub struct Chip { + ichip: Arc, + info: ChipInfo, +} + +impl Chip { + /// Find a chip by path. + pub fn open(path: &str) -> Result { + let ichip = Arc::new(ChipInternal::open(path)?); + let info = ChipInfo::new(ichip.clone())?; + + Ok(Self { ichip, info }) + } + + /// Get the chip name as represented in the kernel. + pub fn get_name(&self) -> Result<&str> { + self.info.name() + } + + /// Get the chip label as represented in the kernel. + pub fn get_label(&self) -> Result<&str> { + self.info.label() + } + + /// Get the number of GPIO lines exposed by the chip. + pub fn get_num_lines(&self) -> u32 { + self.info.num_lines() + } + + /// Get the path used to find the chip. + pub fn get_path(&self) -> Result<&str> { + // SAFETY: The string returned by libgpiod is guaranteed to live as long + // as the `struct Chip`. + let path = unsafe { bindings::gpiod_chip_get_path(self.ichip.chip()) }; + + // SAFETY: The string is guaranteed to be valid here. + str::from_utf8(unsafe { + slice::from_raw_parts(path as *const u8, bindings::strlen(path) as usize) + }) + .map_err(Error::InvalidString) + } + + /// Get information about the chip. + pub fn info(&self) -> Result { + ChipInfo::new(self.ichip.clone()) + } + + /// Get a snapshot of information about the line. + pub fn line_info(&self, offset: u32) -> Result { + LineInfo::new(self.ichip.clone(), offset, false) + } + + /// Get the current snapshot of information about the line at given offset + /// and optionally start watching it for future changes. + pub fn watch_line_info(&self, offset: u32) -> Result { + LineInfo::new(self.ichip.clone(), offset, true) + } + + /// Get the file descriptor associated with the chip. + /// + /// The returned file descriptor must not be closed by the caller, else other methods for the + /// `struct Chip` may fail. + pub fn get_fd(&self) -> Result { + let fd = unsafe { bindings::gpiod_chip_get_fd(self.ichip.chip()) }; + + if fd < 0 { + Err(Error::OperationFailed("Gpio Chip get-fd", IoError::last())) + } else { + Ok(fd as u32) + } + } + + /// Wait for line status events on any of the watched lines on the chip. + pub fn wait_info_event(&self, timeout: Duration) -> Result<()> { + let ret = unsafe { + bindings::gpiod_chip_wait_info_event(self.ichip.chip(), timeout.as_nanos() as i64) + }; + + match ret { + -1 => Err(Error::OperationFailed( + "Gpio Chip info-event-wait", + IoError::last(), + )), + 0 => Err(Error::OperationTimedOut), + _ => Ok(()), + } + } + + /// Read a single line status change event from the chip. If no events are + /// pending, this function will block. + pub fn read_info_event(&self) -> Result { + InfoEvent::new(&self.ichip) + } + + /// Map a GPIO line's name to its offset within the chip. + pub fn find_line(&self, name: &str) -> Result { + // Null-terminate the string + let name = name.to_owned() + "\0"; + + let ret = unsafe { + bindings::gpiod_chip_get_line_offset_from_name( + self.ichip.chip(), + name.as_ptr() as *const c_char, + ) + }; + + if ret == -1 { + Err(Error::OperationFailed( + "Gpio Chip find-line", + IoError::last(), + )) + } else { + Ok(ret as u32) + } + } + + /// Request a set of lines for exclusive usage. + pub fn request_lines( + &self, + rconfig: &RequestConfig, + lconfig: &LineConfig, + ) -> Result { + LineRequest::new(&self.ichip, rconfig, lconfig) + } +} diff --git a/bindings/rust/src/chip_info.rs b/bindings/rust/src/chip_info.rs new file mode 100644 index 000000000000..950368b54c6f --- /dev/null +++ b/bindings/rust/src/chip_info.rs @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use std::sync::Arc; +use std::{slice, str}; + +use vmm_sys_util::errno::Error as IoError; + +use super::{bindings, ChipInternal, Error, Result}; + +/// GPIO chip Information +pub struct ChipInfo { + info: *mut bindings::gpiod_chip_info, +} + +impl ChipInfo { + /// Find a GPIO chip by path. + pub(crate) fn new(chip: Arc) -> Result { + let info = unsafe { bindings::gpiod_chip_get_info(chip.chip()) }; + if info.is_null() { + return Err(Error::OperationFailed( + "Gpio Chip get info", + IoError::last(), + )); + } + + Ok(Self { info }) + } + + /// Get the GPIO chip name as represented in the kernel. + pub(crate) fn name(&self) -> Result<&str> { + // SAFETY: The string returned by libgpiod is guaranteed to live as long + // as the `struct Chip`. + let name = unsafe { bindings::gpiod_chip_info_get_name(self.info) }; + + // SAFETY: The string is guaranteed to be valid here. + str::from_utf8(unsafe { + slice::from_raw_parts(name as *const u8, bindings::strlen(name) as usize) + }) + .map_err(Error::InvalidString) + } + + /// Get the GPIO chip label as represented in the kernel. + pub(crate) fn label(&self) -> Result<&str> { + // SAFETY: The string returned by libgpiod is guaranteed to live as long + // as the `struct Chip`. + let label = unsafe { bindings::gpiod_chip_info_get_label(self.info) }; + + // SAFETY: The string is guaranteed to be valid here. + str::from_utf8(unsafe { + slice::from_raw_parts(label as *const u8, bindings::strlen(label) as usize) + }) + .map_err(Error::InvalidString) + } + + /// Get the number of GPIO lines exposed by the chip. + pub(crate) fn num_lines(&self) -> u32 { + unsafe { bindings::gpiod_chip_info_get_num_lines(self.info) as u32 } + } +} + +impl Drop for ChipInfo { + /// Close the GPIO chip info and release all associated resources. + fn drop(&mut self) { + unsafe { bindings::gpiod_chip_info_free(self.info) } + } +} diff --git a/bindings/rust/src/edge_event.rs b/bindings/rust/src/edge_event.rs new file mode 100644 index 000000000000..89bda58709d2 --- /dev/null +++ b/bindings/rust/src/edge_event.rs @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use std::sync::Arc; +use std::time::Duration; + +use vmm_sys_util::errno::Error as IoError; + +use super::{bindings, EdgeEventBufferInternal, Error, LineEdgeEvent, Result}; + +/// Line edge events handling +/// +/// An edge event object contains information about a single line edge event. +/// It contains the event type, timestamp and the offset of the line on which +/// the event occurred as well as two sequence numbers (global for all lines +/// in the associated request and local for this line only). +/// +/// Edge events are stored into an edge-event buffer object to improve +/// performance and to limit the number of memory allocations when a large +/// number of events are being read. + +pub struct EdgeEvent { + ibuffer: Option>, + event: *mut bindings::gpiod_edge_event, +} + +impl EdgeEvent { + /// Get an event stored in the buffer. + pub(crate) fn new( + ibuffer: &Arc, + index: u64, + copy: bool, + ) -> Result { + let event = unsafe { bindings::gpiod_edge_event_buffer_get_event(ibuffer.buffer(), index) }; + if event.is_null() { + return Err(Error::OperationFailed( + "Gpio EdgeEvent buffer-get-event", + IoError::last(), + )); + } + + if copy { + let event = unsafe { bindings::gpiod_edge_event_copy(event) }; + if event.is_null() { + return Err(Error::OperationFailed( + "Gpio EdgeEvent copy", + IoError::last(), + )); + } + + Ok(Self { + ibuffer: None, + event, + }) + } else { + Ok(Self { + ibuffer: Some(ibuffer.clone()), + event, + }) + } + } + + /// Get the event type. + pub fn get_event_type(&self) -> Result { + LineEdgeEvent::new(unsafe { bindings::gpiod_edge_event_get_event_type(self.event) } as u32) + } + + /// Get the timestamp of the event. + pub fn get_timestamp(&self) -> Duration { + Duration::from_nanos(unsafe { bindings::gpiod_edge_event_get_timestamp_ns(self.event) }) + } + + /// Get the offset of the line on which the event was triggered. + pub fn get_line_offset(&self) -> u32 { + unsafe { bindings::gpiod_edge_event_get_line_offset(self.event) } + } + + /// Get the global sequence number of the event. + /// + /// Returns sequence number of the event relative to all lines in the + /// associated line request. + pub fn get_global_seqno(&self) -> u64 { + unsafe { bindings::gpiod_edge_event_get_global_seqno(self.event) } + } + + /// Get the event sequence number specific to concerned line. + /// + /// Returns sequence number of the event relative to the line within the + /// lifetime of the associated line request. + pub fn get_line_seqno(&self) -> u64 { + unsafe { bindings::gpiod_edge_event_get_line_seqno(self.event) } + } +} + +impl Drop for EdgeEvent { + /// Free the edge event. + fn drop(&mut self) { + // Free the event only if a copy is made + if self.ibuffer.is_none() { + unsafe { bindings::gpiod_edge_event_free(self.event) }; + } + } +} diff --git a/bindings/rust/src/event_buffer.rs b/bindings/rust/src/event_buffer.rs new file mode 100644 index 000000000000..bb568a80e09c --- /dev/null +++ b/bindings/rust/src/event_buffer.rs @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use std::os::raw::c_ulong; +use std::sync::Arc; + +use vmm_sys_util::errno::Error as IoError; + +use super::{bindings, EdgeEvent, Error, Result}; + +/// Line edge events buffer +pub(crate) struct EdgeEventBufferInternal { + buffer: *mut bindings::gpiod_edge_event_buffer, +} + +impl EdgeEventBufferInternal { + /// Create a new edge event buffer. + /// + /// If capacity equals 0, it will be set to a default value of 64. If + /// capacity is larger than 1024, it will be limited to 1024. + pub fn new(capacity: u32) -> Result { + let buffer = unsafe { bindings::gpiod_edge_event_buffer_new(capacity as c_ulong) }; + if buffer.is_null() { + return Err(Error::OperationFailed( + "Gpio EdgeEventBuffer new", + IoError::last(), + )); + } + + Ok(Self { buffer }) + } + + /// Private helper, Returns gpiod_edge_event_buffer + pub(crate) fn buffer(&self) -> *mut bindings::gpiod_edge_event_buffer { + self.buffer + } +} + +impl Drop for EdgeEventBufferInternal { + /// Free the edge event buffer and release all associated resources. + fn drop(&mut self) { + unsafe { bindings::gpiod_edge_event_buffer_free(self.buffer) }; + } +} + +/// Line edge events buffer +pub struct EdgeEventBuffer { + ibuffer: Arc, +} + +impl EdgeEventBuffer { + /// Create a new edge event buffer. + /// + /// If capacity equals 0, it will be set to a default value of 64. If + /// capacity is larger than 1024, it will be limited to 1024. + pub fn new(capacity: u32) -> Result { + Ok(Self { + ibuffer: Arc::new(EdgeEventBufferInternal::new(capacity)?), + }) + } + + /// Private helper, Returns gpiod_edge_event_buffer + pub(crate) fn buffer(&self) -> *mut bindings::gpiod_edge_event_buffer { + self.ibuffer.buffer() + } + + /// Get the capacity of the event buffer. + pub fn get_capacity(&self) -> u32 { + unsafe { bindings::gpiod_edge_event_buffer_get_capacity(self.buffer()) as u32 } + } + + /// Read an event stored in the buffer. + pub fn get_event(&self, index: u64) -> Result { + EdgeEvent::new(&self.ibuffer, index, false) + } + + /// Make copy of an edge event stored in the buffer. + pub fn get_event_copy(&self, index: u64) -> Result { + EdgeEvent::new(&self.ibuffer, index, true) + } + + /// Get the number of events the buffers stores. + pub fn get_num_events(&self) -> u32 { + unsafe { bindings::gpiod_edge_event_buffer_get_num_events(self.buffer()) as u32 } + } +} diff --git a/bindings/rust/src/info_event.rs b/bindings/rust/src/info_event.rs new file mode 100644 index 000000000000..ffba23f350be --- /dev/null +++ b/bindings/rust/src/info_event.rs @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use std::convert::TryFrom; +use std::sync::Arc; +use std::time::Duration; + +use vmm_sys_util::errno::Error as IoError; + +use super::{bindings, ChipInternal, Error, Event, LineInfo, Result}; + +/// Line status watch events +/// +/// Accessors for the info event objects allowing to monitor changes in GPIO +/// line state. +/// +/// Callers can be notified about changes in line's state using the interfaces +/// exposed by GPIO chips. Each info event contains information about the event +/// itself (timestamp, type) as well as a snapshot of line's state in the form +/// of a line-info object. + +pub struct InfoEvent { + event: *mut bindings::gpiod_info_event, +} + +impl InfoEvent { + /// Get a single chip's line's status change event. + pub(crate) fn new(ichip: &Arc) -> Result { + let event = unsafe { bindings::gpiod_chip_read_info_event(ichip.chip()) }; + if event.is_null() { + return Err(Error::OperationFailed( + "Gpio InfoEvent event-read", + IoError::last(), + )); + } + + Ok(Self { event }) + } + + /// Private helper, Returns gpiod_info_event + pub(crate) fn event(&self) -> *mut bindings::gpiod_info_event { + self.event + } + + /// Get the event type of the status change event. + pub fn get_event_type(&self) -> Result { + Event::new(unsafe { bindings::gpiod_info_event_get_event_type(self.event) } as u32) + } + + /// Get the timestamp of the event, read from the monotonic clock. + pub fn get_timestamp(&self) -> Duration { + Duration::from_nanos(unsafe { bindings::gpiod_info_event_get_timestamp_ns(self.event) }) + } + + /// Get the line-info object associated with the event. + pub fn line_info(&self) -> Result { + LineInfo::try_from(self) + } +} + +impl Drop for InfoEvent { + /// Free the info event object and release all associated resources. + fn drop(&mut self) { + unsafe { bindings::gpiod_info_event_free(self.event) } + } +} diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs new file mode 100644 index 000000000000..2f2ac515d353 --- /dev/null +++ b/bindings/rust/src/lib.rs @@ -0,0 +1,317 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Rust wrappers for GPIOD APIs +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +//! libgpiod public API +//! +//! This is the complete documentation of the public Rust API made available to +//! users of libgpiod. +//! +//! The API is logically split into several parts such as: GPIO chip & line +//! operators, GPIO events handling etc. + +mod chip; +mod chip_info; +mod edge_event; +mod event_buffer; +mod info_event; +mod line_config; +mod line_info; +mod line_request; +mod request_config; + +use libgpiod_sys as bindings; + +pub use crate::chip::*; +pub use crate::edge_event::*; +pub use crate::event_buffer::*; +pub use crate::info_event::*; +pub use crate::line_config::*; +pub use crate::line_info::*; +pub use crate::line_request::*; +pub use crate::request_config::*; + +use std::os::raw::c_char; +use std::{slice, str}; + +use thiserror::Error as ThisError; +use vmm_sys_util::errno::Error as IoError; + +/// Result of libgpiod operations +pub type Result = std::result::Result; + +/// Error codes for libgpiod operations +#[derive(Copy, Clone, Debug, PartialEq, ThisError)] +pub enum Error { + #[error("Failed to find {0}")] + NameNotFound(&'static str), + #[error("Invalid String: {0:?}")] + InvalidString(str::Utf8Error), + #[error("Invalid {0} value: {1}")] + InvalidValue(&'static str, u32), + #[error("Operation {0} Failed: {1}")] + OperationFailed(&'static str, IoError), + #[error("Operation Timed-out")] + OperationTimedOut, +} + +/// Direction settings. +pub enum Direction { + /// Request the line(s), but don't change direction. + AsIs, + /// Direction is input - for reading the value of an externally driven GPIO line. + Input, + /// Direction is output - for driving the GPIO line. + Output, +} + +impl Direction { + fn new(dir: u32) -> Result { + match dir { + bindings::GPIOD_LINE_DIRECTION_AS_IS => Ok(Direction::AsIs), + bindings::GPIOD_LINE_DIRECTION_INPUT => Ok(Direction::Input), + bindings::GPIOD_LINE_DIRECTION_OUTPUT => Ok(Direction::Output), + _ => Err(Error::InvalidValue("direction", dir)), + } + } + + fn gpiod_direction(&self) -> u32 { + match self { + Direction::AsIs => bindings::GPIOD_LINE_DIRECTION_AS_IS, + Direction::Input => bindings::GPIOD_LINE_DIRECTION_INPUT, + Direction::Output => bindings::GPIOD_LINE_DIRECTION_OUTPUT, + } + } +} + +/// Internal bias settings. +pub enum Bias { + /// Don't change the bias setting when applying line config. + AsIs, + /// The internal bias state is unknown. + Unknown, + /// The internal bias is disabled. + Disabled, + /// The internal pull-up bias is enabled. + PullUp, + /// The internal pull-down bias is enabled. + PullDown, +} + +impl Bias { + fn new(bias: u32) -> Result { + match bias { + bindings::GPIOD_LINE_BIAS_AS_IS => Ok(Bias::AsIs), + bindings::GPIOD_LINE_BIAS_UNKNOWN => Ok(Bias::Unknown), + bindings::GPIOD_LINE_BIAS_DISABLED => Ok(Bias::Disabled), + bindings::GPIOD_LINE_BIAS_PULL_UP => Ok(Bias::PullUp), + bindings::GPIOD_LINE_BIAS_PULL_DOWN => Ok(Bias::PullDown), + _ => Err(Error::InvalidValue("bias", bias)), + } + } + + fn gpiod_bias(&self) -> u32 { + match self { + Bias::AsIs => bindings::GPIOD_LINE_BIAS_AS_IS, + Bias::Unknown => bindings::GPIOD_LINE_BIAS_UNKNOWN, + Bias::Disabled => bindings::GPIOD_LINE_BIAS_DISABLED, + Bias::PullUp => bindings::GPIOD_LINE_BIAS_PULL_UP, + Bias::PullDown => bindings::GPIOD_LINE_BIAS_PULL_DOWN, + } + } +} + +/// Drive settings. +pub enum Drive { + /// Drive setting is push-pull. + PushPull, + /// Line output is open-drain. + OpenDrain, + /// Line output is open-source. + OpenSource, +} + +impl Drive { + fn new(drive: u32) -> Result { + match drive { + bindings::GPIOD_LINE_DRIVE_PUSH_PULL => Ok(Drive::PushPull), + bindings::GPIOD_LINE_DRIVE_OPEN_DRAIN => Ok(Drive::OpenDrain), + bindings::GPIOD_LINE_DRIVE_OPEN_SOURCE => Ok(Drive::OpenSource), + _ => Err(Error::InvalidValue("drive", drive)), + } + } + + fn gpiod_drive(&self) -> u32 { + match self { + Drive::PushPull => bindings::GPIOD_LINE_DRIVE_PUSH_PULL, + Drive::OpenDrain => bindings::GPIOD_LINE_DRIVE_OPEN_DRAIN, + Drive::OpenSource => bindings::GPIOD_LINE_DRIVE_OPEN_SOURCE, + } + } +} + +/// Edge detection settings. +pub enum Edge { + /// Line edge detection is disabled. + None, + /// Line detects rising edge events. + Rising, + /// Line detects falling edge events. + Falling, + /// Line detects both rising and falling edge events. + Both, +} + +impl Edge { + fn new(edge: u32) -> Result { + match edge { + bindings::GPIOD_LINE_EDGE_NONE => Ok(Edge::None), + bindings::GPIOD_LINE_EDGE_RISING => Ok(Edge::Rising), + bindings::GPIOD_LINE_EDGE_FALLING => Ok(Edge::Falling), + bindings::GPIOD_LINE_EDGE_BOTH => Ok(Edge::Both), + _ => Err(Error::InvalidValue("edge", edge)), + } + } + + fn gpiod_edge(&self) -> u32 { + match self { + Edge::None => bindings::GPIOD_LINE_EDGE_NONE, + Edge::Rising => bindings::GPIOD_LINE_EDGE_RISING, + Edge::Falling => bindings::GPIOD_LINE_EDGE_FALLING, + Edge::Both => bindings::GPIOD_LINE_EDGE_BOTH, + } + } +} + +/// Line config settings. +pub enum Config { + /// Line direction. + Direction, + /// Edge detection. + EdgeDetection, + /// Bias. + Bias, + /// Drive. + Drive, + /// Active-low setting. + ActiveLow, + /// Debounce period. + DebouncePeriodUs, + /// Event clock type. + EventClock, + /// Output value. + OutputValue, +} + +impl Config { + fn new(config: u32) -> Result { + match config { + bindings::GPIOD_LINE_CONFIG_PROP_DIRECTION => Ok(Config::Direction), + bindings::GPIOD_LINE_CONFIG_PROP_EDGE_DETECTION => Ok(Config::EdgeDetection), + bindings::GPIOD_LINE_CONFIG_PROP_BIAS => Ok(Config::Bias), + bindings::GPIOD_LINE_CONFIG_PROP_DRIVE => Ok(Config::Drive), + bindings::GPIOD_LINE_CONFIG_PROP_ACTIVE_LOW => Ok(Config::ActiveLow), + bindings::GPIOD_LINE_CONFIG_PROP_DEBOUNCE_PERIOD_US => Ok(Config::DebouncePeriodUs), + bindings::GPIOD_LINE_CONFIG_PROP_EVENT_CLOCK => Ok(Config::EventClock), + bindings::GPIOD_LINE_CONFIG_PROP_OUTPUT_VALUE => Ok(Config::OutputValue), + _ => Err(Error::InvalidValue("config", config)), + } + } +} + +/// Event clock settings. +pub enum EventClock { + /// Line uses the monotonic clock for edge event timestamps. + Monotonic, + /// Line uses the realtime clock for edge event timestamps. + Realtime, +} + +impl EventClock { + fn new(clock: u32) -> Result { + match clock { + bindings::GPIOD_LINE_EVENT_CLOCK_MONOTONIC => Ok(EventClock::Monotonic), + bindings::GPIOD_LINE_EVENT_CLOCK_REALTIME => Ok(EventClock::Realtime), + _ => Err(Error::InvalidValue("event clock", clock)), + } + } + + fn gpiod_clock(&self) -> u32 { + match self { + EventClock::Monotonic => bindings::GPIOD_LINE_EVENT_CLOCK_MONOTONIC, + EventClock::Realtime => bindings::GPIOD_LINE_EVENT_CLOCK_REALTIME, + } + } +} + +/// Line status change event types. +pub enum Event { + /// Line has been requested. + LineRequested, + /// Previously requested line has been released. + LineReleased, + /// Line configuration has changed. + LineConfigChanged, +} + +impl Event { + fn new(event: u32) -> Result { + match event { + bindings::GPIOD_INFO_EVENT_LINE_REQUESTED => Ok(Event::LineRequested), + bindings::GPIOD_INFO_EVENT_LINE_RELEASED => Ok(Event::LineReleased), + bindings::GPIOD_INFO_EVENT_LINE_CONFIG_CHANGED => Ok(Event::LineConfigChanged), + _ => Err(Error::InvalidValue("event", event)), + } + } +} + +#[derive(Copy, Clone)] +/// Edge event types. +pub enum LineEdgeEvent { + /// Rising edge event. + Rising, + /// Falling edge event. + Falling, +} + +impl LineEdgeEvent { + fn new(event: u32) -> Result { + match event { + bindings::GPIOD_EDGE_EVENT_RISING_EDGE => Ok(LineEdgeEvent::Rising), + bindings::GPIOD_EDGE_EVENT_FALLING_EDGE => Ok(LineEdgeEvent::Falling), + _ => Err(Error::InvalidValue("edge event", event)), + } + } +} + +/// Various libgpiod-related functions. + +/// Check if the file pointed to by path is a GPIO chip character device. +/// +/// Returns true if the file exists and is a GPIO chip character device or a +/// symbolic link to it. +pub fn gpiod_is_gpiochip_device(path: &str) -> bool { + // Null-terminate the string + let path = path.to_owned() + "\0"; + + unsafe { bindings::gpiod_is_gpiochip_device(path.as_ptr() as *const c_char) } +} + +/// Get the API version of the library as a human-readable string. +pub fn gpiod_version_string() -> Result<&'static str> { + // SAFETY: The string returned by libgpiod is guaranteed to live forever. + let version = unsafe { bindings::gpiod_version_string() }; + + if version.is_null() { + return Err(Error::NameNotFound("GPIO library version")); + } + + // SAFETY: The string is guaranteed to be valid here. + str::from_utf8(unsafe { + slice::from_raw_parts(version as *const u8, bindings::strlen(version) as usize) + }) + .map_err(Error::InvalidString) +} diff --git a/bindings/rust/src/line_config.rs b/bindings/rust/src/line_config.rs new file mode 100644 index 000000000000..0fc8d0b736a3 --- /dev/null +++ b/bindings/rust/src/line_config.rs @@ -0,0 +1,493 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use libc::EINVAL; +use std::os::raw::c_ulong; +use std::time::Duration; + +use vmm_sys_util::errno::Error as IoError; + +use super::{bindings, Bias, Config, Direction, Drive, Edge, Error, EventClock, Result}; + +/// Line configuration objects. +/// +/// The line-config object contains the configuration for lines that can be +/// used in two cases: +/// - when making a line request +/// - when reconfiguring a set of already requested lines. +/// +/// A new line-config object is instantiated with a set of sane defaults +/// for all supported configuration settings. Those defaults can be modified by +/// the caller. Default values can be overridden by applying different values +/// for specific lines. When making a request or reconfiguring an existing one, +/// the overridden settings for specific lines take precedence. For lines +/// without an override the requested default settings are used. +/// +/// For every setting there are two mutators (one setting the default and one +/// for the per-line override), two getters (one for reading the global +/// default and one for retrieving the effective value for the line), +/// a function for testing if a setting is overridden for the line +/// and finally a function for clearing the overrides (per line). +/// +/// The mutators don't return errors. If the set of options is too complex to +/// be translated into kernel uAPI structures then an error will be returned at +/// the time of the request or reconfiguration. If an invalid value was passed +/// to any of the mutators then the default value will be silently used instead. +/// +/// Operating on lines in struct LineConfig has no immediate effect on real +/// GPIOs, it only manipulates the config object in memory. Those changes are +/// only applied to the hardware at the time of the request or reconfiguration. +/// +/// Overrides for lines that don't end up being requested are silently ignored +/// both in LineRequest::new() as well as in LineRequest::reconfigure_lines(). +/// +/// In cases where all requested lines are using the one configuration, the +/// line overrides can be entirely ignored when preparing the configuration. + +pub struct LineConfig { + config: *mut bindings::gpiod_line_config, +} + +impl LineConfig { + /// Create a new line config object. + pub fn new() -> Result { + let config = unsafe { bindings::gpiod_line_config_new() }; + + if config.is_null() { + return Err(Error::OperationFailed( + "Gpio LineConfig new", + IoError::last(), + )); + } + + Ok(Self { config }) + } + + /// Private helper, Returns gpiod_line_config + pub(crate) fn config(&self) -> *mut bindings::gpiod_line_config { + self.config + } + + /// Resets the entire configuration stored in the object. This is useful if + /// the user wants to reuse the object without reallocating it. + pub fn reset(&mut self) { + unsafe { bindings::gpiod_line_config_reset(self.config) } + } + + /// Set the default line direction. + pub fn set_direction_default(&mut self, direction: Direction) { + unsafe { + bindings::gpiod_line_config_set_direction_default( + self.config, + direction.gpiod_direction() as i32, + ) + } + } + + /// Set the direction for a line. + pub fn set_direction_override(&mut self, direction: Direction, offset: u32) { + unsafe { + bindings::gpiod_line_config_set_direction_override( + self.config, + direction.gpiod_direction() as i32, + offset, + ) + } + } + + /// Clear the direction for a line. + pub fn clear_direction_override(&mut self, offset: u32) { + unsafe { bindings::gpiod_line_config_clear_direction_override(self.config, offset) } + } + + /// Check if the direction is overridden for a line. + pub fn direction_is_overridden(&self, offset: u32) -> bool { + unsafe { bindings::gpiod_line_config_direction_is_overridden(self.config, offset) } + } + + /// Get the default direction setting. + pub fn get_direction_default(&self) -> Result { + Direction::new( + unsafe { bindings::gpiod_line_config_get_direction_default(self.config) } as u32, + ) + } + + /// Get the direction of a given line. + /// + /// Direction setting for the line if the config object were used in a request. + pub fn get_direction_offset(&self, offset: u32) -> Result { + Direction::new(unsafe { + bindings::gpiod_line_config_get_direction_offset(self.config, offset) + } as u32) + } + + /// Set the default edge event detection setting. + pub fn set_edge_detection_default(&mut self, edge: Edge) { + unsafe { + bindings::gpiod_line_config_set_edge_detection_default( + self.config, + edge.gpiod_edge() as i32, + ) + } + } + + /// Set the edge event detection for a single line. + pub fn set_edge_detection_override(&mut self, edge: Edge, offset: u32) { + unsafe { + bindings::gpiod_line_config_set_edge_detection_override( + self.config, + edge.gpiod_edge() as i32, + offset, + ) + } + } + + /// Clear the edge event detection for a single line. + pub fn clear_edge_detection_override(&mut self, offset: u32) { + unsafe { bindings::gpiod_line_config_clear_edge_detection_override(self.config, offset) } + } + + /// Check if the edge event detection is overridden for a line. + pub fn edge_detection_is_overridden(&self, offset: u32) -> bool { + unsafe { bindings::gpiod_line_config_edge_detection_is_overridden(self.config, offset) } + } + + /// Get the default edge event detection setting. + pub fn get_edge_detection_default(&self) -> Result { + Edge::new( + unsafe { bindings::gpiod_line_config_get_edge_detection_default(self.config) } as u32, + ) + } + + /// Get the edge event detection setting for a given line. + /// + /// Edge event detection setting for the line if the config object were used in a request. + pub fn get_edge_detection_offset(&self, offset: u32) -> Result { + Edge::new(unsafe { + bindings::gpiod_line_config_get_edge_detection_offset(self.config, offset) + } as u32) + } + + /// Set the default bias setting. + pub fn set_bias_default(&mut self, bias: Bias) { + unsafe { + bindings::gpiod_line_config_set_bias_default(self.config, bias.gpiod_bias() as i32) + } + } + + /// Set the bias for a single line. + pub fn set_bias_override(&mut self, bias: Bias, offset: u32) { + unsafe { + bindings::gpiod_line_config_set_bias_override( + self.config, + bias.gpiod_bias() as i32, + offset, + ) + } + } + + /// Clear the bias for a single line. + pub fn clear_bias_override(&mut self, offset: u32) { + unsafe { bindings::gpiod_line_config_clear_bias_override(self.config, offset) } + } + + /// Check if the bias is overridden for a line. + pub fn bias_is_overridden(&self, offset: u32) -> bool { + unsafe { bindings::gpiod_line_config_bias_is_overridden(self.config, offset) } + } + + /// Get the default bias setting. + pub fn get_bias_default(&self) -> Result { + Bias::new(unsafe { bindings::gpiod_line_config_get_bias_default(self.config) } as u32) + } + + /// Get the bias setting for a given line. + /// + /// Bias setting used for the line if the config object were used in a request. + pub fn get_bias_offset(&self, offset: u32) -> Result { + Bias::new( + unsafe { bindings::gpiod_line_config_get_bias_offset(self.config, offset) } as u32, + ) + } + + /// Set the default drive setting. + pub fn set_drive_default(&mut self, drive: Drive) { + unsafe { + bindings::gpiod_line_config_set_drive_default(self.config, drive.gpiod_drive() as i32) + } + } + + /// Set the drive for a single line. + pub fn set_drive_override(&mut self, drive: Drive, offset: u32) { + unsafe { + bindings::gpiod_line_config_set_drive_override( + self.config, + drive.gpiod_drive() as i32, + offset, + ) + } + } + + /// clear the drive for a single line. + pub fn clear_drive_override(&mut self, offset: u32) { + unsafe { bindings::gpiod_line_config_clear_drive_override(self.config, offset) } + } + + /// Check if the drive is overridden for a line. + pub fn drive_is_overridden(&self, offset: u32) -> bool { + unsafe { bindings::gpiod_line_config_drive_is_overridden(self.config, offset) } + } + + /// Get the default drive setting. + pub fn get_drive_default(&self) -> Result { + Drive::new(unsafe { bindings::gpiod_line_config_get_drive_default(self.config) } as u32) + } + + /// Get the drive setting for a given line. + /// + /// The offset of the line for which to read the drive setting. Drive setting for the line if + /// the config object were used in a request. + pub fn get_drive_offset(&self, offset: u32) -> Result { + Drive::new( + unsafe { bindings::gpiod_line_config_get_drive_offset(self.config, offset) } as u32, + ) + } + + /// Set default active-low setting. + pub fn set_active_low_default(&mut self, active_low: bool) { + unsafe { bindings::gpiod_line_config_set_active_low_default(self.config, active_low) } + } + + /// Set active-low setting for a single line. + pub fn set_active_low_override(&mut self, active_low: bool, offset: u32) { + unsafe { + bindings::gpiod_line_config_set_active_low_override(self.config, active_low, offset) + } + } + + /// Clear a single line's active-low setting. + pub fn clear_active_low_override(&mut self, offset: u32) { + unsafe { bindings::gpiod_line_config_clear_active_low_override(self.config, offset) } + } + + /// Check if the active-low is overridden for a line. + pub fn active_low_is_overridden(&mut self, offset: u32) -> bool { + unsafe { bindings::gpiod_line_config_active_low_is_overridden(self.config, offset) } + } + + /// Check the default active-low setting. + pub fn get_active_low_default(&self) -> bool { + unsafe { bindings::gpiod_line_config_get_active_low_default(self.config) } + } + + /// Check the active-low setting of a line. + /// + /// Active-low setting for the line if the config object were used in a request. + pub fn get_active_low_offset(&self, offset: u32) -> bool { + unsafe { bindings::gpiod_line_config_get_active_low_offset(self.config, offset) } + } + + /// Set the deafult debounce period setting. + pub fn set_debounce_period_default(&mut self, period: Duration) { + unsafe { + bindings::gpiod_line_config_set_debounce_period_us_default( + self.config, + period.as_micros() as u64, + ) + } + } + + /// Set the debounce period for a single line. + pub fn set_debounce_period_override(&mut self, period: Duration, offset: u32) { + unsafe { + bindings::gpiod_line_config_set_debounce_period_us_override( + self.config, + period.as_micros() as u64, + offset, + ) + } + } + + /// Clear the debounce period for a single line. + pub fn clear_debounce_period_override(&mut self, offset: u32) { + unsafe { + bindings::gpiod_line_config_clear_debounce_period_us_override(self.config, offset) + } + } + + /// Check if the debounce period setting is overridden. + pub fn debounce_period_is_overridden(&self, offset: u32) -> bool { + unsafe { bindings::gpiod_line_config_debounce_period_us_is_overridden(self.config, offset) } + } + + /// Get the default debounce period. + pub fn get_debounce_period_default(&self) -> Result { + Ok(Duration::from_micros(unsafe { + bindings::gpiod_line_config_get_debounce_period_us_default(self.config) + })) + } + + /// Get the debounce period for a given line. + /// + /// Debounce period for the line if the config object were used in a request, 0 if debouncing + /// is disabled. + pub fn get_debounce_period_offset(&self, offset: u32) -> Result { + Ok(Duration::from_micros(unsafe { + bindings::gpiod_line_config_get_debounce_period_us_offset(self.config, offset) + })) + } + + /// Set the default event clock setting. + pub fn set_event_clock_default(&mut self, clock: EventClock) { + unsafe { + bindings::gpiod_line_config_set_event_clock_default( + self.config, + clock.gpiod_clock() as i32, + ) + } + } + + /// Set the event clock for a single line. + pub fn set_event_clock_override(&mut self, clock: EventClock, offset: u32) { + unsafe { + bindings::gpiod_line_config_set_event_clock_override( + self.config, + clock.gpiod_clock() as i32, + offset, + ) + } + } + + /// Clear the event clock for a single line. + pub fn clear_event_clock_override(&mut self, offset: u32) { + unsafe { bindings::gpiod_line_config_clear_event_clock_override(self.config, offset) } + } + + /// Check if the event clock is overridden for a line. + pub fn event_clock_is_overridden(&mut self, offset: u32) -> bool { + unsafe { bindings::gpiod_line_config_event_clock_is_overridden(self.config, offset) } + } + + /// Get the default event clock setting. + pub fn get_event_clock_default(&self) -> Result { + EventClock::new( + unsafe { bindings::gpiod_line_config_get_event_clock_default(self.config) } as u32, + ) + } + + /// Get the event clock setting for a given line. + /// + /// Event clock setting for the line if the config object were used in a request. + pub fn get_event_clock_offset(&self, offset: u32) -> Result { + EventClock::new(unsafe { + bindings::gpiod_line_config_get_event_clock_offset(self.config, offset) + } as u32) + } + + /// Set the default output value setting. + pub fn set_output_value_default(&mut self, value: u32) { + unsafe { bindings::gpiod_line_config_set_output_value_default(self.config, value as i32) } + } + + /// Set the output value for a line. + pub fn set_output_value_override(&mut self, value: u32, offset: u32) { + unsafe { + bindings::gpiod_line_config_set_output_value_override(self.config, value as i32, offset) + } + } + + /// Set the output values for a set of lines. + pub fn set_output_values(&mut self, offsets: &[u32], values: &[i32]) -> Result<()> { + if offsets.len() != values.len() { + return Err(Error::OperationFailed( + "Gpio LineConfig array size mismatch", + IoError::new(EINVAL), + )); + } + + unsafe { + bindings::gpiod_line_config_set_output_values( + self.config, + values.len() as c_ulong, + offsets.as_ptr(), + values.as_ptr(), + ); + } + + Ok(()) + } + + /// Clear the output value for a line. + pub fn clear_output_value_override(&mut self, offset: u32) { + unsafe { bindings::gpiod_line_config_clear_output_value_override(self.config, offset) } + } + + /// Check if the output value is overridden for a line. + pub fn output_value_is_overridden(&self, offset: u32) -> bool { + unsafe { bindings::gpiod_line_config_output_value_is_overridden(self.config, offset) } + } + + /// Get the default output value, 0 or 1. + pub fn get_output_value_default(&self) -> Result { + let value = unsafe { bindings::gpiod_line_config_get_output_value_default(self.config) }; + + if value != 0 && value != 1 { + Err(Error::OperationFailed( + "Gpio LineConfig get-output-value", + IoError::last(), + )) + } else { + Ok(value as u32) + } + } + + /// Get the output value configured for a given line, 0 or 1. + pub fn get_output_value_offset(&self, offset: u32) -> Result { + let value = + unsafe { bindings::gpiod_line_config_get_output_value_offset(self.config, offset) }; + + if value != 0 && value != 1 { + Err(Error::OperationFailed( + "Gpio LineConfig get-output-value", + IoError::last(), + )) + } else { + Ok(value as u32) + } + } + + /// Get the list of overridden offsets and the corresponding types of overridden settings. + pub fn get_overrides(&self) -> Result> { + let num = unsafe { bindings::gpiod_line_config_get_num_overrides(self.config) } as usize; + if num == 0 { + return Ok(Vec::new()); + } + + let mut overrides = Vec::new(); + let mut offset = vec![0_u32; num]; + let mut props = vec![0_i32; num]; + + unsafe { + bindings::gpiod_line_config_get_overrides( + self.config, + offset.as_mut_ptr(), + props.as_mut_ptr(), + ) + }; + + for i in 0..num { + overrides.push((offset[i], Config::new(props[i] as u32)?)); + } + + Ok(overrides) + } +} + +impl Drop for LineConfig { + /// Free the line config object and release all associated resources. + fn drop(&mut self) { + unsafe { bindings::gpiod_line_config_free(self.config) } + } +} diff --git a/bindings/rust/src/line_info.rs b/bindings/rust/src/line_info.rs new file mode 100644 index 000000000000..70b6bd6a84bb --- /dev/null +++ b/bindings/rust/src/line_info.rs @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use std::convert::TryFrom; +use std::sync::Arc; +use std::time::Duration; +use std::{slice, str}; + +use vmm_sys_util::errno::Error as IoError; + +use super::{ + bindings, Bias, ChipInternal, Direction, Drive, Edge, Error, EventClock, InfoEvent, Result, +}; + +/// Line info +/// +/// Exposes functions for retrieving kernel information about both requested and +/// free lines. Line info object contains an immutable snapshot of a line's status. +/// +/// The line info contains all the publicly available information about a +/// line, which does not include the line value. The line must be requested +/// to access the line value. + +pub struct LineInfo { + info: *mut bindings::gpiod_line_info, + ichip: Option>, + free: bool, +} + +impl LineInfo { + /// Get a snapshot of information about the line and optionally start watching it for changes. + pub(crate) fn new(ichip: Arc, offset: u32, watch: bool) -> Result { + let info = if watch { + unsafe { bindings::gpiod_chip_watch_line_info(ichip.chip(), offset) } + } else { + unsafe { bindings::gpiod_chip_get_line_info(ichip.chip(), offset) } + }; + + if info.is_null() { + return Err(Error::OperationFailed( + "Gpio LineInfo line-info", + IoError::last(), + )); + } + + Ok(Self { + info, + ichip: if watch { Some(ichip) } else { None }, + free: watch, + }) + } + + /// Stop watching the line + pub fn unwatch(&mut self) { + if let Some(ichip) = &self.ichip { + unsafe { + bindings::gpiod_chip_unwatch_line_info(ichip.chip(), self.get_offset()); + } + self.ichip = None; + } + } + + /// Get the offset of the line within the GPIO chip. + /// + /// The offset uniquely identifies the line on the chip. The combination of the chip and offset + /// uniquely identifies the line within the system. + + pub fn get_offset(&self) -> u32 { + unsafe { bindings::gpiod_line_info_get_offset(self.info) } + } + + /// Get GPIO line's name. + pub fn get_name(&self) -> Result<&str> { + // SAFETY: The string returned by libgpiod is guaranteed to live as long + // as the `struct LineInfo`. + let name = unsafe { bindings::gpiod_line_info_get_name(self.info) }; + if name.is_null() { + return Err(Error::NameNotFound("GPIO line's name")); + } + + // SAFETY: The string is guaranteed to be valid here. + str::from_utf8(unsafe { + slice::from_raw_parts(name as *const u8, bindings::strlen(name) as usize) + }) + .map_err(Error::InvalidString) + } + + /// Returns True if the line is in use, false otherwise. + /// + /// The user space can't know exactly why a line is busy. It may have been + /// requested by another process or hogged by the kernel. It only matters that + /// the line is used and we can't request it. + pub fn is_used(&self) -> bool { + unsafe { bindings::gpiod_line_info_is_used(self.info) } + } + + /// Get the GPIO line's consumer name. + pub fn get_consumer(&self) -> Result<&str> { + // SAFETY: The string returned by libgpiod is guaranteed to live as long + // as the `struct LineInfo`. + let name = unsafe { bindings::gpiod_line_info_get_consumer(self.info) }; + if name.is_null() { + return Err(Error::NameNotFound("GPIO line's consumer name")); + } + + // SAFETY: The string is guaranteed to be valid here. + str::from_utf8(unsafe { + slice::from_raw_parts(name as *const u8, bindings::strlen(name) as usize) + }) + .map_err(Error::InvalidString) + } + + /// Get the GPIO line's direction. + pub fn get_direction(&self) -> Result { + Direction::new(unsafe { bindings::gpiod_line_info_get_direction(self.info) } as u32) + } + + /// Returns true if the line is "active-low", false otherwise. + pub fn is_active_low(&self) -> bool { + unsafe { bindings::gpiod_line_info_is_active_low(self.info) } + } + + /// Get the GPIO line's bias setting. + pub fn get_bias(&self) -> Result { + Bias::new(unsafe { bindings::gpiod_line_info_get_bias(self.info) } as u32) + } + + /// Get the GPIO line's drive setting. + pub fn get_drive(&self) -> Result { + Drive::new(unsafe { bindings::gpiod_line_info_get_drive(self.info) } as u32) + } + + /// Get the current edge detection setting of the line. + pub fn get_edge_detection(&self) -> Result { + Edge::new(unsafe { bindings::gpiod_line_info_get_edge_detection(self.info) } as u32) + } + + /// Get the current event clock setting used for edge event timestamps. + pub fn get_event_clock(&self) -> Result { + EventClock::new(unsafe { bindings::gpiod_line_info_get_event_clock(self.info) } as u32) + } + + /// Returns true if the line is debounced (either by hardware or by the + /// kernel software debouncer), false otherwise. + pub fn is_debounced(&self) -> bool { + unsafe { bindings::gpiod_line_info_is_debounced(self.info) } + } + + /// Get the debounce period of the line. + pub fn get_debounce_period(&self) -> Duration { + Duration::from_micros(unsafe { + bindings::gpiod_line_info_get_debounce_period_us(self.info) + }) + } +} + +impl TryFrom<&InfoEvent> for LineInfo { + type Error = Error; + + /// Get the Line info object associated with a event. + fn try_from(event: &InfoEvent) -> Result { + let info = unsafe { bindings::gpiod_info_event_get_line_info(event.event()) }; + if info.is_null() { + return Err(Error::OperationFailed( + "Gpio LineInfo try-from", + IoError::last(), + )); + } + + Ok(Self { + info, + ichip: None, + free: false, + }) + } +} + +impl Drop for LineInfo { + fn drop(&mut self) { + // We must not free the Line info object created from `struct InfoEvent` by calling + // libgpiod API. + if self.free { + self.unwatch(); + unsafe { bindings::gpiod_line_info_free(self.info) } + } + } +} diff --git a/bindings/rust/src/line_request.rs b/bindings/rust/src/line_request.rs new file mode 100644 index 000000000000..bb338e72671d --- /dev/null +++ b/bindings/rust/src/line_request.rs @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use libc::EINVAL; +use std::os::raw::c_ulong; +use std::sync::Arc; +use std::time::Duration; + +use vmm_sys_util::errno::Error as IoError; + +use super::{bindings, ChipInternal, EdgeEventBuffer, Error, LineConfig, RequestConfig, Result}; + +/// Line request operations +/// +/// Allows interaction with a set of requested lines. +pub struct LineRequest { + request: *mut bindings::gpiod_line_request, +} + +impl LineRequest { + /// Request a set of lines for exclusive usage. + pub(crate) fn new( + ichip: &Arc, + rconfig: &RequestConfig, + lconfig: &LineConfig, + ) -> Result { + let request = unsafe { + bindings::gpiod_chip_request_lines(ichip.chip(), rconfig.config(), lconfig.config()) + }; + + if request.is_null() { + return Err(Error::OperationFailed( + "Gpio LineRequest request-lines", + IoError::last(), + )); + } + + Ok(Self { request }) + } + + /// Get the number of lines in the request. + pub fn get_num_lines(&self) -> u32 { + unsafe { bindings::gpiod_line_request_get_num_lines(self.request) as u32 } + } + + /// Get the offsets of lines in the request. + pub fn get_offsets(&self) -> Vec { + let mut offsets = vec![0; self.get_num_lines() as usize]; + + unsafe { bindings::gpiod_line_request_get_offsets(self.request, offsets.as_mut_ptr()) }; + offsets + } + + /// Get the value (0 or 1) of a single line associated with the request. + pub fn get_value(&self, offset: u32) -> Result { + let value = unsafe { bindings::gpiod_line_request_get_value(self.request, offset) }; + + if value != 0 && value != 1 { + Err(Error::OperationFailed( + "Gpio LineRequest get-value", + IoError::last(), + )) + } else { + Ok(value as u32) + } + } + + /// Get values of a subset of lines associated with the request. + pub fn get_values_subset(&self, offsets: &[u32], values: &mut Vec) -> Result<()> { + if offsets.len() != values.len() { + return Err(Error::OperationFailed( + "Gpio LineRequest array size mismatch", + IoError::new(EINVAL), + )); + } + + let ret = unsafe { + bindings::gpiod_line_request_get_values_subset( + self.request, + offsets.len() as c_ulong, + offsets.as_ptr(), + values.as_mut_ptr(), + ) + }; + + if ret == -1 { + Err(Error::OperationFailed( + "Gpio LineRequest get-values-subset", + IoError::last(), + )) + } else { + Ok(()) + } + } + + /// Get values of all lines associated with the request. + pub fn get_values(&self, values: &mut Vec) -> Result<()> { + if values.len() != self.get_num_lines() as usize { + return Err(Error::OperationFailed( + "Gpio LineRequest array size mismatch", + IoError::new(EINVAL), + )); + } + + let ret = + unsafe { bindings::gpiod_line_request_get_values(self.request, values.as_mut_ptr()) }; + + if ret == -1 { + Err(Error::OperationFailed( + "Gpio LineRequest get-values", + IoError::last(), + )) + } else { + Ok(()) + } + } + + /// Set the value of a single line associated with the request. + pub fn set_value(&self, offset: u32, value: i32) -> Result<()> { + let ret = unsafe { bindings::gpiod_line_request_set_value(self.request, offset, !!value) }; + + if ret == -1 { + Err(Error::OperationFailed( + "Gpio LineRequest set-value", + IoError::last(), + )) + } else { + Ok(()) + } + } + + /// Get values of a subset of lines associated with the request. + pub fn set_values_subset(&self, offsets: &[u32], values: &[i32]) -> Result<()> { + if offsets.len() != values.len() { + return Err(Error::OperationFailed( + "Gpio LineRequest array size mismatch", + IoError::new(EINVAL), + )); + } + + let ret = unsafe { + bindings::gpiod_line_request_set_values_subset( + self.request, + offsets.len() as c_ulong, + offsets.as_ptr(), + values.as_ptr(), + ) + }; + + if ret == -1 { + Err(Error::OperationFailed( + "Gpio LineRequest set-values-subset", + IoError::last(), + )) + } else { + Ok(()) + } + } + + /// Get values of all lines associated with the request. + pub fn set_values(&self, values: &[i32]) -> Result<()> { + if values.len() != self.get_num_lines() as usize { + return Err(Error::OperationFailed( + "Gpio LineRequest array size mismatch", + IoError::new(EINVAL), + )); + } + + let ret = unsafe { bindings::gpiod_line_request_set_values(self.request, values.as_ptr()) }; + + if ret == -1 { + Err(Error::OperationFailed( + "Gpio LineRequest set-values", + IoError::last(), + )) + } else { + Ok(()) + } + } + + /// Update the configuration of lines associated with the line request. + pub fn reconfigure_lines(&self, lconfig: &LineConfig) -> Result<()> { + let ret = unsafe { + bindings::gpiod_line_request_reconfigure_lines(self.request, lconfig.config()) + }; + + if ret == -1 { + Err(Error::OperationFailed( + "Gpio LineRequest reconfigure-lines", + IoError::last(), + )) + } else { + Ok(()) + } + } + + /// Get the file descriptor associated with the line request. + pub fn get_fd(&self) -> u32 { + unsafe { bindings::gpiod_line_request_get_fd(self.request) as u32 } + } + + /// Wait for edge events on any of the lines associated with the request. + pub fn wait_edge_event(&self, timeout: Duration) -> Result<()> { + let ret = unsafe { + bindings::gpiod_line_request_wait_edge_event(self.request, timeout.as_nanos() as i64) + }; + + match ret { + -1 => Err(Error::OperationFailed( + "Gpio LineRequest edge-event-wait", + IoError::last(), + )), + 0 => Err(Error::OperationTimedOut), + _ => Ok(()), + } + } + + /// Get a number of edge events from a line request. + /// + /// This function will block if no event was queued for the line. + pub fn read_edge_event(&self, buffer: &EdgeEventBuffer, max_events: u32) -> Result { + let ret = unsafe { + bindings::gpiod_line_request_read_edge_event( + self.request, + buffer.buffer(), + max_events as c_ulong, + ) + }; + + if ret == -1 { + Err(Error::OperationFailed( + "Gpio LineRequest edge-event-read", + IoError::last(), + )) + } else { + Ok(ret as u32) + } + } +} + +impl Drop for LineRequest { + /// Release the requested lines and free all associated resources. + fn drop(&mut self) { + unsafe { bindings::gpiod_line_request_release(self.request) } + } +} diff --git a/bindings/rust/src/request_config.rs b/bindings/rust/src/request_config.rs new file mode 100644 index 000000000000..3e667d28cc1f --- /dev/null +++ b/bindings/rust/src/request_config.rs @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause +// +// Copyright 2022 Linaro Ltd. All Rights Reserved. +// Viresh Kumar + +use std::os::raw::{c_char, c_ulong}; +use std::{slice, str}; + +use vmm_sys_util::errno::Error as IoError; + +use super::{bindings, Error, Result}; + +/// Request configuration objects +/// +/// Request config objects are used to pass a set of options to the kernel at +/// the time of the line request. Similarly to the line-config - the mutators +/// don't return error values. If the values are invalid, in general they are +/// silently adjusted to acceptable ranges. + +pub struct RequestConfig { + config: *mut bindings::gpiod_request_config, +} + +impl RequestConfig { + /// Create a new request config object. + pub fn new() -> Result { + let config = unsafe { bindings::gpiod_request_config_new() }; + if config.is_null() { + return Err(Error::OperationFailed( + "Gpio RequestConfig new", + IoError::last(), + )); + } + + Ok(Self { config }) + } + + /// Private helper, Returns gpiod_request_config + pub(crate) fn config(&self) -> *mut bindings::gpiod_request_config { + self.config + } + + /// Set the consumer name for the request. + /// + /// If the consumer string is too long, it will be truncated to the max + /// accepted length. + pub fn set_consumer(&self, consumer: &str) { + // Null-terminate the string + let consumer = consumer.to_owned() + "\0"; + + unsafe { + bindings::gpiod_request_config_set_consumer( + self.config, + consumer.as_ptr() as *const c_char, + ) + } + } + + /// Get the consumer name configured in the request config. + pub fn get_consumer(&self) -> Result<&str> { + // SAFETY: The string returned by libgpiod is guaranteed to live as long + // as the `struct RequestConfig`. + let consumer = unsafe { bindings::gpiod_request_config_get_consumer(self.config) }; + if consumer.is_null() { + return Err(Error::OperationFailed( + "Gpio RequestConfig get-consumer", + IoError::last(), + )); + } + + // SAFETY: The string is guaranteed to be valid here. + str::from_utf8(unsafe { + slice::from_raw_parts(consumer as *const u8, bindings::strlen(consumer) as usize) + }) + .map_err(Error::InvalidString) + } + + /// Set the offsets of the lines to be requested. + /// + /// If too many offsets were specified, the offsets above the limit accepted + /// by the kernel (64 lines) are silently dropped. + pub fn set_offsets(&self, offsets: &[u32]) { + unsafe { + bindings::gpiod_request_config_set_offsets( + self.config, + offsets.len() as c_ulong, + offsets.as_ptr(), + ) + } + } + + /// Get the offsets of lines in the request config. + pub fn get_offsets(&self) -> Vec { + let num = unsafe { bindings::gpiod_request_config_get_num_offsets(self.config) }; + let mut offsets = vec![0; num as usize]; + + unsafe { bindings::gpiod_request_config_get_offsets(self.config, offsets.as_mut_ptr()) }; + offsets + } + + /// Set the size of the kernel event buffer for the request. + /// + /// The kernel may adjust the value if it's too high. If set to 0, the + /// default value will be used. + pub fn set_event_buffer_size(&self, size: u32) { + unsafe { + bindings::gpiod_request_config_set_event_buffer_size(self.config, size as c_ulong) + } + } + + /// Get the edge event buffer size for the request config. + pub fn get_event_buffer_size(&self) -> u32 { + unsafe { bindings::gpiod_request_config_get_event_buffer_size(self.config) as u32 } + } +} + +impl Drop for RequestConfig { + /// Free the request config object and release all associated resources. + fn drop(&mut self) { + unsafe { bindings::gpiod_request_config_free(self.config) } + } +}