From 9e62d858b1f4eb12db4fd132192514a52e3f42c3 Mon Sep 17 00:00:00 2001 From: Akrm Al-Hakimi Date: Sat, 9 May 2026 09:44:52 -0400 Subject: [PATCH 1/9] feat(#400): add dbus module declarations for modemmanager --- mmrs/src/dbus/mod.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/mmrs/src/dbus/mod.rs b/mmrs/src/dbus/mod.rs index 8b137891..3f8af5f9 100644 --- a/mmrs/src/dbus/mod.rs +++ b/mmrs/src/dbus/mod.rs @@ -1 +1,19 @@ +//! D-Bus proxy interfaces for ModemManager. +//! +//! This module contains low-level D-Bus proxy definitions for communicating +//! with ModemManager over the system bus. +// Re-exports are consumed by core/api layers that are not yet implemented. +#![allow(unused_imports)] + +mod bearer; +mod manager; +mod modem; +mod modem_simple; +mod sim; + +pub(crate) use bearer::MMBearerProxy; +pub(crate) use manager::MMManagerProxy; +pub(crate) use modem::MMModemProxy; +pub(crate) use modem_simple::MMModemSimpleProxy; +pub(crate) use sim::MMSimProxy; From 6e078b2dc87ddb7c853464be3b73a81f8cc6759d Mon Sep 17 00:00:00 2001 From: Akrm Al-Hakimi Date: Sat, 9 May 2026 09:44:52 -0400 Subject: [PATCH 2/9] feat(#400): add ModemManager1 manager proxy --- mmrs/src/dbus/manager.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 mmrs/src/dbus/manager.rs diff --git a/mmrs/src/dbus/manager.rs b/mmrs/src/dbus/manager.rs new file mode 100644 index 00000000..6ff3270f --- /dev/null +++ b/mmrs/src/dbus/manager.rs @@ -0,0 +1,27 @@ +//! ModemManager manager proxy. + +use zbus::proxy; + +/// Proxy for the main ModemManager1 interface. +/// +/// Provides methods for scanning devices, configuring logging, +/// and inhibiting device management. +#[proxy( + interface = "org.freedesktop.ModemManager1", + default_service = "org.freedesktop.ModemManager1", + default_path = "/org/freedesktop/ModemManager1" +)] +pub trait MMManager { + /// Re-scan for available modem devices. + fn scan_devices(&self) -> zbus::Result<()>; + + /// Set the logging verbosity level. + fn set_logging(&self, level: &str) -> zbus::Result<()>; + + /// Inhibit or release a modem device by its UID. + fn inhibit_device(&self, uid: &str, inhibit: bool) -> zbus::Result<()>; + + /// ModemManager version string. + #[zbus(property)] + fn version(&self) -> zbus::Result; +} From 1196f2b22793811777c31ad501331adffd4c354a Mon Sep 17 00:00:00 2001 From: Akrm Al-Hakimi Date: Sat, 9 May 2026 09:44:52 -0400 Subject: [PATCH 3/9] feat(#400): add Modem proxy --- mmrs/src/dbus/modem.rs | 110 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 mmrs/src/dbus/modem.rs diff --git a/mmrs/src/dbus/modem.rs b/mmrs/src/dbus/modem.rs new file mode 100644 index 00000000..17d2f00a --- /dev/null +++ b/mmrs/src/dbus/modem.rs @@ -0,0 +1,110 @@ +//! ModemManager Modem proxy. + +use std::collections::HashMap; + +use zbus::proxy; +use zvariant::OwnedObjectPath; + +/// Proxy for the ModemManager1 Modem interface. +/// +/// Provides methods for enabling/disabling the modem, managing bearers, +/// resetting, and querying modem properties like signal quality and state. +/// +/// # Signals +/// +/// The `StateChanged` signal is emitted whenever the modem state changes. +/// Use `receive_state_changed()` to get a stream of state change events: +/// +/// ```ignore +/// let mut stream = modem_proxy.receive_modem_state_changed().await?; +/// while let Some(signal) = stream.next().await { +/// let args = signal.args()?; +/// println!("Old: {}, New: {}, Reason: {}", args.old, args.new, args.reason); +/// } +/// ``` +#[proxy( + interface = "org.freedesktop.ModemManager1.Modem", + default_service = "org.freedesktop.ModemManager1" +)] +pub trait MMModem { + /// Enable or disable the modem. + fn enable(&self, enable: bool) -> zbus::Result<()>; + + /// List paths to all bearer objects owned by this modem. + fn list_bearers(&self) -> zbus::Result>; + + /// Create a new packet data bearer from the given properties. + fn create_bearer( + &self, + properties: HashMap<&str, zvariant::Value<'_>>, + ) -> zbus::Result; + + /// Delete an existing bearer. + fn delete_bearer(&self, bearer: OwnedObjectPath) -> zbus::Result<()>; + + /// Reset the modem to its factory state. + fn reset(&self) -> zbus::Result<()>; + + /// Set the power state of the modem. + fn set_power_state(&self, state: u32) -> zbus::Result<()>; + + /// Send an AT command to the modem (direct AT channel access). + fn command(&self, cmd: &str, timeout: u32) -> zbus::Result; + + /// Path to the primary SIM object. + #[zbus(property)] + fn sim(&self) -> zbus::Result; + + /// Current modem state (see `MMModemState` enum values). + #[zbus(property)] + fn state(&self) -> zbus::Result; + + /// Current power state of the modem. + #[zbus(property)] + fn power_state(&self) -> zbus::Result; + + /// Bitmask of current access technologies in use. + #[zbus(property)] + fn access_technologies(&self) -> zbus::Result; + + /// Signal quality (percentage, recently-updated flag). + #[zbus(property)] + fn signal_quality(&self) -> zbus::Result<(u32, bool)>; + + /// Modem manufacturer name. + #[zbus(property)] + fn manufacturer(&self) -> zbus::Result; + + /// Modem model name. + #[zbus(property)] + fn model(&self) -> zbus::Result; + + /// Equipment identifier (IMEI for GSM, ESN/MEID for CDMA). + #[zbus(property)] + fn equipment_identifier(&self) -> zbus::Result; + + /// Maximum number of bearers this modem supports. + #[zbus(property)] + fn max_bearers(&self) -> zbus::Result; + + /// Maximum number of simultaneously active bearers. + #[zbus(property)] + fn max_active_bearers(&self) -> zbus::Result; + + /// Paths to all bearer objects owned by this modem. + #[zbus(property)] + fn bearers(&self) -> zbus::Result>; + + /// Signal emitted when the modem state changes. + /// + /// Named `modem_state_changed` to avoid conflict with the `state` + /// property's change stream. Use `receive_modem_state_changed()` to + /// subscribe to this signal. + /// + /// Arguments: + /// - `old`: The previous modem state + /// - `new`: The new modem state + /// - `reason`: The reason code for the state transition + #[zbus(signal, name = "StateChanged")] + fn modem_state_changed(&self, old: i32, new: i32, reason: u32); +} From a66345e952f1d63bfbf5deb437249e464ec5dd50 Mon Sep 17 00:00:00 2001 From: Akrm Al-Hakimi Date: Sat, 9 May 2026 09:44:52 -0400 Subject: [PATCH 4/9] feat(#400): add Modem.Simple proxy --- mmrs/src/dbus/modem_simple.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 mmrs/src/dbus/modem_simple.rs diff --git a/mmrs/src/dbus/modem_simple.rs b/mmrs/src/dbus/modem_simple.rs new file mode 100644 index 00000000..06481afb --- /dev/null +++ b/mmrs/src/dbus/modem_simple.rs @@ -0,0 +1,30 @@ +//! ModemManager Modem.Simple proxy. + +use std::collections::HashMap; + +use zbus::proxy; +use zvariant::{OwnedObjectPath, OwnedValue}; + +/// Proxy for the ModemManager1 Modem.Simple interface. +/// +/// Provides a simplified API for connecting, disconnecting, +/// and querying modem status in a single call. +#[proxy( + interface = "org.freedesktop.ModemManager1.Modem.Simple", + default_service = "org.freedesktop.ModemManager1" +)] +pub trait MMModemSimple { + /// Simple connect with the given connection properties. + /// + /// Returns the path to the connected bearer. + fn connect( + &self, + properties: HashMap<&str, zvariant::Value<'_>>, + ) -> zbus::Result; + + /// Disconnect a specific bearer, or all if `"/"` is passed. + fn disconnect(&self, bearer: OwnedObjectPath) -> zbus::Result<()>; + + /// Get the overall modem status as a property dictionary. + fn get_status(&self) -> zbus::Result>; +} From a4005187f585134a0c078117b64b172d188d56eb Mon Sep 17 00:00:00 2001 From: Akrm Al-Hakimi Date: Sat, 9 May 2026 09:44:52 -0400 Subject: [PATCH 5/9] feat(#400): add SIM proxy --- mmrs/src/dbus/sim.rs | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 mmrs/src/dbus/sim.rs diff --git a/mmrs/src/dbus/sim.rs b/mmrs/src/dbus/sim.rs new file mode 100644 index 00000000..cb7b8d7d --- /dev/null +++ b/mmrs/src/dbus/sim.rs @@ -0,0 +1,41 @@ +//! ModemManager SIM proxy. + +use zbus::proxy; + +/// Proxy for the ModemManager1 SIM interface. +/// +/// Provides methods for PIN/PUK management and access +/// to SIM identification properties. +#[proxy( + interface = "org.freedesktop.ModemManager1.Sim", + default_service = "org.freedesktop.ModemManager1" +)] +pub trait MMSim { + /// Send the SIM PIN to unlock the modem. + fn send_pin(&self, pin: &str) -> zbus::Result<()>; + + /// Send the PUK and set a new PIN. + fn send_puk(&self, puk: &str, pin: &str) -> zbus::Result<()>; + + /// Enable or disable PIN checking on the SIM. + fn enable_pin(&self, pin: &str, enabled: bool) -> zbus::Result<()>; + + /// Change the SIM PIN. + fn change_pin(&self, old_pin: &str, new_pin: &str) -> zbus::Result<()>; + + /// Whether this SIM slot is currently active. + #[zbus(property)] + fn active(&self) -> zbus::Result; + + /// SIM identifier (ICCID). + #[zbus(property)] + fn sim_identifier(&self) -> zbus::Result; + + /// International Mobile Subscriber Identity. + #[zbus(property)] + fn imsi(&self) -> zbus::Result; + + /// Name of the operator this SIM is registered with. + #[zbus(property)] + fn operator_name(&self) -> zbus::Result; +} From 01c9e8d18bc5b7919806fd112bf50721bea58a9a Mon Sep 17 00:00:00 2001 From: Akrm Al-Hakimi Date: Sat, 9 May 2026 09:44:53 -0400 Subject: [PATCH 6/9] feat(#400): add Bearer proxy --- mmrs/src/dbus/bearer.rs | 46 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 mmrs/src/dbus/bearer.rs diff --git a/mmrs/src/dbus/bearer.rs b/mmrs/src/dbus/bearer.rs new file mode 100644 index 00000000..f4504d32 --- /dev/null +++ b/mmrs/src/dbus/bearer.rs @@ -0,0 +1,46 @@ +//! ModemManager Bearer proxy. + +use std::collections::HashMap; + +use zbus::proxy; +use zvariant::OwnedValue; + +/// Proxy for the ModemManager1 Bearer interface. +/// +/// Represents a packet data connection. Provides methods to connect +/// and disconnect, plus properties for IP configuration and statistics. +#[proxy( + interface = "org.freedesktop.ModemManager1.Bearer", + default_service = "org.freedesktop.ModemManager1" +)] +pub trait MMBearer { + /// Activate the bearer and bring up the data connection. + fn connect(&self) -> zbus::Result<()>; + + /// Deactivate the bearer and tear down the data connection. + fn disconnect(&self) -> zbus::Result<()>; + + /// Network interface name for this bearer (e.g., "wwan0"). + #[zbus(property)] + fn interface(&self) -> zbus::Result; + + /// Whether the bearer is currently connected. + #[zbus(property)] + fn connected(&self) -> zbus::Result; + + /// Whether the bearer connection is suspended. + #[zbus(property)] + fn suspended(&self) -> zbus::Result; + + /// IPv4 configuration dictionary. + #[zbus(property)] + fn ip4_config(&self) -> zbus::Result>; + + /// IPv6 configuration dictionary. + #[zbus(property)] + fn ip6_config(&self) -> zbus::Result>; + + /// Connection statistics (bytes tx/rx, duration, etc.). + #[zbus(property)] + fn stats(&self) -> zbus::Result>; +} From 2dfde368df793721da6ede3c8b5ccd642b79ef59 Mon Sep 17 00:00:00 2001 From: Akrm Al-Hakimi Date: Sat, 9 May 2026 09:44:53 -0400 Subject: [PATCH 7/9] docs(#400): add mmrs crate README --- mmrs/README.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 mmrs/README.md diff --git a/mmrs/README.md b/mmrs/README.md new file mode 100644 index 00000000..113fdaa7 --- /dev/null +++ b/mmrs/README.md @@ -0,0 +1,23 @@ +# mmrs + +Rust bindings for [ModemManager](https://modemmanager.org/) over D-Bus. + +> **Status:** Early development. Tracking issue [#398](https://github.com/networkmanager-rs/nmrs/issues/398). + +## Requirements + +- Linux with ModemManager running +- Rust 1.90.0+ + +## Contributing + +Contributions welcome — see the [contributing guide](../docs/src/development/contributing.md). In short: + +- Follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) (`type(#issue): description`) +- Run `cargo +nightly fmt`, `cargo clippy -- -D warnings`, and `cargo test -p mmrs` before submitting +- Attach a relevant [issue](https://github.com/cachebag/nmrs/issues) when possible +- All tests must pass before merge + +## License + +Licensed under either of [MIT](../LICENSE-MIT) or [Apache-2.0](../LICENSE-APACHE), at your option. From 7580daf8eff051a4eff570c1651c416453b0c596 Mon Sep 17 00:00:00 2001 From: Akrm Al-Hakimi Date: Sat, 9 May 2026 09:46:54 -0400 Subject: [PATCH 8/9] chore: add CHANGELOG.md --- mmrs/CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 mmrs/CHANGELOG.md diff --git a/mmrs/CHANGELOG.md b/mmrs/CHANGELOG.md new file mode 100644 index 00000000..ba13edc9 --- /dev/null +++ b/mmrs/CHANGELOG.md @@ -0,0 +1,5 @@ +# Changelog + +All notable changes to the `mmrs` crate will be documented in this file. + +## [Unreleased] From 1aa098a3763b323b179a516cac51d293114bc289 Mon Sep 17 00:00:00 2001 From: Akrm Al-Hakimi Date: Sat, 9 May 2026 09:52:36 -0400 Subject: [PATCH 9/9] refactor: correct inhibit aou and fixed modem state method name --- mmrs/src/dbus/manager.rs | 7 +++++-- mmrs/src/dbus/modem.rs | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/mmrs/src/dbus/manager.rs b/mmrs/src/dbus/manager.rs index 6ff3270f..b0a60c9a 100644 --- a/mmrs/src/dbus/manager.rs +++ b/mmrs/src/dbus/manager.rs @@ -18,8 +18,11 @@ pub trait MMManager { /// Set the logging verbosity level. fn set_logging(&self, level: &str) -> zbus::Result<()>; - /// Inhibit or release a modem device by its UID. - fn inhibit_device(&self, uid: &str, inhibit: bool) -> zbus::Result<()>; + /// Inhibit a modem device by its UID and return the inhibition cookie. + fn inhibit_device(&self, uid: &str) -> zbus::Result; + + /// Release a previously acquired device inhibition cookie. + fn uninhibit_device(&self, cookie: u32) -> zbus::Result<()>; /// ModemManager version string. #[zbus(property)] diff --git a/mmrs/src/dbus/modem.rs b/mmrs/src/dbus/modem.rs index 17d2f00a..dc3af63e 100644 --- a/mmrs/src/dbus/modem.rs +++ b/mmrs/src/dbus/modem.rs @@ -13,7 +13,7 @@ use zvariant::OwnedObjectPath; /// # Signals /// /// The `StateChanged` signal is emitted whenever the modem state changes. -/// Use `receive_state_changed()` to get a stream of state change events: +/// Use `receive_modem_state_changed()` to get a stream of state change events: /// /// ```ignore /// let mut stream = modem_proxy.receive_modem_state_changed().await?; @@ -55,7 +55,7 @@ pub trait MMModem { #[zbus(property)] fn sim(&self) -> zbus::Result; - /// Current modem state (see `MMModemState` enum values). + /// Current modem state as the raw numeric ModemManager D-Bus value. #[zbus(property)] fn state(&self) -> zbus::Result;