diff mbox series

[libgpiod,V9,6/8] bindings: rust: Add examples to libgpiod crate

Message ID 43c0d28fc12bbfb5a0feba0d30542a4ca2d4bad6.1667815011.git.viresh.kumar@linaro.org
State New
Headers show
Series libgpiod: Add Rust bindings | expand

Commit Message

Viresh Kumar Nov. 7, 2022, 9:57 a.m. UTC
Add examples for the usage of the rust bindings, quite similar to the
ones in cxx bindings.

Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
---
 .../rust/libgpiod/examples/gpio_events.rs     |  89 ++++++++++++
 .../examples/gpio_threaded_info_events.rs     | 133 ++++++++++++++++++
 bindings/rust/libgpiod/examples/gpiodetect.rs |  31 ++++
 bindings/rust/libgpiod/examples/gpiofind.rs   |  37 +++++
 bindings/rust/libgpiod/examples/gpioget.rs    |  46 ++++++
 bindings/rust/libgpiod/examples/gpioinfo.rs   |  98 +++++++++++++
 bindings/rust/libgpiod/examples/gpiomon.rs    |  75 ++++++++++
 bindings/rust/libgpiod/examples/gpioset.rs    |  64 +++++++++
 bindings/rust/libgpiod/examples/gpiowatch.rs  |  54 +++++++
 9 files changed, 627 insertions(+)
 create mode 100644 bindings/rust/libgpiod/examples/gpio_events.rs
 create mode 100644 bindings/rust/libgpiod/examples/gpio_threaded_info_events.rs
 create mode 100644 bindings/rust/libgpiod/examples/gpiodetect.rs
 create mode 100644 bindings/rust/libgpiod/examples/gpiofind.rs
 create mode 100644 bindings/rust/libgpiod/examples/gpioget.rs
 create mode 100644 bindings/rust/libgpiod/examples/gpioinfo.rs
 create mode 100644 bindings/rust/libgpiod/examples/gpiomon.rs
 create mode 100644 bindings/rust/libgpiod/examples/gpioset.rs
 create mode 100644 bindings/rust/libgpiod/examples/gpiowatch.rs

Comments

Viresh Kumar Nov. 14, 2022, 10:03 a.m. UTC | #1
On 10-11-22, 19:26, Bartosz Golaszewski wrote:
> I'm looking at it and thinking that it would look much better as:
> 
> let settings = line::Settings::new()
>     .set_direction(Direction::Output)
>     .set_output_value(Value::new(value))
>     .build()?;
> 
> settings would not need to be mutable (we'd have some intermediate
> SettingsBuilder object?) and could be directly passed to
> add_line_settings()?
> 
> We now have chained mutators for LineSettings in C++ and keyword
> arguments in Python - somehow I think that the former suits rust much
> better than passing an array of properties.

We already support chained mutators in the Rust bindings. This example can also
be written as:

         let mut lsettings = line::Settings::new()?;
 
         lsettings
             .set_direction(Direction::Output)?
             .set_output_value(Value::new(value)?)?;
Bartosz Golaszewski Nov. 14, 2022, 10:29 a.m. UTC | #2
On Mon, Nov 14, 2022 at 11:03 AM Viresh Kumar <viresh.kumar@linaro.org> wrote:
>
> On 10-11-22, 19:26, Bartosz Golaszewski wrote:
> > I'm looking at it and thinking that it would look much better as:
> >
> > let settings = line::Settings::new()
> >     .set_direction(Direction::Output)
> >     .set_output_value(Value::new(value))
> >     .build()?;
> >
> > settings would not need to be mutable (we'd have some intermediate
> > SettingsBuilder object?) and could be directly passed to
> > add_line_settings()?
> >
> > We now have chained mutators for LineSettings in C++ and keyword
> > arguments in Python - somehow I think that the former suits rust much
> > better than passing an array of properties.
>
> We already support chained mutators in the Rust bindings. This example can also
> be written as:
>
>          let mut lsettings = line::Settings::new()?;
>
>          lsettings
>              .set_direction(Direction::Output)?
>              .set_output_value(Value::new(value)?)?;
>
> --
> viresh

Ah, I missed that, my bad.

Would it also work in non-mutable way like

let lsettings = line::Settings::new.set_direction(Direction::Output)?;

?
Viresh Kumar Nov. 14, 2022, 10:42 a.m. UTC | #3
On 14-11-22, 11:29, Bartosz Golaszewski wrote:
> Ah, I missed that, my bad.
> 
> Would it also work in non-mutable way like
> 
> let lsettings = line::Settings::new.set_direction(Direction::Output)?;

