Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 14 additions & 7 deletions libcdio-rs/src/cdio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Self> {
/// Initialize a hardware Cdio resource with read-write access.
pub(crate) fn with_device(device: Option<&CStr>) -> Option<Self> {
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 {
Expand Down
7 changes: 4 additions & 3 deletions libcdio-rs/src/drive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<PathBuf> {
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![];
}
Expand Down Expand Up @@ -72,7 +73,7 @@ impl Drive {
/// # Errors
/// If there are no drives connected, or the drive could not be opened.
pub fn new() -> Result<Self, DriveNotFoundError> {
Cdio::new(None, Cdio::DEVICE_DRIVER)
Cdio::with_device(None)
.ok_or(DriveNotFoundError)
.map(|cdio| Self { cdio })
}
Expand All @@ -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,
})?;
Expand Down
7 changes: 5 additions & 2 deletions libcdio-rs/src/mmc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,15 @@ pub use get_config::*;
pub use get_event_status::*;
pub use read_disc_info::*;
pub use read_subchannel::*;
pub use set_cd_speed::*;
pub use test_unit_ready::*;
pub use read_toc::*;

mod get_config;
mod get_event_status;
mod read_disc_info;
mod read_subchannel;
mod set_cd_speed;
mod test_unit_ready;
mod read_toc;

Expand Down Expand Up @@ -77,7 +79,7 @@ impl Mmc {
/// # Errors
/// If an MMC capable device could not be found.
pub fn new() -> Result<Mmc, MmcNotFoundError> {
Cdio::new(None, Cdio::DEVICE_DRIVER)
Cdio::with_device(None)
.map(|cdio| Self { cdio })
.filter(|mmc| mmc.level().is_ok())
.ok_or(MmcNotFoundError)
Expand All @@ -95,7 +97,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,
Expand Down Expand Up @@ -365,6 +367,7 @@ pub enum OsError {
enum MmcCommand {
#[allow(unused)]
GetConfiguration = 0x46,
SetCdSpeed = 0xBB,
TestUnitReady = 0x00,
ReadDiscInfo = 0x51,
ReadToc = 0x43,
Expand Down
82 changes: 82 additions & 0 deletions libcdio-rs/src/mmc/set_cd_speed.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Copyright (C) 2026 Shiva Kiran Koninty <shiva@skran.xyz>
//
// 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 <https://www.gnu.org/licenses/>.

//! Routines based on MMC `SET CD SPEED`.

use displaydoc::Display;
use thiserror::Error;

use crate::{
Mmc,
mmc::{Cdb, MmcCommand, MmcDirection, MmcError},
};

/// Routines based on MMC `SET CD SPEED`.
impl Mmc {
/// Set the read and write speeds of the drive, in kilo bytes per second.
pub fn set_cd_speed(
&self,
rotation_mode: RotationMode,
read_speed: u16,
write_speed: u16,
) -> Result<(), MmcSetCdSpeedError> {
let mut cdb = Cdb::default();
cdb[0] = MmcCommand::SetCdSpeed as u8;
cdb[1] = rotation_mode as u8;
cdb[2..4].copy_from_slice(&read_speed.to_be_bytes());
cdb[4..6].copy_from_slice(&write_speed.to_be_bytes());

self.run_command(Some(MmcDirection::Write), &mut [], cdb)?;

Ok(())
}
}

/// Rotation mode used by the drive.
#[repr(u8)]
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum RotationMode {
/// Constant Angular Velocity: Maintains a fixed rotation rate.
/// Provides faster seek times.
#[default]
Cav = 0b00,

/// Constant Linear Velocity: Variable rotation rate.
/// Provides consistent data rates.
Clv = 0b01,
}

/// error from a `SET CD SPEED` command.
#[derive(Debug, Display, Error)]
pub struct MmcSetCdSpeedError {
#[from]
pub source: MmcError,
}

#[cfg(test)]
mod tests {
use super::*;

#[test_log::test(test)]
#[ignore = "requires a drive with mmc"]
fn set_cd_speed() {
Mmc::new()
.unwrap()
.set_cd_speed(RotationMode::Cav, 0xffff, 0xffff)
.unwrap();
}
}