libcdio_rs/cdio.rs
1// Copyright (C) 2026 Shiva Kiran Koninty <shiva@skran.xyz>
2//
3// This file is part of libcdio-rs.
4//
5// libcdio-rs is free software: you can redistribute it and/or
6// modify it under the terms of the GNU General Public License as
7// published by the Free Software Foundation, either version 3 of the
8// License, or (at your option) any later version.
9//
10// libcdio-rs is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13// General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with libcdio-rs. If not, see <https://www.gnu.org/licenses/>.
17
18use std::{
19 ffi::{CStr, c_char},
20 ops::Deref,
21 ptr::{self, NonNull},
22 sync::Mutex,
23};
24
25use libcdio_sys::{CdIo_t, driver_id_t, driver_id_t_DRIVER_DEVICE};
26
27use crate::logging;
28
29/// The Cdio type.
30pub(crate) struct Cdio {
31 pub(crate) cdio: NonNull<CdIo_t>,
32}
33
34impl Cdio {
35 /// Initialize a hardware Cdio resource with read-write access.
36 pub(crate) fn with_device(device: Option<&CStr>) -> Option<Self> {
37 let source = device.map(|s| s.as_ptr()).unwrap_or(ptr::null());
38 NonNull::new(Self::open(true, source, driver_id_t_DRIVER_DEVICE)).map(|cdio| Self { cdio })
39 }
40
41 fn open(allow_writes: bool, source: *const c_char, driver: driver_id_t) -> *mut CdIo_t {
42 logging::init_logger();
43 let access_mode = if allow_writes {
44 RW_ACCESS_MODE.as_ptr()
45 } else {
46 ptr::null()
47 };
48
49 // SAFETY: This invokes cdio_init(), which mutates a static variable.
50 // CDIO_LAST_DRIVER_LOCK is held to prevent data races.
51 let _lock = CDIO_INIT_LOCK.lock().unwrap();
52 return unsafe { libcdio_sys::cdio_open_am(source, driver, access_mode) };
53
54 /// Although prefixed "MMC", this does imply read-write for all
55 /// operations
56 static RW_ACCESS_MODE: &CStr = c"MMC_RDWR";
57 }
58}
59
60impl Deref for Cdio {
61 type Target = NonNull<CdIo_t>;
62
63 fn deref(&self) -> &Self::Target {
64 &self.cdio
65 }
66}
67
68impl Drop for Cdio {
69 fn drop(&mut self) {
70 let _lock = CDIO_INIT_LOCK.lock().unwrap();
71
72 // SAFETY: This method invokes modifies a static variable.
73 // CDIO_LAST_DRIVER_LOCK is held to prevent data races.
74 unsafe { libcdio_sys::cdio_destroy(self.cdio.as_ptr()) }
75 }
76}
77
78/// A lock that must be held before any routine that initializes or
79/// destroys `CdIo_t`.
80/// It was found that the GNU/Linux driver initialization routine,
81/// is NOT thread safe as of libcdio v2.4.0.
82/// Apart from that, this also guards the use of a private static
83/// named `CdIo_last_driver`, used by `CdIo_t` during init
84/// and cleanup.
85pub(crate) static CDIO_INIT_LOCK: Mutex<()> = Mutex::new(());