From 5ca756579eb780c1bd8b3341ae8943658905899a Mon Sep 17 00:00:00 2001 From: Shiva Kiran Koninty Date: Tue, 28 Jul 2026 10:55:26 +0530 Subject: [PATCH 1/6] cli: Add mmc-tool --- libcdio-cli/Cargo.toml | 8 ++++++- libcdio-cli/src/mmc-tool/cli.rs | 37 ++++++++++++++++++++++++++++++++ libcdio-cli/src/mmc-tool/main.rs | 36 +++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 libcdio-cli/src/mmc-tool/cli.rs create mode 100644 libcdio-cli/src/mmc-tool/main.rs diff --git a/libcdio-cli/Cargo.toml b/libcdio-cli/Cargo.toml index b381648..041416f 100644 --- a/libcdio-cli/Cargo.toml +++ b/libcdio-cli/Cargo.toml @@ -30,11 +30,17 @@ name = "iso-read-rs" path = "src/iso-read/main.rs" required-features = ["iso-read"] +[[bin]] +name = "mmc-tool-rs" +path = "src/mmc-tool/main.rs" +required-features = ["mmc-tool"] + [features] -default = ["cd-drive", "iso-info", "iso-read"] +default = ["cd-drive", "iso-info", "iso-read", "mmc-tool"] cd-drive = [] iso-info = ["libcdio-rs/iso9660", "libcdio-rs/udf", "dep:time"] iso-read = ["libcdio-rs/iso9660", "libcdio-rs/udf"] +mmc-tool = [] [dev-dependencies] assert_cmd = { version = "2.2.2", features = ["color"] } diff --git a/libcdio-cli/src/mmc-tool/cli.rs b/libcdio-cli/src/mmc-tool/cli.rs new file mode 100644 index 0000000..660c44e --- /dev/null +++ b/libcdio-cli/src/mmc-tool/cli.rs @@ -0,0 +1,37 @@ +// Copyright (C) 2026 Shiva Kiran Koninty +// +// This file is part of libcdio-cli. +// +// libcdio-cli is free software: you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// libcdio-cli is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with libcdio-cli. If not, see . + +use std::path::PathBuf; + +use clap::Parser; + +#[derive(Debug, Parser)] +#[command(arg_required_else_help = true, long_about = libcdio_cli::HEADER, version)] +pub struct Cli { + /// Show debugging information (1 = Error, 2 = Warn, 3 = Info, 4 = Debug) + #[arg( + default_value = "2", + short, + long, + value_name = "LEVEL", + value_parser = clap::value_parser!(u8).range(1..=4), + )] + pub debug: u8, + + /// Path to an MMC device. + pub device: Option, +} diff --git a/libcdio-cli/src/mmc-tool/main.rs b/libcdio-cli/src/mmc-tool/main.rs new file mode 100644 index 0000000..0acbeb4 --- /dev/null +++ b/libcdio-cli/src/mmc-tool/main.rs @@ -0,0 +1,36 @@ +// Copyright (C) 2026 Shiva Kiran Koninty +// +// This file is part of libcdio-cli. +// +// libcdio-cli is free software: you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// libcdio-cli is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with libcdio-cli. If not, see . + +use anyhow::Result; +use clap::Parser; +use libcdio_rs::Mmc; + +use crate::cli::Cli; + +mod cli; + +fn main() -> Result<()> { + let cli = Cli::parse(); + libcdio_cli::setup_logs(cli.debug); + let _mmc = if let Some(device) = cli.device { + Mmc::with_device(device)? + } else { + Mmc::new()? + }; + + Ok(()) +} From 8b810629f20419e3c256ee13693eaaf5f3833709 Mon Sep 17 00:00:00 2001 From: Shiva Kiran Koninty Date: Tue, 28 Jul 2026 12:47:25 +0530 Subject: [PATCH 2/6] cli/mmc-tool: Add eject and close tray --- libcdio-cli/src/mmc-tool/cli.rs | 17 ++++++++++++++++- libcdio-cli/src/mmc-tool/main.rs | 9 ++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/libcdio-cli/src/mmc-tool/cli.rs b/libcdio-cli/src/mmc-tool/cli.rs index 660c44e..ffc8966 100644 --- a/libcdio-cli/src/mmc-tool/cli.rs +++ b/libcdio-cli/src/mmc-tool/cli.rs @@ -17,7 +17,7 @@ use std::path::PathBuf; -use clap::Parser; +use clap::{Args, Parser}; #[derive(Debug, Parser)] #[command(arg_required_else_help = true, long_about = libcdio_cli::HEADER, version)] @@ -34,4 +34,19 @@ pub struct Cli { /// Path to an MMC device. pub device: Option, + + #[command(flatten)] + pub actions: MmcActions, +} + +#[derive(Args, Debug)] +#[group(required = true, multiple = false)] +pub struct MmcActions { + /// Eject the drive + #[arg(short, long)] + pub eject: bool, + + /// Close the tray, if present + #[arg(short, long)] + pub close_tray: bool, } diff --git a/libcdio-cli/src/mmc-tool/main.rs b/libcdio-cli/src/mmc-tool/main.rs index 0acbeb4..9417a2b 100644 --- a/libcdio-cli/src/mmc-tool/main.rs +++ b/libcdio-cli/src/mmc-tool/main.rs @@ -26,11 +26,18 @@ mod cli; fn main() -> Result<()> { let cli = Cli::parse(); libcdio_cli::setup_logs(cli.debug); - let _mmc = if let Some(device) = cli.device { + let mmc = if let Some(device) = cli.device { Mmc::with_device(device)? } else { Mmc::new()? }; + if cli.actions.eject { + mmc.allow_media_removal()?; + mmc.eject()?; + } else if cli.actions.close_tray { + mmc.close_tray()?; + } + Ok(()) } From 2b31b07bd1cba517b340b57f18493007a02a2a47 Mon Sep 17 00:00:00 2001 From: Shiva Kiran Koninty Date: Tue, 28 Jul 2026 13:08:03 +0530 Subject: [PATCH 3/6] lib/mmc: Add method to set power state --- libcdio-rs/src/mmc/start_stop_unit.rs | 31 +++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/libcdio-rs/src/mmc/start_stop_unit.rs b/libcdio-rs/src/mmc/start_stop_unit.rs index a49f9a7..12b601a 100644 --- a/libcdio-rs/src/mmc/start_stop_unit.rs +++ b/libcdio-rs/src/mmc/start_stop_unit.rs @@ -41,24 +41,40 @@ impl Mmc { Ok(()) } + /// Set power state. + pub fn set_power_state(&self, state: PowerCondition) -> Result<(), MmcSetPowerStateError> { + self.start_stop_unit(StartStopOperation::Power(state))?; + Ok(()) + } + fn start_stop_unit(&self, operation: StartStopOperation) -> Result<(), MmcStartStopError> { let mut cdb = Cdb::default(); cdb[0] = MmcCommand::StartStopUnit as u8; cdb[1] = 0; // not using the immediate bit for now if let StartStopOperation::Jump { layer_number } = operation { - cdb[3] = layer_number & 0b11 + cdb[3] = layer_number & LAYER_NUM_BITMASK; + cdb[4] |= 1 << FORMAT_LAYER_BITPOS; } - cdb[4] = match operation { + // loej and start + cdb[4] |= match operation { StartStopOperation::StartDisc => 0b01, StartStopOperation::EjectDisc => 0b10, StartStopOperation::LoadStartDisc | StartStopOperation::Jump { .. } => 0b11, _ => 0b00, }; + if let StartStopOperation::Power(pow_cond) = operation { + cdb[4] |= (pow_cond as u8 & POWER_COND_BITMASK) << POWER_COND_BITPOS; + } self.run_command(Some(MmcDirection::Write), &mut [], cdb)?; - Ok(()) + return Ok(()); + + const LAYER_NUM_BITMASK: u8 = 0b11; + const FORMAT_LAYER_BITPOS: usize = 2; + const POWER_COND_BITMASK: u8 = 0b1111; + const POWER_COND_BITPOS: usize = 4; } } @@ -76,6 +92,13 @@ pub struct MmcCloseTrayError { pub source: MmcStartStopError, } +/// could not set power state of MMC device +#[derive(Debug, Display, Error)] +pub struct MmcSetPowerStateError { + #[from] + pub source: MmcStartStopError, +} + /// error from a `START STOP UNIT` command #[derive(Debug, Display, Error)] pub struct MmcStartStopError { @@ -103,7 +126,7 @@ enum StartStopOperation { /// A power state as defined under MMC `START STOP UNIT` #[allow(unused)] -enum PowerCondition { +pub enum PowerCondition { Idle = 0x2, Standby = 0x3, Sleep = 0x5, From b025eb6dc5ffb4fb520e090161df7f93d9003360 Mon Sep 17 00:00:00 2001 From: Shiva Kiran Koninty Date: Tue, 28 Jul 2026 13:16:44 +0530 Subject: [PATCH 4/6] cli/mmc-tool: Add option to put the device into standby --- libcdio-cli/src/mmc-tool/cli.rs | 4 ++++ libcdio-cli/src/mmc-tool/main.rs | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/libcdio-cli/src/mmc-tool/cli.rs b/libcdio-cli/src/mmc-tool/cli.rs index ffc8966..a556ec7 100644 --- a/libcdio-cli/src/mmc-tool/cli.rs +++ b/libcdio-cli/src/mmc-tool/cli.rs @@ -49,4 +49,8 @@ pub struct MmcActions { /// Close the tray, if present #[arg(short, long)] pub close_tray: bool, + + /// Put the device into standby + #[arg(short, long)] + pub standby: bool, } diff --git a/libcdio-cli/src/mmc-tool/main.rs b/libcdio-cli/src/mmc-tool/main.rs index 9417a2b..4bfb797 100644 --- a/libcdio-cli/src/mmc-tool/main.rs +++ b/libcdio-cli/src/mmc-tool/main.rs @@ -17,7 +17,7 @@ use anyhow::Result; use clap::Parser; -use libcdio_rs::Mmc; +use libcdio_rs::{Mmc, mmc::PowerCondition}; use crate::cli::Cli; @@ -37,6 +37,8 @@ fn main() -> Result<()> { mmc.eject()?; } else if cli.actions.close_tray { mmc.close_tray()?; + } else if cli.actions.standby { + mmc.set_power_state(PowerCondition::Standby)?; } Ok(()) From f778827ab1d5337c421839f8593a772f1d053ea5 Mon Sep 17 00:00:00 2001 From: Shiva Kiran Koninty Date: Tue, 28 Jul 2026 13:40:01 +0530 Subject: [PATCH 5/6] cli/mmc-tool: Add options to get MCN and Hardware identifers --- libcdio-cli/src/mmc-tool/cli.rs | 8 ++++++++ libcdio-cli/src/mmc-tool/main.rs | 13 ++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/libcdio-cli/src/mmc-tool/cli.rs b/libcdio-cli/src/mmc-tool/cli.rs index a556ec7..587c1f6 100644 --- a/libcdio-cli/src/mmc-tool/cli.rs +++ b/libcdio-cli/src/mmc-tool/cli.rs @@ -53,4 +53,12 @@ pub struct MmcActions { /// Put the device into standby #[arg(short, long)] pub standby: bool, + + /// Get the MCN (Media Catalog Number) of the media + #[arg(short, long)] + pub mcn: bool, + + /// Get hardware identifiers (Product, Vendor and Revision) + #[arg(short, long)] + pub inquiry: bool, } diff --git a/libcdio-cli/src/mmc-tool/main.rs b/libcdio-cli/src/mmc-tool/main.rs index 4bfb797..e798c97 100644 --- a/libcdio-cli/src/mmc-tool/main.rs +++ b/libcdio-cli/src/mmc-tool/main.rs @@ -15,7 +15,7 @@ // You should have received a copy of the GNU General Public License // along with libcdio-cli. If not, see . -use anyhow::Result; +use anyhow::{Context, Result}; use clap::Parser; use libcdio_rs::{Mmc, mmc::PowerCondition}; @@ -39,6 +39,17 @@ fn main() -> Result<()> { mmc.close_tray()?; } else if cli.actions.standby { mmc.set_power_state(PowerCondition::Standby)?; + } else if cli.actions.mcn { + let mcn = mmc + .media_catalog_number() + .context("could not get MCN")? + .context("current media does not have a Media Catalog Number")?; + println!("{}", mcn); + } else if cli.actions.inquiry { + let ident = mmc.hardware_identifiers()?; + println!("Product: {}", ident.product); + println!("Vendor: {}", ident.vendor); + println!("Revision: {}", ident.revision); } Ok(()) From 55b950afcc0500ea08938d6734c648cd3af1a715 Mon Sep 17 00:00:00 2001 From: Shiva Kiran Koninty Date: Tue, 28 Jul 2026 14:39:38 +0530 Subject: [PATCH 6/6] cli/mmc-tool: Add options to set drive speed --- libcdio-cli/src/mmc-tool/cli.rs | 7 +++++++ libcdio-cli/src/mmc-tool/main.rs | 10 +++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/libcdio-cli/src/mmc-tool/cli.rs b/libcdio-cli/src/mmc-tool/cli.rs index 587c1f6..1435b84 100644 --- a/libcdio-cli/src/mmc-tool/cli.rs +++ b/libcdio-cli/src/mmc-tool/cli.rs @@ -61,4 +61,11 @@ pub struct MmcActions { /// Get hardware identifiers (Product, Vendor and Revision) #[arg(short, long)] pub inquiry: bool, + + /// Set the drive read and write speed in KB/s. + /// + /// Falls back to the nearest supported value if the provided value is not + /// supported. + #[arg(short = 'S', long)] + pub speed: Option, } diff --git a/libcdio-cli/src/mmc-tool/main.rs b/libcdio-cli/src/mmc-tool/main.rs index e798c97..dd1b1b9 100644 --- a/libcdio-cli/src/mmc-tool/main.rs +++ b/libcdio-cli/src/mmc-tool/main.rs @@ -17,7 +17,10 @@ use anyhow::{Context, Result}; use clap::Parser; -use libcdio_rs::{Mmc, mmc::PowerCondition}; +use libcdio_rs::{ + Mmc, + mmc::{PowerCondition, RotationMode}, +}; use crate::cli::Cli; @@ -50,6 +53,11 @@ fn main() -> Result<()> { println!("Product: {}", ident.product); println!("Vendor: {}", ident.vendor); println!("Revision: {}", ident.revision); + } else if let Some(speed) = cli.actions.speed { + // some drives may not support CLV (Constant Linear Velocity) + if mmc.set_cd_speed(RotationMode::Clv, speed, speed).is_err() { + mmc.set_cd_speed(RotationMode::Cav, speed, speed)?; + } } Ok(())