No, since set_direction() expects a reference instead of the object itself. So
currently we need to do Settings::new() in a separate statement and so mutable
is required too. But then we don't need a build() routine to covert to lsettings
here.
diff mbox series

Patch

diff --git a/bindings/rust/libgpiod/examples/gpio_events.rs b/bindings/rust/libgpiod/examples/gpio_events.rs
new file mode 100644
index 000000000000..6f3c4d5cadb3
--- /dev/null
+++ b/bindings/rust/libgpiod/examples/gpio_events.rs
@@ -0,0 +1,89 @@ 
+// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
+//
+// Copyright 2022 Linaro Ltd. All Rights Reserved.
+//     Viresh Kumar <viresh.kumar@linaro.org>
+//
+// Simplified Rust implementation to show handling of events, when the buffer
+// is read into multiple times. Based on gpiomon example.
+
+use std::env;
+
+use libgpiod::{
+    chip::Chip,
+    line::{self, Edge, Offset},
+    request, Error, Result,
+};
+
+fn usage(name: &str) {
+    println!("Usage: {} <chip> <offset0> ...", name);
+}
+
+fn main() -> Result<()> {
+    let args: Vec<String> = env::args().collect();
+    if args.len() < 3 {
+        usage(&args[0]);
+        return Err(Error::InvalidArguments);
+    }
+
+    let mut lsettings = line::Settings::new()?;
+    let lconfig = line::Config::new()?;
+    let mut offsets = Vec::<Offset>::new();
+
+    for arg in &args[2..] {
+        let offset = arg.parse::<Offset>().map_err(|_| Error::InvalidArguments)?;
+        offsets.push(offset);
+    }
+
+    lsettings.set_edge_detection(Some(Edge::Both))?;
+    lconfig.add_line_settings(&offsets, lsettings)?;
+
+    let path = format!("/dev/gpiochip{}", args[1]);
+    let chip = Chip::open(&path)?;
+
+    let rconfig = request::Config::new()?;
+
+    let mut buffer = request::Buffer::new(1)?;
+    let request = chip.request_lines(&rconfig, &lconfig)?;
+
+    loop {
+        match request.wait_edge_event(None) {
+            Err(x) => {
+                println!("{:?}", x);
+                return Err(Error::InvalidArguments);
+            }
+
+            Ok(false) => {
+                // This shouldn't happen as the call is blocking.
+                panic!();
+            }
+            Ok(true) => (),
+        }
+
+        let mut events = request.read_edge_events(&mut buffer)?;
+
+        // This can't be used across the next read_edge_events().
+        let event = events.next().unwrap()?;
+
+        // This will out live `event` and the next read_edge_events().
+        let cloned_event = request::Event::event_clone(event)?;
+
+        let events = request.read_edge_events(&mut buffer)?;
+        for event in events {
+            let event = event?;
+            println!(
+                "line: {} type: {:?}, time: {:?}",
+                event.line_offset(),
+                event.event_type(),
+                event.timestamp()
+            );
+        }
+
+        // `cloned_event` is still available to be used.
+        println!(
+            "line: {} type: {:?}, time: {:?}",
+            cloned_event.line_offset(),
+            cloned_event.event_type(),
+            cloned_event.timestamp()
+        );
+    }
+}
diff --git a/bindings/rust/libgpiod/examples/gpio_threaded_info_events.rs b/bindings/rust/libgpiod/examples/gpio_threaded_info_events.rs
new file mode 100644
index 000000000000..04566f92e66c
--- /dev/null
+++ b/bindings/rust/libgpiod/examples/gpio_threaded_info_events.rs
@@ -0,0 +1,133 @@ 
+// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
+//
+// Copyright 2022 Linaro Ltd. All Rights Reserved.
+//     Viresh Kumar <viresh.kumar@linaro.org>
+//
+// Simplified Rust implementation to show handling of info events, that are
+// generated from another thread.
+
+use std::{
+    env,
+    sync::{
+        mpsc::{self, Receiver, Sender},
+        Arc, Mutex,
+    },
+    thread,
+    time::Duration,
+};
+
+use libgpiod::{
+    chip::Chip,
+    line::{self, Direction, InfoChangeKind, Offset},
+    request, Error, Result,
+};
+
+fn usage(name: &str) {
+    println!("Usage: {} <chip> <offset>", name);
+}
+
+fn request_reconfigure_line(
+    chip: Arc<Mutex<Chip>>,
+    offset: Offset,
+    tx: Sender<()>,
+    rx: Receiver<()>,
+) {
+    thread::spawn(move || {
+        let lconfig = line::Config::new().unwrap();
+        let lsettings = line::Settings::new().unwrap();
+        lconfig.add_line_settings(&[offset], lsettings).unwrap();
+        let rconfig = request::Config::new().unwrap();
+
+        let request = chip
+            .lock()
+            .unwrap()
+            .request_lines(&rconfig, &lconfig)
+            .unwrap();
+
+        // Signal the parent to continue
+        tx.send(()).expect("Could not send signal on channel");
+
+        // Wait for parent to signal
+        rx.recv().expect("Could not receive from channel");
+
+        let lconfig = line::Config::new().unwrap();
+        let mut lsettings = line::Settings::new().unwrap();
+        lsettings.set_direction(Direction::Output).unwrap();
+        lconfig.add_line_settings(&[offset], lsettings).unwrap();
+
+        request.reconfigure_lines(&lconfig).unwrap();
+
+        // Signal the parent to continue
+        tx.send(()).expect("Could not send signal on channel");
+
+        // Wait for parent to signal
+        rx.recv().expect("Could not receive from channel");
+    });
+}
+
+fn main() -> Result<()> {
+    let args: Vec<String> = env::args().collect();
+    if args.len() != 3 {
+        usage(&args[0]);
+        return Err(Error::InvalidArguments);
+    }
+
+    let path = format!("/dev/gpiochip{}", args[1]);
+    let offset = args[2]
+        .parse::<Offset>()
+        .map_err(|_| Error::InvalidArguments)?;
+
+    let chip = Arc::new(Mutex::new(Chip::open(&path)?));
+    chip.lock().unwrap().watch_line_info(offset)?;
+
+    // Thread synchronizing mechanism
+    let (tx_main, rx_thread) = mpsc::channel();
+    let (tx_thread, rx_main) = mpsc::channel();
+
+    // Generate events
+    request_reconfigure_line(chip.clone(), offset, tx_thread, rx_thread);
+
+    // Wait for thread to signal
+    rx_main.recv().expect("Could not receive from channel");
+
+    // Line requested event
+    assert!(chip
+        .lock()
+        .unwrap()
+        .wait_info_event(Some(Duration::from_secs(1)))?);
+    let event = chip.lock().unwrap().read_info_event()?;
+    assert_eq!(event.event_type()?, InfoChangeKind::LineRequested);
+
+    // Signal the thread to continue
+    tx_main.send(()).expect("Could not send signal on channel");
+
+    // Wait for thread to signal
+    rx_main.recv().expect("Could not receive from channel");
+
+    // Line changed event
+    assert!(chip
+        .lock()
+        .unwrap()
+        .wait_info_event(Some(Duration::from_millis(10)))?);
+    let event = chip.lock().unwrap().read_info_event()?;
+    assert_eq!(event.event_type()?, InfoChangeKind::LineConfigChanged);
+
+    // Signal the thread to continue
+    tx_main.send(()).expect("Could not send signal on channel");
+
+    // Line released event
+    assert!(chip
+        .lock()
+        .unwrap()
+        .wait_info_event(Some(Duration::from_millis(10)))?);
+    let event = chip.lock().unwrap().read_info_event()?;
+    assert_eq!(event.event_type().unwrap(), InfoChangeKind::LineReleased);
+
+    // No events available
+    assert!(!chip
+        .lock()
+        .unwrap()
+        .wait_info_event(Some(Duration::from_millis(100)))?);
+
+    Ok(())
+}
diff --git a/bindings/rust/libgpiod/examples/gpiodetect.rs b/bindings/rust/libgpiod/examples/gpiodetect.rs
new file mode 100644
index 000000000000..e1d8f59268b5
--- /dev/null
+++ b/bindings/rust/libgpiod/examples/gpiodetect.rs
@@ -0,0 +1,31 @@ 
+// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
+//
+// Copyright 2022 Linaro Ltd. All Rights Reserved.
+//     Viresh Kumar <viresh.kumar@linaro.org>
+//
+// Simplified Rust implementation of gpiodetect tool.
+
+use std::env;
+use std::path::Path;
+
+use libgpiod::{self, Error, Result};
+
+fn main() -> Result<()> {
+    let args: Vec<String> = env::args().collect();
+    if args.len() > 1 {
+        println!("Usage: {}", args[0]);
+        return Err(Error::InvalidArguments);
+    }
+
+    for chip in libgpiod::gpiochip_devices(&Path::new("/dev"))? {
+        let info = chip.info()?;
+        println!(
+            "{} [{}] ({})",
+            info.name()?,
+            info.label()?,
+            info.num_lines(),
+        );
+    }
+
+    Ok(())
+}
diff --git a/bindings/rust/libgpiod/examples/gpiofind.rs b/bindings/rust/libgpiod/examples/gpiofind.rs
new file mode 100644
index 000000000000..daaa93cc1bd2
--- /dev/null
+++ b/bindings/rust/libgpiod/examples/gpiofind.rs
@@ -0,0 +1,37 @@ 
+// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
+//
+// Copyright 2022 Linaro Ltd. All Rights Reserved.
+//     Viresh Kumar <viresh.kumar@linaro.org>
+//
+// Simplified Rust implementation of gpiofind tool.
+
+use std::env;
+use std::path::Path;
+
+use libgpiod::{self, Error, Result};
+
+fn main() -> Result<()> {
+    let args: Vec<String> = env::args().collect();
+    if args.len() != 2 {
+        println!("Usage: {} <line-name>", args[0]);
+        return Err(Error::InvalidArguments);
+    }
+
+    for chip in libgpiod::gpiochip_devices(&Path::new("/dev"))? {
+        let offset = chip.line_offset_from_name(&args[1]);
+        let info = chip.info()?;
+
+        if offset.is_ok() {
+            println!(
+                "Line {} found: Chip: {}, offset: {}",
+                args[1],
+                info.name()?,
+                offset?
+            );
+            return Ok(());
+        }
+    }
+
+    println!("Failed to find line: {}", args[1]);
+    Ok(())
+}
diff --git a/bindings/rust/libgpiod/examples/gpioget.rs b/bindings/rust/libgpiod/examples/gpioget.rs
new file mode 100644
index 000000000000..5ae50a4fdead
--- /dev/null
+++ b/bindings/rust/libgpiod/examples/gpioget.rs
@@ -0,0 +1,46 @@ 
+// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
+//
+// Copyright 2022 Linaro Ltd. All Rights Reserved.
+//     Viresh Kumar <viresh.kumar@linaro.org>
+//
+// Simplified Rust implementation of gpioget tool.
+
+use std::env;
+
+use libgpiod::{
+    chip::Chip,
+    line::{self, Direction, Offset},
+    request, Error, Result,
+};
+
+fn main() -> Result<()> {
+    let args: Vec<String> = env::args().collect();
+    if args.len() < 3 {
+        println!("Usage: {} <chip> <line_offset0> ...", args[0]);
+        return Err(Error::InvalidArguments);
+    }
+
+    let mut lsettings = line::Settings::new()?;
+    let lconfig = line::Config::new()?;
+    let mut offsets = Vec::<Offset>::new();
+
+    for arg in &args[2..] {
+        let offset = arg.parse::<Offset>().map_err(|_| Error::InvalidArguments)?;
+        offsets.push(offset);
+    }
+
+    lsettings.set_direction(Direction::Input)?;
+    lconfig.add_line_settings(&offsets, lsettings)?;
+
+    let path = format!("/dev/gpiochip{}", args[1]);
+    let chip = Chip::open(&path)?;
+
+    let rconfig = request::Config::new()?;
+    rconfig.set_consumer(&args[0])?;
+
+    let request = chip.request_lines(&rconfig, &lconfig)?;
+    let map = request.values()?;
+
+    println!("{:?}", map);
+    Ok(())
+}
diff --git a/bindings/rust/libgpiod/examples/gpioinfo.rs b/bindings/rust/libgpiod/examples/gpioinfo.rs
new file mode 100644
index 000000000000..f972e4f405d2
--- /dev/null
+++ b/bindings/rust/libgpiod/examples/gpioinfo.rs
@@ -0,0 +1,98 @@ 
+// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
+//
+// Copyright 2022 Linaro Ltd. All Rights Reserved.
+//     Viresh Kumar <viresh.kumar@linaro.org>
+//
+// Simplified Rust implementation of gpioinfo tool.
+
+use std::env;
+use std::path::Path;
+
+use libgpiod::{
+    chip::Chip,
+    line::{Direction, Offset},
+    Error, Result,
+};
+
+fn line_info(chip: &Chip, offset: Offset) -> Result<()> {
+    let info = chip.line_info(offset)?;
+    let off = info.offset();
+
+    let name = match info.name() {
+        Ok(name) => name,
+        _ => "unused",
+    };
+
+    let consumer = match info.consumer() {
+        Ok(name) => name,
+        _ => "unnamed",
+    };
+
+    let low = if info.is_active_low() {
+        "active-low"
+    } else {
+        "active-high"
+    };
+
+    let dir = match info.direction()? {
+        Direction::AsIs => "None",
+        Direction::Input => "Input",
+        Direction::Output => "Output",
+    };
+
+    println!(
+        "\tline {:>3}\
+              \t{:>10}\
+              \t{:>10}\
+              \t{:>6}\
+              \t{:>14}",
+        off, name, consumer, dir, low
+    );
+
+    Ok(())
+}
+
+fn chip_info(chip: &Chip) -> Result<()> {
+    let info = chip.info()?;
+    let ngpio = info.num_lines();
+
+    println!("GPIO Chip name: {}", info.name()?);
+    println!("\tlabel: {}", info.label()?);
+    println!("\tpath: {}", chip.path()?);
+    println!("\tngpio: {}\n", ngpio);
+
+    println!("\tLine information:");
+
+    for offset in 0..ngpio {
+        line_info(chip, offset as Offset)?;
+    }
+    println!("\n");
+
+    Ok(())
+}
+
+fn main() -> Result<()> {
+    let args: Vec<String> = env::args().collect();
+    if args.len() > 2 {
+        println!("Usage: {}", args[0]);
+        return Err(Error::InvalidArguments);
+    }
+
+    if args.len() == 1 {
+        for chip in libgpiod::gpiochip_devices(&Path::new("/dev"))? {
+            chip_info(&chip)?;
+        }
+    } else {
+        let index = args[1]
+            .parse::<u32>()
+            .map_err(|_| Error::InvalidArguments)?;
+        let path = format!("/dev/gpiochip{}", index);
+        if libgpiod::is_gpiochip_device(&path) {
+            let chip = Chip::open(&path)?;
+
+            chip_info(&chip)?;
+        }
+    }
+
+    Ok(())
+}
diff --git a/bindings/rust/libgpiod/examples/gpiomon.rs b/bindings/rust/libgpiod/examples/gpiomon.rs
new file mode 100644
index 000000000000..fb5b208b3972
--- /dev/null
+++ b/bindings/rust/libgpiod/examples/gpiomon.rs
@@ -0,0 +1,75 @@ 
+// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
+//
+// Copyright 2022 Linaro Ltd. All Rights Reserved.
+//     Viresh Kumar <viresh.kumar@linaro.org>
+//
+// Simplified Rust implementation of the gpiomon tool.
+
+use std::env;
+
+use libgpiod::{
+    chip::Chip,
+    line::{self, Edge, EdgeKind, Offset},
+    request, Error, Result,
+};
+
+fn usage(name: &str) {
+    println!("Usage: {} <chip> <offset0> ...", name);
+}
+
+fn main() -> Result<()> {
+    let args: Vec<String> = env::args().collect();
+    if args.len() < 3 {
+        usage(&args[0]);
+        return Err(Error::InvalidArguments);
+    }
+
+    let mut lsettings = line::Settings::new()?;
+    let lconfig = line::Config::new()?;
+    let mut offsets = Vec::<Offset>::new();
+
+    for arg in &args[2..] {
+        let offset = arg.parse::<Offset>().map_err(|_| Error::InvalidArguments)?;
+        offsets.push(offset);
+    }
+
+    lsettings.set_edge_detection(Some(Edge::Both))?;
+    lconfig.add_line_settings(&offsets, lsettings)?;
+
+    let path = format!("/dev/gpiochip{}", args[1]);
+    let chip = Chip::open(&path)?;
+
+    let rconfig = request::Config::new()?;
+
+    let mut buffer = request::Buffer::new(1)?;
+    let request = chip.request_lines(&rconfig, &lconfig)?;
+
+    loop {
+        match request.wait_edge_event(None) {
+            Err(x) => {
+                println!("{:?}", x);
+                return Err(Error::InvalidArguments);
+            }
+
+            Ok(false) => {
+                // This shouldn't happen as the call is blocking.
+                panic!();
+            }
+            Ok(true) => (),
+        }
+
+        let events = request.read_edge_events(&mut buffer)?;
+        for event in events {
+            let event = event?;
+            println!(
+                "line: {} type: {}, time: {:?}",
+                event.line_offset(),
+                match event.event_type()? {
+                    EdgeKind::Rising => "Rising",
+                    EdgeKind::Falling => "Falling",
+                },
+                event.timestamp()
+            );
+        }
+    }
+}
diff --git a/bindings/rust/libgpiod/examples/gpioset.rs b/bindings/rust/libgpiod/examples/gpioset.rs
new file mode 100644
index 000000000000..f72a623ab28c
--- /dev/null
+++ b/bindings/rust/libgpiod/examples/gpioset.rs
@@ -0,0 +1,64 @@ 
+// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
+//
+// Copyright 2022 Linaro Ltd. All Rights Reserved.
+//     Viresh Kumar <viresh.kumar@linaro.org>
+//
+// Simplified Rust implementation of the gpioset tool.
+
+use std::env;
+use std::io::{stdin, Read};
+
+use libgpiod::{
+    chip::Chip,
+    line::{self, Direction, Offset, SettingVal, Value},
+    request, Error, Result,
+};
+
+fn usage(name: &str) {
+    println!("Usage: {} <chip> <line_offset0>=<value0> ...", name);
+}
+
+fn main() -> Result<()> {
+    let args: Vec<String> = env::args().collect();
+    if args.len() < 3 {
+        usage(&args[0]);
+        return Err(Error::InvalidArguments);
+    }
+
+    let lconfig = line::Config::new()?;
+
+    for arg in &args[2..] {
+        let pair: Vec<&str> = arg.split('=').collect();
+        if pair.len() != 2 {
+            usage(&args[0]);
+            return Err(Error::InvalidArguments);
+        }
+
+        let offset = pair[0]
+            .parse::<Offset>()
+            .map_err(|_| Error::InvalidArguments)?;
+        let value = pair[1]
+            .parse::<i32>()
+            .map_err(|_| Error::InvalidArguments)?;
+
+        let mut lsettings = line::Settings::new()?;
+        lsettings.set_prop(&[
+            SettingVal::Direction(Direction::Output),
+            SettingVal::OutputValue(Value::new(value)?),
+        ])?;
+        lconfig.add_line_settings(&[offset], lsettings)?;
+    }
+
+    let path = format!("/dev/gpiochip{}", args[1]);
+    let chip = Chip::open(&path)?;
+
+    let rconfig = request::Config::new()?;
+    rconfig.set_consumer(&args[0])?;
+
+    chip.request_lines(&rconfig, &lconfig)?;
+
+    // Wait for keypress, let user verify line status.
+    stdin().read_exact(&mut [0u8]).unwrap();
+
+    Ok(())
+}
diff --git a/bindings/rust/libgpiod/examples/gpiowatch.rs b/bindings/rust/libgpiod/examples/gpiowatch.rs
new file mode 100644
index 000000000000..ea62515b1e0d
--- /dev/null
+++ b/bindings/rust/libgpiod/examples/gpiowatch.rs
@@ -0,0 +1,54 @@ 
+// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
+//
+// Copyright 2022 Linaro Ltd. All Rights Reserved.
+//     Viresh Kumar <viresh.kumar@linaro.org>
+//
+// Simplified Rust implementation of the gpiowatch tool.
+
+use std::env;
+
+use libgpiod::{chip::Chip, line::Offset, Error, Result};
+
+fn usage(name: &str) {
+    println!("Usage: {} <chip> <offset0> ...", name);
+}
+
+fn main() -> Result<()> {
+    let args: Vec<String> = env::args().collect();
+    if args.len() < 2 {
+        usage(&args[0]);
+        return Err(Error::InvalidArguments);
+    }
+
+    let path = format!("/dev/gpiochip{}", args[1]);
+    let offset = args[2]
+        .parse::<Offset>()
+        .map_err(|_| Error::InvalidArguments)?;
+
+    let chip = Chip::open(&path)?;
+    let _info = chip.watch_line_info(offset)?;
+
+    match chip.wait_info_event(None) {
+        Err(x) => {
+            println!("{:?}", x);
+            return Err(Error::InvalidArguments);
+        }
+
+        Ok(false) => {
+            // This shouldn't happen as the call is blocking.
+            panic!();
+        }
+        Ok(true) => (),
+    }
+
+    let event = chip.read_info_event()?;
+    println!(
+        "line: {} type: {:?}, time: {:?}",
+        offset,
+        event.event_type()?,
+        event.timestamp()
+    );
+
+    chip.unwatch(offset);
+    Ok(())
+}