Initial code commit

This commit is contained in:
G2-Games 2023-09-23 00:58:35 -05:00
parent ad3a4330d7
commit 397a886693
6 changed files with 598 additions and 0 deletions

12
Cargo.toml Normal file
View file

@ -0,0 +1,12 @@
[package]
name = "minidisc-rs"
version = "0.0.1"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
nofmt = "1.0.0"
once_cell = "1.18.0"
rusb = "0.9.3"

49
src/main.rs Normal file
View file

@ -0,0 +1,49 @@
mod netmd;
use crate::netmd::base::NetMD;
use crate::netmd::interface::NetMDInterface;
use std::thread;
use std::time::Duration;
fn main() {
for device in rusb::devices().unwrap().iter() {
let device_desc = device.device_descriptor().unwrap();
let new_device = match device.open() {
Ok(device) => device,
Err(_) => continue,
};
println!("Connected to Bus {:03} Device {:03} VID: {:04x}, PID: {:04x}, {:?}",
device.bus_number(),
device.address(),
device_desc.vendor_id(),
device_desc.product_id(),
new_device.read_product_string_ascii(&device_desc));
let player = NetMD::new(new_device, device_desc).unwrap();
let player_controller = NetMDInterface::new(player);
println!(
"Player Model: {}",
player_controller.net_md_device.device_name().unwrap()
);
player_controller.get_disc_subunit_identifier();
/*
let mut request: [u8; 19] = [0x00, 0x18, 0x06, 0x02, 0x20, 0x18,
0x01, 0x00, 0x00, 0x30, 0x00, 0xa,
0x00, 0xff, 0x00, 0x00, 0x00, 0x00,
0x00];
request[4] = 0x75;
let test_result = player.send_command(&request);
match test_result {
Ok(_) => println!("Successfully sent command! Response: {:?}", test_result),
Err(error) => println!("Command failed! Error: {}", error)
}
*/
}
}

228
src/netmd/base.rs Normal file
View file

