From 1022cc9d8452074da62cb9b0614826566c993daa Mon Sep 17 00:00:00 2001 From: Shiva Kiran Koninty Date: Thu, 16 Jul 2026 11:12:38 +0530 Subject: [PATCH 1/2] lib/cdio: Replace `Cdio::new()` with `Cdio::with_device()` There are two major types of drivers in Cdio. - OS - Images Thus, rather than have a generic `new()` method with multiple (unused) initialization options, have two methods `with_device()` and `with_image()`. This commit introduces `with_device()`. --- libcdio-rs/src/cdio.rs | 21 ++++++++++++++------- libcdio-rs/src/drive.rs | 7 ++++--- libcdio-rs/src/mmc.rs | 4 ++-- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/libcdio-rs/src/cdio.rs b/libcdio-rs/src/cdio.rs index a46e447..cd3645b 100644 --- a/libcdio-rs/src/cdio.rs +++ b/libcdio-rs/src/cdio.rs @@ -32,22 +32,29 @@ pub(crate) struct Cdio { } impl Cdio { - /// Create a new Cdio object with the given parameters. - pub(crate) fn new(device: Option<&CStr>, driver: driver_id_t) -> Option { + /// Initialize a hardware Cdio resource with read-write access. + pub(crate) fn with_device(device: Option<&CStr>) -> Option { let source = device.map(|s| s.as_ptr()).unwrap_or(ptr::null()); - NonNull::new(Self::open(source, driver)).map(|cdio| Self { cdio }) + NonNull::new(Self::open(true, source, driver_id_t_DRIVER_DEVICE)).map(|cdio| Self { cdio }) } - fn open(source: *const c_char, driver: driver_id_t) -> *mut CdIo_t { + fn open(allow_writes: bool, source: *const c_char, driver: driver_id_t) -> *mut CdIo_t { logging::init_logger(); + let access_mode = if allow_writes { + RW_ACCESS_MODE.as_ptr() + } else { + ptr::null() + }; // SAFETY: This invokes cdio_init(), which mutates a static variable. // CDIO_LAST_DRIVER_LOCK is held to prevent data races. let _lock = CDIO_LAST_DRIVER_LOCK.lock().unwrap(); - unsafe { libcdio_sys::cdio_open(source, driver) } - } + return unsafe { libcdio_sys::cdio_open_am(source, driver, access_mode) }; - pub(crate) const DEVICE_DRIVER: driver_id_t = driver_id_t_DRIVER_DEVICE; + /// Although prefixed "MMC", this does imply read-write for all + /// operations + static RW_ACCESS_MODE: &CStr = c"MMC_RDWR"; + } } impl Deref for Cdio { diff --git a/libcdio-rs/src/drive.rs b/libcdio-rs/src/drive.rs index cea98df..ab88260 100644 --- a/libcdio-rs/src/drive.rs +++ b/libcdio-rs/src/drive.rs @@ -40,7 +40,8 @@ impl Drive { /// Get a list of connected drives. /// The values could be used with [`Self::with_drive()`]. pub fn drives() -> Vec { - let drive_list = unsafe { libcdio_sys::cdio_get_devices(Cdio::DEVICE_DRIVER) }; + let drive_list = + unsafe { libcdio_sys::cdio_get_devices(libcdio_sys::driver_id_t_DRIVER_DEVICE) }; if drive_list.is_null() { return vec![]; } @@ -72,7 +73,7 @@ impl Drive { /// # Errors /// If there are no drives connected, or the drive could not be opened. pub fn new() -> Result { - Cdio::new(None, Cdio::DEVICE_DRIVER) + Cdio::with_device(None) .ok_or(DriveNotFoundError) .map(|cdio| Self { cdio }) } @@ -91,7 +92,7 @@ impl Drive { source: WithDriveErrorKind::DriveHasNullChar(err), } })?; - let cdio = Cdio::new(Some(&drive), Cdio::DEVICE_DRIVER).ok_or_else(|| WithDriveError { + let cdio = Cdio::with_device(Some(&drive)).ok_or_else(|| WithDriveError { drive: os_string_from_bytes_safe(drive.into_bytes()).into(), source: WithDriveErrorKind::CouldNotOpenAsDrive, })?; diff --git a/libcdio-rs/src/mmc.rs b/libcdio-rs/src/mmc.rs index 36e09d5..bdfadef 100644 --- a/libcdio-rs/src/mmc.rs +++ b/libcdio-rs/src/mmc.rs @@ -71,7 +71,7 @@ impl Mmc { /// # Errors /// If an MMC capable device could not be found. pub fn new() -> Result { - Cdio::new(None, Cdio::DEVICE_DRIVER) + Cdio::with_device(None) .map(|cdio| Self { cdio }) .filter(|mmc| mmc.level().is_ok()) .ok_or(MmcNotFoundError) @@ -89,7 +89,7 @@ impl Mmc { source: WithDeviceErrorKind::DeviceHasNullChar(err), } })?; - let Some(cdio) = Cdio::new(Some(&device), Cdio::DEVICE_DRIVER) else { + let Some(cdio) = Cdio::with_device(Some(&device)) else { return Err(WithDeviceError { device: os_string_from_bytes_safe(device.into_bytes()).into(), source: WithDeviceErrorKind::CouldNotOpenDevice, From ccfa82594e2ee4bb97fbd6468f749279f62558c3 Mon Sep 17 00:00:00 2001 From: Shiva Kiran Koninty Date: Wed, 15 Jul 2026 21:22:15 +0530 Subject: [PATCH 2/2] lib/mmc: Add routines for `START STOP UNIT` --- libcdio-rs/src/mmc.rs | 3 + libcdio-rs/src/mmc/start_stop_unit.rs | 112 ++++++++++++++++++++++++++ 2 files changed, 115 insertions(+) create mode 100644 libcdio-rs/src/mmc/start_stop_unit.rs diff --git a/libcdio-rs/src/mmc.rs b/libcdio-rs/src/mmc.rs index bdfadef..7b04ac4 100644 --- a/libcdio-rs/src/mmc.rs +++ b/libcdio-rs/src/mmc.rs @@ -26,10 +26,12 @@ use std::{ pub use get_config::*; pub use get_event_status::*; pub use read_subchannel::*; +pub use start_stop_unit::*; mod get_config; mod get_event_status; mod read_subchannel; +mod start_stop_unit; use docsplay::Display; use libcdio_sys::{ @@ -359,6 +361,7 @@ pub enum OsError { #[derive(Clone, Copy, Debug)] enum MmcCommand { GetConfiguration = 0x46, + StartStopUnit = 0x1B, } #[cfg(test)] diff --git a/libcdio-rs/src/mmc/start_stop_unit.rs b/libcdio-rs/src/mmc/start_stop_unit.rs new file mode 100644 index 0000000..a49f9a7 --- /dev/null +++ b/libcdio-rs/src/mmc/start_stop_unit.rs @@ -0,0 +1,112 @@ +// Copyright (C) 2026 Shiva Kiran Koninty +// +// This file is part of libcdio-rs. +// +// libcdio-rs 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-rs 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-rs. If not, see . + +//! Routines based on MMC `START STOP UNIT`. + +use displaydoc::Display; +use thiserror::Error; + +use crate::{ + Mmc, + mmc::{Cdb, MmcCommand, MmcDirection, MmcError}, +}; + +/// Routines based on MMC `START STOP UNIT`. +impl Mmc { + /// Eject the media, if permitted. + /// + /// This might require a prior allow eject operation. + pub fn eject(&self) -> Result<(), MmcEjectError> { + self.start_stop_unit(StartStopOperation::EjectDisc)?; + Ok(()) + } + + /// Close the tray. + pub fn close_tray(&self) -> Result<(), MmcCloseTrayError> { + self.start_stop_unit(StartStopOperation::LoadStartDisc)?; + 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[4] = match operation { + StartStopOperation::StartDisc => 0b01, + StartStopOperation::EjectDisc => 0b10, + StartStopOperation::LoadStartDisc | StartStopOperation::Jump { .. } => 0b11, + _ => 0b00, + }; + + self.run_command(Some(MmcDirection::Write), &mut [], cdb)?; + + Ok(()) + } +} + +/// could not eject MMC device +#[derive(Debug, Display, Error)] +pub struct MmcEjectError { + #[from] + pub source: MmcStartStopError, +} + +/// could not close tray of the MMC device +#[derive(Debug, Display, Error)] +pub struct MmcCloseTrayError { + #[from] + pub source: MmcStartStopError, +} + +/// error from a `START STOP UNIT` command +#[derive(Debug, Display, Error)] +pub struct MmcStartStopError { + #[from] + pub source: MmcError, +} + +/// Operations of the `START STOP UNIT` command +#[allow(unused)] +enum StartStopOperation { + StopDisc, + StartDisc, + EjectDisc, + LoadStartDisc, + + /// Change the online format-layer to the specified value for hybrid discs. + /// Only the last two bits will be set. + Jump { + layer_number: u8, + }, + + /// Place the device in the specified power condition + Power(PowerCondition), +} + +/// A power state as defined under MMC `START STOP UNIT` +#[allow(unused)] +enum PowerCondition { + Idle = 0x2, + Standby = 0x3, + Sleep = 0x5, +} + +// TODO: add manual tests