From 20965bbafc9b3193c0cdb364898d4473b87046de Mon Sep 17 00:00:00 2001
From: G2-Games <ke0bhogsg@gmail.com>
Date: Mon, 27 Jan 2025 15:28:35 -0600
Subject: [PATCH] Initally working!

---
 .gitignore        |   1 +
 Cargo.lock        | 109 ++++++++++++++++++++++++++++++++
 Cargo.toml        |   9 +++
 src/constants.rs  | 157 ++++++++++++++++++++++++++++++++++++++++++++++
 src/main.rs       |  65 +++++++++++++++++++
 src/structures.rs |  77 +++++++++++++++++++++++
 6 files changed, 418 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 Cargo.lock
 create mode 100644 Cargo.toml
 create mode 100644 src/constants.rs
 create mode 100644 src/main.rs
 create mode 100644 src/structures.rs

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ea8c4bf
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..22bb23b
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,109 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "autocfg"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
+
+[[package]]
+name = "bitflags"
+version = "2.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36"
+
+[[package]]
+name = "cd_read"
+version = "0.1.0"
+dependencies = [
+ "nix",
+ "num-derive",
+ "num-traits",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "cfg_aliases"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
+
+[[package]]
+name = "libc"
+version = "0.2.169"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
+
+[[package]]
+name = "nix"
+version = "0.29.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
+dependencies = [
+ "bitflags",
+ "cfg-if",
+ "cfg_aliases",
+ "libc",
+]
+
+[[package]]
+name = "num-derive"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.93"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.38"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.96"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "11cd88e12b17c6494200a9c1b683a04fcac9573ed74cd1b62aeb2727c5592243"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..f5b8b7c
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "cd_read"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+nix = { version = "0.29.0", features = ["ioctl"] }
+num-derive = "0.4.2"
+num-traits = "0.2.19"
diff --git a/src/constants.rs b/src/constants.rs
new file mode 100644
index 0000000..889ba9a
--- /dev/null
+++ b/src/constants.rs
@@ -0,0 +1,157 @@
+//! Read more about the meanings of the below bytes here:
+//! <https://docs.kernel.org/userspace-api/ioctl/cdrom.html>
+
+use num_traits::ToPrimitive;
+
+/// CDROM ioctl byte, from <linux/cdrom.h>
+pub const IOC_BYTE: u8 = 0x53;
+
+#[repr(u8)]
+#[derive(FromPrimitive, ToPrimitive)]
+#[derive(Debug, Clone, Copy)]
+pub enum Operations {
+    /// Pause Audio Operation
+    Pause = 0x01,
+    /// Resume paused Audio Operation
+    Resume = 0x02,
+    /// Play Audio MSF (struct cdrom_msf)
+    PlayMsf = 0x03,
+    /// Play Audio Track/index (struct cdrom_ti)
+    PlayTrackIndex = 0x04,
+    /// Read TOC header (struct cdrom_tochdr)
+    ReadTocHeader = 0x05,
+    /// Read TOC entry (struct cdrom_tocentry)
+    ReadTocEntry = 0x06,
+    /// Stop the cdrom drive
+    Stop = 0x07,
+    /// Start the cdrom drive
+    Start = 0x08,
+    /// Ejects the cdrom media
+    Eject = 0x09,
+    /// Control output volume (struct cdrom_volctrl)
+    VolumeControl = 0x0a,
+    /// Read subchannel data (struct cdrom_subchnl)
+    SubChannel = 0x0b,
+    /// Read CDROM mode 2 data (2336 Bytes) (struct cdrom_subchnl)
+    ReadMode2 = 0x0c,
+    /// Read CDROM mode 1 data (2048 Bytes) (struct cdrom_read)
+    ReadMode1 = 0x0d,
+    /// (struct cdrom_read_audio)
+    ReadAudio = 0x0e,
+    /// Enable (1)/Disable (0) auto-ejecting
+    EjectSoftware = 0x0f,
+    /// Obtain the start-of-last-session address of multi session disks (struct cdrom_multisession)
+    MultiSession = 0x10,
+    /// Obtain the "Universal Product Code" if available (struct cdrom_mcn)
+    GetMcn = 0x11,
+    /// Hard-reset the drive
+    Reset = 0x12,
+    /// Get the drive's volume setting (struct cdrom_volctrl)
+    VolumeRead = 0x13,
+    /// Read data in raw mode (2352 Bytes) (struct cdrom_read)
+    ReadRaw = 0x14,
+
+
+    // These ioctls are only used in aztcd.c and optcd.c
+
+    /// Read data in cooked mode (???)
+    ReadCooked = 0x15,
+    /// Seek msf address
+    Seek = 0x16,
+
+
+    // This ioctl is only used by the scsi-cd driver.
+    // It is for playing audio in logtal block addresing mode.
+
+    /// (struct cdrom_blk)
+    PlayBlock = 0x17,
+
+
+    // These ioctls are only used in optcd.c
+
+    /// Read all 2646 bytes
+    ReadAll = 0x18,
+
+
+    // These ioctls were only in (now removed) ide-cd.c for controlling
+    // drive spindown time.  They should be implemented in the
+    // Uniform driver, via generic packet commands, GPCMD_MODE_SELECT_10,
+    // GPCMD_MODE_SENSE_10 and the GPMODE_POWER_PAGE...
+    // -Erik
+    GetSpindown = 0x1d,
+    SetSpindown = 0x1e,
+
+    // These ioctls are implemented through the uniform CD-ROM driver
+    // They _will_ be adopted by all CD-ROM drivers, when all the CD-ROM
+    // drivers are eventually ported to the uniform CD-ROM driver interface.
+
+    /// Pendant of [`Operations::Eject`]
+    CloseTray = 0x19,
+    /// Set behavior options
+    SetOptions = 0x20,
+    /// Clear behavior options
+    ClearOptions = 0x21,
+    /// Set the CD-ROM speed
+    SelectSpeed = 0x22,
+    /// Select disc (for juke-boxes)
+    SelectDisk = 0x23,
+    /// Check is media changed
+    MediaChanged = 0x25,
+    /// Get tray position, etc
+    DriveStatus = 0x26,
+    /// Get disc type, etc
+    DiscStatus = 0x27,
+    /// Get number of slots
+    ChangerNslots = 0x28,
+    /// Lock or unlock door
+    LockDoor = 0x29,
+    /// Turn debug messages on/off
+    Debug = 0x30,
+    /// Get capabilities
+    GetCapability = 0x31,
+
+    // Note that scsi/scsi_ioctl.h also uses 0x5382 - 0x5386.
+    // Future CDROM ioctls should be kept below 0x537F
+
+    /// Set the audio buffer size
+    /// conflict with SCSI_IOCTL_GET_IDLUN
+    AudioBufferSize = 0x82,
+
+    // DVD-ROM Specific ioctls
+
+    /// Read structure
+    DvdReadStructure = 0x90,
+    /// Write structure
+    DvdWriteStructure = 0x91,
+    /// Authentication
+    DvdAuthenticate = 0x92,
+
+    /// Send a packet to the drive
+    SendPacket = 0x93,
+    /// Get next writable block
+    NextWritable = 0x94,
+    /// Get last block written on disc
+    LastWritten = 0x95,
+    /// Get the timestamp of the last media change
+    TimedMediaChange = 0x96,
+}
+
+impl Operations {
+    /// Turns the byte into its full IOCTL representation
+    pub fn to_full(&self) -> u64 {
+        let value = self.to_u8().unwrap();
+
+        ((IOC_BYTE as u64) << 8) + value as u64
+    }
+}
+
+/// Drive status possibilities returned by CDROM_DRIVE_STATUS ioctl
+#[derive(FromPrimitive, ToPrimitive)]
+#[derive(Debug, Clone, Copy)]
+pub enum Status {
+    NoInfo = 0,
+    NoDisc = 1,
+    TrayOpen = 2,
+    DriveNotReady = 3,
+    DiscOK = 4
+}
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..21d2688
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,65 @@
+mod constants;
+mod structures;
+
+use std::os::{fd::IntoRawFd, unix::fs::OpenOptionsExt};
+use std::fs::OpenOptions;
+
+use constants::{Operations, IOC_BYTE};
+use nix::{ioctl_none, ioctl_none_bad, ioctl_readwrite_bad, libc};
+
+use nix::libc::c_int;
+use num_traits::FromPrimitive as _;
+use structures::{Addr, Msf0, ReadAudio};
+
+#[macro_use]
+extern crate num_derive;
+
+ioctl_none_bad!(cdrom_stop, Operations::to_full(&Operations::Stop));
+ioctl_none_bad!(cdrom_start, Operations::to_full(&Operations::Start));
+ioctl_none_bad!(cdrom_eject, Operations::to_full(&Operations::Eject));
+ioctl_none_bad!(cdrom_close_tray, Operations::to_full(&Operations::CloseTray));
+ioctl_none_bad!(cdrom_status, Operations::to_full(&Operations::DriveStatus));
+ioctl_readwrite_bad!(cdrom_read_audio, Operations::to_full(&Operations::ReadAudio), structures::ReadAudio);
+
+fn main() {
+    const FRAMES: i32 = 75;
+    const BYTES_PER_FRAME: i32 = 2352;
+
+    let time = Msf0 {
+        minute: 2,
+        second: 45,
+        frame: 0,
+    };
+
+    let address = Addr {
+        msf: time,
+    };
+
+    let mut buff = [0u8; FRAMES as usize * BYTES_PER_FRAME as usize]; //Frames per second (75) * bytes per frame (2352)
+
+    let mut ra = ReadAudio {
+        addr: address,
+        addr_format: 0x02,
+        nframes: FRAMES,
+        buf: buff.as_mut_ptr()
+    };
+
+    let cdrom = OpenOptions::new()
+        .read(true)
+        .custom_flags(libc::O_NONBLOCK | libc::O_RDONLY)
+        .open("/dev/sr0")
+        .unwrap();
+    let cdrom_fd: c_int = cdrom.into_raw_fd();
+
+    unsafe {
+        let result = constants::Status::from_i32(cdrom_status(cdrom_fd).unwrap()).unwrap();
+        dbg!(result);
+        //dbg!(cdrom_start(cdrom_fd).unwrap_or_default());
+
+        //std::thread::sleep(std::time::Duration::from_secs(1));
+
+        dbg!(cdrom_read_audio(cdrom_fd, std::ptr::addr_of_mut!(ra)).unwrap());
+    }
+
+    println!("{:02X?}", &buff[0..10]);
+}
diff --git a/src/structures.rs b/src/structures.rs
new file mode 100644
index 0000000..9e9e2bf
--- /dev/null
+++ b/src/structures.rs
@@ -0,0 +1,77 @@
+use std::ffi::c_int;
+
+/// Address in MSF format
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct Msf0 {
+    pub minute: u8,
+    pub second: u8,
+    pub frame: u8,
+}
+
+/// Address in either MSF or logical format
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub union Addr {
+    pub lba: c_int,
+    pub msf: Msf0,
+}
+
+/// This struct is used by [`crate::constants::PLAY_MSF`]
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct Msf {
+    /// Start minute
+    pub min0: u8,
+    /// Start second
+    pub sec0: u8,
+    /// Start frame
+    pub frame0: u8,
+    /// End minute
+    pub min1: u8,
+    /// End second
+    pub sec1: u8,
+    /// End frame
+    pub frame1: u8,
+}
+
+#[repr(C)]
+pub union RawResult {
+    pub cdrom_msf: Msf,
+    pub buffer: [u8; 2646],
+}
+
+/// This struct is used by [`crate::constants::PLAY_TRACK_INDEX`]
+struct TrackIndex {
+    /// Start track
+    trk0: u8,
+    /// Start index
+    ind0: u8,
+    /// End track
+    trk1: u8,
+    /// End index
+    ind1: u8,
+}
+
+/// This struct is used by [`crate::constants::READ_TOC_HEADER`]
+struct TocHeader {
+    trk0: u8,
+    trk1: u8,
+}
+
+struct VolCtl {
+
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct ReadAudio {
+    /// Frame address
+    pub addr: Addr,
+    /// CDROM_LBA or CDROM_MSF
+    pub addr_format: u8,
+    /// Number of 2352-byte-frames to read at once
+    pub nframes: i32,
+    /// Pointer to frame buffer (size: nframes*2352 bytes)
+    pub buf: *mut u8,
+}