@ -0,0 +1,228 @@
use nofmt;
use once_cell::sync::Lazy;
use rusb::{DeviceDescriptor, DeviceHandle, Direction, GlobalContext, Recipient, RequestType};
use std::error::Error;
const DEFAULT_TIMEOUT: std::time::Duration = std::time::Duration::new(1, 0);
const STANDARD_SEND: u8 =
rusb::request_type(Direction::Out, RequestType::Vendor, Recipient::Interface);
const STANDARD_RECV: u8 =
rusb::request_type(Direction::In, RequestType::Vendor, Recipient::Interface);
// TODO: I think this sucks, figure out a better way
pub static DEVICE_IDS: Lazy<Vec<DeviceId>> = Lazy::new(|| nofmt::pls!{
Vec::from([
DeviceId {vendor_id: 0x04dd, product_id: 0x7202, name: Some(String::from("Sharp IM-MT899H"))},
DeviceId {vendor_id: 0x04dd, product_id: 0x9013, name: Some(String::from("Sharp IM-DR400"))},
DeviceId {vendor_id: 0x04dd, product_id: 0x9014, name: Some(String::from("Sharp IM-DR80"))},
DeviceId {vendor_id: 0x054c, product_id: 0x0034, name: Some(String::from("Sony PCLK-XX"))},
DeviceId {vendor_id: 0x054c, product_id: 0x0036, name: Some(String::from("Sony"))},
DeviceId {vendor_id: 0x054c, product_id: 0x0075, name: Some(String::from("Sony MZ-N1"))},
DeviceId {vendor_id: 0x054c, product_id: 0x007c, name: Some(String::from("Sony"))},
DeviceId {vendor_id: 0x054c, product_id: 0x0080, name: Some(String::from("Sony LAM-1"))},
DeviceId {vendor_id: 0x054c, product_id: 0x0081, name: Some(String::from("Sony MDS-JB980/MDS-NT1/MDS-JE780"))},
DeviceId {vendor_id: 0x054c, product_id: 0x0084, name: Some(String::from("Sony MZ-N505"))},
DeviceId {vendor_id: 0x054c, product_id: 0x0085, name: Some(String::from("Sony MZ-S1"))},
DeviceId {vendor_id: 0x054c, product_id: 0x0086, name: Some(String::from("Sony MZ-N707"))},
DeviceId {vendor_id: 0x054c, product_id: 0x008e, name: Some(String::from("Sony CMT-C7NT"))},
DeviceId {vendor_id: 0x054c, product_id: 0x0097, name: Some(String::from("Sony PCGA-MDN1"))},
DeviceId {vendor_id: 0x054c, product_id: 0x00ad, name: Some(String::from("Sony CMT-L7HD"))},
DeviceId {vendor_id: 0x054c, product_id: 0x00c6, name: Some(String::from("Sony MZ-N10"))},
DeviceId {vendor_id: 0x054c, product_id: 0x00c7, name: Some(String::from("Sony MZ-N910"))},
DeviceId {vendor_id: 0x054c, product_id: 0x00c8, name: Some(String::from("Sony MZ-N710/NF810"))},
DeviceId {vendor_id: 0x054c, product_id: 0x00c9, name: Some(String::from("Sony MZ-N510/N610"))},
DeviceId {vendor_id: 0x054c, product_id: 0x00ca, name: Some(String::from("Sony MZ-NE410/NF520D"))},
DeviceId {vendor_id: 0x054c, product_id: 0x00e7, name: Some(String::from("Sony CMT-M333NT/M373NT"))},
DeviceId {vendor_id: 0x054c, product_id: 0x00eb, name: Some(String::from("Sony MZ-NE810/NE910"))},
DeviceId {vendor_id: 0x054c, product_id: 0x0101, name: Some(String::from("Sony LAM"))},
DeviceId {vendor_id: 0x054c, product_id: 0x0113, name: Some(String::from("Aiwa AM-NX1"))},
DeviceId {vendor_id: 0x054c, product_id: 0x013f, name: Some(String::from("Sony MDS-S500"))},
DeviceId {vendor_id: 0x054c, product_id: 0x014c, name: Some(String::from("Aiwa AM-NX9"))},
DeviceId {vendor_id: 0x054c, product_id: 0x017e, name: Some(String::from("Sony MZ-NH1"))},
DeviceId {vendor_id: 0x054c, product_id: 0x0180, name: Some(String::from("Sony MZ-NH3D"))},
DeviceId {vendor_id: 0x054c, product_id: 0x0182, name: Some(String::from("Sony MZ-NH900"))},
DeviceId {vendor_id: 0x054c, product_id: 0x0184, name: Some(String::from("Sony MZ-NH700/NH800"))},
DeviceId {vendor_id: 0x054c, product_id: 0x0186, name: Some(String::from("Sony MZ-NH600"))},
DeviceId {vendor_id: 0x054c, product_id: 0x0187, name: Some(String::from("Sony MZ-NH600D"))},
DeviceId {vendor_id: 0x054c, product_id: 0x0188, name: Some(String::from("Sony MZ-N920"))},
DeviceId {vendor_id: 0x054c, product_id: 0x018a, name: Some(String::from("Sony LAM-3"))},
DeviceId {vendor_id: 0x054c, product_id: 0x01e9, name: Some(String::from("Sony MZ-DH10P"))},
DeviceId {vendor_id: 0x054c, product_id: 0x0219, name: Some(String::from("Sony MZ-RH10"))},
DeviceId {vendor_id: 0x054c, product_id: 0x021b, name: Some(String::from("Sony MZ-RH710/MZ-RH910"))},
DeviceId {vendor_id: 0x054c, product_id: 0x021d, name: Some(String::from("Sony CMT-AH10"))},
DeviceId {vendor_id: 0x054c, product_id: 0x022c, name: Some(String::from("Sony CMT-AH10"))},
DeviceId {vendor_id: 0x054c, product_id: 0x023c, name: Some(String::from("Sony DS-HMD1"))},
DeviceId {vendor_id: 0x054c, product_id: 0x0286, name: Some(String::from("Sony MZ-RH1"))},
DeviceId {vendor_id: 0x054c, product_id: 0x011a, name: Some(String::from("Sony CMT-SE7"))},
DeviceId {vendor_id: 0x054c, product_id: 0x0148, name: Some(String::from("Sony MDS-A1"))},
DeviceId {vendor_id: 0x0b28, product_id: 0x1004, name: Some(String::from("Kenwood MDX-J9"))},
DeviceId {vendor_id: 0x04da, product_id: 0x23b3, name: Some(String::from("Panasonic SJ-MR250"))},
DeviceId {vendor_id: 0x04da, product_id: 0x23b6, name: Some(String::from("Panasonic SJ-MR270"))},
])
});
pub enum Status {
Ready,
Playing,
Paused,
FastForward,
Rewind,
ReadingTOC,
NoDisc,
DiscBlank,
}
pub struct DeviceId {
vendor_id: u16,
product_id: u16,
name: Option<String>,
}
pub struct NetMD {
device_connection: DeviceHandle<GlobalContext>,
model: DeviceId,
status: Option<Status>,
}
impl NetMD {
/// Creates a new `NetMD` struct
pub fn new(
device: DeviceHandle<GlobalContext>,
device_desc: DeviceDescriptor,
) -> Result<Self, Box<dyn Error>> {
let mut model = DeviceId {
vendor_id: device_desc.vendor_id(),
product_id: device_desc.product_id(),
name: None,
};
for device_type in DEVICE_IDS.iter() {
if device_type.vendor_id == model.vendor_id
&& device_type.product_id == model.product_id
{
model.name = device_type.name.clone()
}
}
match model.name {
None => return Err("Could not find device in list".into()),
Some(_) => (),
}
Ok(Self {
device_connection: device,
model,
status: None,
})
}
/// Gets the device name from the struct
pub fn device_name(&self) -> Option<String> {
self.model.name.clone()
}
/// Gets the vendor id from the struct
pub fn vendor_id(&self) -> u16 {
self.model.vendor_id.clone()
}
/// Gets the product id from the struct
pub fn product_id(&self) -> u16 {
self.model.product_id.clone()
}
/// Poll the device to get either the result
/// of the previous command, or the status
fn poll(&self, tries: usize) -> Result<(usize, [u8; 4]), Box<dyn Error>> {
// Create an array to store the result of the poll
let mut poll_result = [0u8; 4];
// Try until failure or `tries` reached
for i in 0..tries {
let _status = match self.device_connection.read_control(
STANDARD_RECV,
0x01,
0,
0,
&mut poll_result,
DEFAULT_TIMEOUT,
) {
Ok(size) => size,
Err(error) => return Err(error.into()),
};
if poll_result[0] != 0 {
return Ok((poll_result[2] as usize, poll_result));
}
if i > 0 {
std::thread::sleep(std::time::Duration::from_secs(1));
}
}
Ok((poll_result[2] as usize, poll_result))
}
/// Send a control message to the device
pub fn send_command(
&self,
command: Vec<u8>,
use_factory_command: bool,
) -> Result<(), Box<dyn Error>> {
//First poll to ensure the device is ready
match self.poll(1) {
Ok(buffer) => match buffer.1[2] {
0 => 0,
_ => return Err("Device not ready!".into()),
},
Err(error) => return Err(error),
};
let _ = match use_factory_command {
false => 0x80,
true => 0xff,
};
match self.device_connection.write_control(
STANDARD_SEND,
0x80,
0,
0,
&command,
DEFAULT_TIMEOUT,
) {
Ok(_) => Ok(()),
Err(error) => Err(error.into()),
}
}
/// Poll to see if a message is ready,
/// and if so, recieve it
pub fn read_reply(&self, use_factory_command: bool) -> Result<Vec<u8>, Box<dyn Error>> {
let poll_result = match self.poll(30) {
Ok(buffer) => buffer,
Err(error) => return Err(error),
};
let request = match use_factory_command {
false => 0x81,
true => 0xff,
};
// Create a buffer to fill with the result
let mut buf: [u8; 255] = [0; 255];
match self.device_connection.read_control(
STANDARD_RECV,
request,
0,
0,
&mut buf,
DEFAULT_TIMEOUT,
) {
Ok(_) => Ok(buf[0..poll_result.0].to_vec()),
Err(error) => return Err(error.into()),
}
}
}

