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] 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. 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>; +} diff --git a/mmrs/src/dbus/manager.rs b/mmrs/src/dbus/manager.rs new file mode 100644 index 00000000..b0a60c9a --- /dev/null +++ b/mmrs/src/dbus/manager.rs @@ -0,0 +1,30 @@ +//! 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 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)] + fn version(&self) -> zbus::Result; +} 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; diff --git a/mmrs/src/dbus/modem.rs b/mmrs/src/dbus/modem.rs new file mode 100644 index 00000000..dc3af63e --- /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_modem_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 as the raw numeric ModemManager D-Bus value. + #[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); +} 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>; +} 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; +}