298
src/netmd/interface.rs Normal file
View file

@ -0,0 +1,298 @@
use crate::netmd::utils;
use crate::NetMD;
use std::error::Error;
#[derive(Copy, Clone)]
enum Action {
Play = 0x75,
Pause = 0x7d,
FastForward = 0x39,
Rewind = 0x49,
}
enum Track {
Previous = 0x0002,
Next = 0x8001,
Restart = 0x0001,
}
enum DiscFormat {
LP4 = 0,
LP2 = 2,
SPMono = 4,
SPStereo = 6,
}
enum WireFormat {
PCM = 0x00,
L105kbps = 0x90,
LP2 = 0x94,
LP4 = 0xA8,
}
impl WireFormat {
fn frame_size(&self) -> u16 {
match self {
WireFormat::PCM => 2048,
WireFormat::L105kbps => 192,
WireFormat::LP2 => 152,
WireFormat::LP4 => 96,
}
}
}
enum Encoding {
SP = 0x90,
LP2 = 0x92,
LP4 = 0x93,
}
enum Channels {
Mono = 0x01,
Stereo = 0x00,
}
enum ChannelCount {
Mono = 1,
Stereo = 2,
}
enum TrackFlag {
Protected = 0x03,
Unprotected = 0x00,
}
enum DiscFlag {
Writable = 0x10,
WriteProtected = 0x40,
}
enum NetMDLevel {
Level1 = 0x20, // Network MD
Level2 = 0x50, // Program play MD
Level3 = 0x70, // Editing MD
}
enum Descriptor {
DiscTitleTD,
AudioUTOC1TD,
AudioUTOC4TD,
DSITD,
AudioContentsTD,
RootTD,
DiscSubunitIdentifier,
OperatingStatusBlock,
}
impl Descriptor {
fn get_array(&self) -> Vec<u8> {
match self {
Descriptor::DiscTitleTD => vec![0x10, 0x18, 0x01],
Descriptor::AudioUTOC1TD => vec![0x10, 0x18, 0x02],
Descriptor::AudioUTOC4TD => vec![0x10, 0x18, 0x03],
Descriptor::DSITD => vec![0x10, 0x18, 0x04],
Descriptor::AudioContentsTD => vec![0x10, 0x10, 0x01],
Descriptor::RootTD => vec![0x10, 0x10, 0x00],
Descriptor::DiscSubunitIdentifier => vec![0x00],
Descriptor::OperatingStatusBlock => vec![0x80, 0x00],
}
}
}
enum DescriptorAction {
OpenRead = 1,
OpenWrite = 3,
Close = 0,
}
#[repr(u8)]
enum Status {
// NetMD Protocol return status (first byte of request)
Control = 0x00,
Status = 0x01,
SpecificInquiry = 0x02,
Notify = 0x03,
GeneralInquiry = 0x04,
// ... (first byte of response)
NotImplemented = 0x08,
Accepted = 0x09,
Rejected = 0x0a,
InTransition = 0x0b,
Implemented = 0x0c,
Changed = 0x0d,
Interim = 0x0f,
}
impl std::convert::TryFrom<u8> for Status {
type Error = Box<dyn Error>;
fn try_from(item: u8) -> Result<Self, Box<dyn Error>> {
match item {
0x00 => Ok(Status::Control),
0x01 => Ok(Status::Status),
0x02 => Ok(Status::SpecificInquiry),
0x03 => Ok(Status::Notify),
0x04 => Ok(Status::GeneralInquiry),
0x08 => Ok(Status::NotImplemented),
0x09 => Ok(Status::Accepted),
0x0a => Ok(Status::Rejected),
0x0b => Ok(Status::InTransition),
0x0c => Ok(Status::Implemented),
0x0d => Ok(Status::Changed),
0x0f => Ok(Status::Interim),
_ => Err("Not a valid value".into()),
}
}
}
pub struct NetMDInterface {
pub net_md_device: NetMD,
}
impl NetMDInterface {
const MAX_INTERIM_READ_ATTEMPTS: u8 = 4;
const INTERIM_RESPONSE_RETRY_INTERVAL: u32 = 100;
pub fn new(net_md_device: NetMD) -> Self {
NetMDInterface { net_md_device }
}
pub fn get_disc_subunit_identifier(&self) -> Result<(), Box<dyn Error>> {
self.change_descriptor_state(
Descriptor::DiscSubunitIdentifier,
DescriptorAction::OpenRead,
);
let mut query = vec![0x18, 0x09, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00];
let reply = self.send_query(&mut query, false, false);
println!("{:X?}", reply);
/*
let descriptor_length = i16::from_le_bytes(reply[0..1].try_into()?);
let generation_id = i16::from_le_bytes(reply[0..1].try_into()?);
*/
Ok(())
}
fn playback_control(&self, action: Action) -> Result<(), Box<dyn Error>> {
let mut query = vec![0x18, 0xc3, 0xff, 0x00, 0x00, 0x00, 0x00];
query[3] = action as u8;
let result = self.send_query(&mut query, false, false)?;
utils::check_result(result, &[0x18, 0xc5, 0x00, action as u8, 0x00, 0x00, 0x00])?;
Ok(())
}
pub fn play(&self) {
let _ = self.playback_control(Action::Play);
}
pub fn fast_forward(&self) {
let _ = self.playback_control(Action::FastForward);
}
pub fn rewind(&self) {
let _ = self.playback_control(Action::Rewind);
}
pub fn pause(&self) {
let _ = self.playback_control(Action::Pause);
}
pub fn stop(&self) -> Result<(), Box<dyn Error>> {
let mut query = vec![0x18, 0xc5, 0xff, 0x00, 0x00, 0x00, 0x00];
let result = self.send_query(&mut query, false, false)?;
utils::check_result(result, &[0x18, 0xc5, 0x00, 0x00, 0x00, 0x00, 0x00])?;
Ok(())
}
fn change_descriptor_state(&self, descriptor: Descriptor, action: DescriptorAction) {
let mut query = vec![0x18, 0x08];
query.append(&mut descriptor.get_array());
query.push(action as u8);
query.push(0x00);
let _ = self.send_query(&mut query, false, false);
}
fn send_query(
&self,
query: &mut Vec<u8>,
test: bool,
accept_interim: bool,
) -> Result<Vec<u8>, Box<dyn Error>> {
self.send_command(query, test)?;
let result = self.read_reply(accept_interim)?;
Ok(result)
}
fn send_command(&self, query: &mut Vec<u8>, test: bool) -> Result<(), Box<dyn Error>> {
let status_byte = match test {
true => Status::GeneralInquiry,
false => Status::Control,
};
let mut new_query = Vec::new();
new_query.push(status_byte as u8);
new_query.append(query);
self.net_md_device.send_command(new_query, false)?;
Ok(())
}
fn read_reply(&self, accept_interim: bool) -> Result<Vec<u8>, Box<dyn Error>> {
let mut current_attempt = 0;
let mut data;
while current_attempt < Self::MAX_INTERIM_READ_ATTEMPTS {
data = match self.net_md_device.read_reply(false) {
Ok(reply) => reply,
Err(error) => return Err(error.into()),
};
let status = match Status::try_from(data[0]) {
Ok(status) => status,
Err(error) => return Err(error),
};
match status {
Status::NotImplemented => return Err("Not implemented".into()),
Status::Rejected => return Err("Rejected".into()),
Status::Interim if !accept_interim => {
let sleep_time = Self::INTERIM_RESPONSE_RETRY_INTERVAL as u64 * (u64::pow(2, current_attempt as u32) - 1);
let sleep_dur = std::time::Duration::from_millis(sleep_time);
std::thread::sleep(sleep_dur); // Sleep to wait before retrying
current_attempt += 1;
continue; // Retry!
}
Status::Accepted | Status::Implemented | Status::Interim => {
if current_attempt >= Self::MAX_INTERIM_READ_ATTEMPTS {
return Err("Max interim retry attempts reached".into());
}
return Ok(data);
}
_ => return Err("Unknown return status".into()),
}
}
// This should NEVER happen unless the code is changed wrongly
Err("The max retries is set to 0".into())
}
}

3
src/netmd/mod.rs Normal file
View file

@ -0,0 +1,3 @@
pub mod base;
pub mod interface;
pub mod utils;

8
src/netmd/utils.rs Normal file
View file

@ -0,0 +1,8 @@
use std::error::Error;
pub fn check_result(result: Vec<u8>, expected: &[u8]) -> Result<(), Box<dyn Error>> {
match result.as_slice().eq(expected) {
true => Ok(()),
false => Err("Response was not expected!".into()),
}
}