mirror of
https://github.com/G2-Games/minidisc-cli.git
synced 2025-04-19 11:42:53 -05:00
Added more functions to interface, cleaned up base.rs
This commit is contained in:
parent
3980cfa189
commit
4a76d96341
5 changed files with 206 additions and 45 deletions
|
@ -6,6 +6,7 @@ edition = "2021"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
bcd-numbers = "1.0.11"
|
||||||
nofmt = "1.0.0"
|
nofmt = "1.0.0"
|
||||||
once_cell = "1.18.0"
|
once_cell = "1.18.0"
|
||||||
rusb = "0.9.3"
|
rusb = "0.9.3"
|
||||||
|
|
17
src/main.rs
17
src/main.rs
|
@ -32,15 +32,20 @@ fn main() {
|
||||||
player_controller.net_md_device.device_name().unwrap()
|
player_controller.net_md_device.device_name().unwrap()
|
||||||
);
|
);
|
||||||
|
|
||||||
player_controller.play();
|
println!("{:?}", player_controller.get_position());
|
||||||
|
println!("{:?}", player_controller.go_to_time(0, 0, 2, 0, 0));
|
||||||
|
let _ = player_controller.play();
|
||||||
|
thread::sleep(Duration::from_secs(5));
|
||||||
|
println!("{:?}", player_controller.get_position());
|
||||||
|
println!("{:?}", player_controller.go_to_time(10, 0, 1, 15, 0));
|
||||||
|
println!("{:?}", player_controller.get_position());
|
||||||
|
|
||||||
println!("{:?}", player_controller.playback_status2());
|
thread::sleep(Duration::from_secs(10));
|
||||||
thread::sleep(Duration::from_secs(1));
|
println!("{:?}", player_controller.get_position());
|
||||||
println!("{:?}", player_controller.playback_status2());
|
|
||||||
|
|
||||||
thread::sleep(Duration::from_secs(1));
|
thread::sleep(Duration::from_secs(5));
|
||||||
|
|
||||||
player_controller.stop();
|
let _ = player_controller.stop();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
let mut request: [u8; 19] = [0x00, 0x18, 0x06, 0x02, 0x20, 0x18,
|
let mut request: [u8; 19] = [0x00, 0x18, 0x06, 0x02, 0x20, 0x18,
|
||||||
|
|
|
@ -93,6 +93,8 @@ pub struct NetMD {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NetMD {
|
impl NetMD {
|
||||||
|
const READ_REPLY_RETRY_INTERVAL: u32 = 10;
|
||||||
|
|
||||||
/// Creates a new `NetMD` struct
|
/// Creates a new `NetMD` struct
|
||||||
pub fn new(
|
pub fn new(
|
||||||
device: DeviceHandle<GlobalContext>,
|
device: DeviceHandle<GlobalContext>,
|
||||||
|
@ -141,37 +143,24 @@ impl NetMD {
|
||||||
|
|
||||||
/// Poll the device to get either the result
|
/// Poll the device to get either the result
|
||||||
/// of the previous command, or the status
|
/// of the previous command, or the status
|
||||||
fn poll(&self, tries: usize) -> Result<(u16, [u8; 4]), Box<dyn Error>> {
|
fn poll(&self) -> Result<(u16, [u8; 4]), Box<dyn Error>> {
|
||||||
// Create an array to store the result of the poll
|
// Create an array to store the result of the poll
|
||||||
let mut poll_result = [0u8; 4];
|
let mut poll_result = [0u8; 4];
|
||||||
|
|
||||||
// Try until failure or `tries` reached
|
let _status = match self.device_connection.read_control(
|
||||||
for i in 0..tries {
|
STANDARD_RECV,
|
||||||
let _status = match self.device_connection.read_control(
|
0x01,
|
||||||
STANDARD_RECV,
|
0,
|
||||||
0x01,
|
0,
|
||||||
0,
|
&mut poll_result,
|
||||||
0,
|
DEFAULT_TIMEOUT,
|
||||||
&mut poll_result,
|
) {
|
||||||
DEFAULT_TIMEOUT,
|
Ok(size) => size,
|
||||||
) {
|
Err(error) => return Err(error.into()),
|
||||||
Ok(size) => size,
|
};
|
||||||
Err(error) => return Err(error.into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let length_bytes = [poll_result[2], poll_result[3]];
|
|
||||||
|
|
||||||
if poll_result[0] != 0 {
|
|
||||||
return Ok((u16::from_le_bytes(length_bytes), poll_result));
|
|
||||||
}
|
|
||||||
|
|
||||||
if i > 0 {
|
|
||||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This should not contain anything
|
|
||||||
let length_bytes = [poll_result[2], poll_result[3]];
|
let length_bytes = [poll_result[2], poll_result[3]];
|
||||||
|
println!("Poll result: {}", u16::from_le_bytes(length_bytes));
|
||||||
Ok((u16::from_le_bytes(length_bytes), poll_result))
|
Ok((u16::from_le_bytes(length_bytes), poll_result))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,7 +179,7 @@ impl NetMD {
|
||||||
use_factory_command: bool,
|
use_factory_command: bool,
|
||||||
) -> Result<(), Box<dyn Error>> {
|
) -> Result<(), Box<dyn Error>> {
|
||||||
//First poll to ensure the device is ready
|
//First poll to ensure the device is ready
|
||||||
match self.poll(1) {
|
match self.poll() {
|
||||||
Ok(buffer) => match buffer.1[2] {
|
Ok(buffer) => match buffer.1[2] {
|
||||||
0 => 0,
|
0 => 0,
|
||||||
_ => return Err("Device not ready!".into()),
|
_ => return Err("Device not ready!".into()),
|
||||||
|
@ -216,21 +205,33 @@ impl NetMD {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_reply(&self) -> Result<Vec<u8>, Box<dyn Error>> {
|
pub fn read_reply(&self, override_length: Option<i32>) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||||
self._read_reply(false)
|
self._read_reply(false, override_length)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_factory_reply(&self) -> Result<Vec<u8>, Box<dyn Error>> {
|
pub fn read_factory_reply(&self, override_length: Option<i32>) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||||
self._read_reply(true)
|
self._read_reply(true, override_length)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Poll to see if a message is ready,
|
/// Poll to see if a message is ready,
|
||||||
/// and if so, recieve it
|
/// and if so, recieve it
|
||||||
fn _read_reply(&self, use_factory_command: bool) -> Result<Vec<u8>, Box<dyn Error>> {
|
fn _read_reply(&self, use_factory_command: bool, override_length: Option<i32>) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||||
let poll_result = match self.poll(30) {
|
let mut length = self.poll()?.0;
|
||||||
Ok(buffer) => buffer,
|
|
||||||
Err(error) => return Err(error),
|
let mut current_attempt = 0;
|
||||||
};
|
while length == 0 {
|
||||||
|
let sleep_time = Self::READ_REPLY_RETRY_INTERVAL as u64
|
||||||
|
* (u64::pow(2, current_attempt as u32 / 10) - 1);
|
||||||
|
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(sleep_time));
|
||||||
|
length = self.poll()?.0;
|
||||||
|
current_attempt += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
match override_length {
|
||||||
|
Some(value) => length = value as u16,
|
||||||
|
None => ()
|
||||||
|
}
|
||||||
|
|
||||||
let request = match use_factory_command {
|
let request = match use_factory_command {
|
||||||
false => 0x81,
|
false => 0x81,
|
||||||
|
@ -238,7 +239,7 @@ impl NetMD {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create a buffer to fill with the result
|
// Create a buffer to fill with the result
|
||||||
let mut buf: Vec<u8> = vec![0; poll_result.0 as usize];
|
let mut buf: Vec<u8> = vec![0; length as usize];
|
||||||
|
|
||||||
match self.device_connection.read_control(
|
match self.device_connection.read_control(
|
||||||
STANDARD_RECV,
|
STANDARD_RECV,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::netmd::utils;
|
use crate::netmd::utils;
|
||||||
use crate::NetMD;
|
use crate::NetMD;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
enum Action {
|
enum Action {
|
||||||
|
@ -188,6 +189,7 @@ impl NetMDInterface {
|
||||||
u32::from_le_bytes(bytes)
|
u32::from_le_bytes(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Finish proper implementation
|
||||||
fn disc_subunit_identifier(&self) -> Result<NetMDLevel, Box<dyn Error>> {
|
fn disc_subunit_identifier(&self) -> Result<NetMDLevel, Box<dyn Error>> {
|
||||||
self.change_descriptor_state(
|
self.change_descriptor_state(
|
||||||
Descriptor::DiscSubunitIdentifier,
|
Descriptor::DiscSubunitIdentifier,
|
||||||
|
@ -271,6 +273,18 @@ impl NetMDInterface {
|
||||||
Err("No supported media types found".into())
|
Err("No supported media types found".into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* TODO: Finish implementation
|
||||||
|
fn factory(&self) -> Result<NetMDLevel, Box<dyn Error>> {
|
||||||
|
let device_name = self.net_md_device.device_name().expect("The device has no name");
|
||||||
|
|
||||||
|
let himd = device_name.contains("MZ-RH") || device_name.contains("MZ-NH");
|
||||||
|
|
||||||
|
self.disc_subunit_identifier()?;
|
||||||
|
|
||||||
|
let constructor =
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
fn net_md_level(&self) -> Result<NetMDLevel, Box<dyn Error>> {
|
fn net_md_level(&self) -> Result<NetMDLevel, Box<dyn Error>> {
|
||||||
let result = self.disc_subunit_identifier()?;
|
let result = self.disc_subunit_identifier()?;
|
||||||
|
|
||||||
|
@ -323,7 +337,7 @@ impl NetMDInterface {
|
||||||
let mut data;
|
let mut data;
|
||||||
|
|
||||||
while current_attempt < Self::MAX_INTERIM_READ_ATTEMPTS {
|
while current_attempt < Self::MAX_INTERIM_READ_ATTEMPTS {
|
||||||
data = match self.net_md_device.read_reply() {
|
data = match self.net_md_device.read_reply(None) {
|
||||||
Ok(reply) => reply,
|
Ok(reply) => reply,
|
||||||
Err(error) => return Err(error.into()),
|
Err(error) => return Err(error.into()),
|
||||||
};
|
};
|
||||||
|
@ -396,6 +410,18 @@ impl NetMDInterface {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn acquire(&self) -> Result<(), Box<dyn Error>> {
|
||||||
|
let mut query = vec![0xff, 0x01, 0x0c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff];
|
||||||
|
let reply = self.send_query(&mut query, false, false)?;
|
||||||
|
utils::check_result(reply, &[0xff, 0x01, 0x0c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn release(&self) -> Result<(), Box<dyn Error>> {
|
||||||
|
let mut query = vec![0xff, 0x01, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff];
|
||||||
|
let reply = self.send_query(&mut query, false, false)?;
|
||||||
|
utils::check_result(reply, &[0xff, 0x01, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff])
|
||||||
|
}
|
||||||
|
|
||||||
fn status(&self) -> Result<Vec<u8>, Box<dyn Error>> {
|
fn status(&self) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||||
self.change_descriptor_state(Descriptor::OperatingStatusBlock, DescriptorAction::OpenRead);
|
self.change_descriptor_state(Descriptor::OperatingStatusBlock, DescriptorAction::OpenRead);
|
||||||
let mut query = vec![
|
let mut query = vec![
|
||||||
|
@ -411,7 +437,7 @@ impl NetMDInterface {
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_disc_present(&self) -> Result<bool, Box<dyn Error>> {
|
pub fn disc_present(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
let status = self.status()?;
|
let status = self.status()?;
|
||||||
|
|
||||||
println!("{:X?}", status);
|
println!("{:X?}", status);
|
||||||
|
@ -477,4 +503,105 @@ impl NetMDInterface {
|
||||||
pub fn playback_status2(&self) -> Result<Vec<u8>, Box<dyn Error>> {
|
pub fn playback_status2(&self) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||||
self.playback_status_query([0x88, 0x02], [0x88, 0x06])
|
self.playback_status_query([0x88, 0x02], [0x88, 0x06])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_position(&self) -> Result<[u16; 5], Box<dyn Error>> {
|
||||||
|
self.change_descriptor_state(Descriptor::OperatingStatusBlock, DescriptorAction::OpenRead);
|
||||||
|
|
||||||
|
let mut query = vec![
|
||||||
|
0x18, 0x09, 0x80, 0x01, 0x04, 0x30, 0x88, 0x02, 0x00, 0x30, 0x88, 0x05, 0x00, 0x30,
|
||||||
|
0x00, 0x03, 0x00, 0x30, 0x00, 0x02, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||||
|
];
|
||||||
|
|
||||||
|
let reply = match self.send_query(&mut query, false, false) {
|
||||||
|
Ok(result) => result,
|
||||||
|
Err(e) if e.to_string() == "Rejected" => Vec::new(),
|
||||||
|
Err(e) => return Err(e)
|
||||||
|
};
|
||||||
|
|
||||||
|
let track_number = u16::from_be_bytes([reply[35], reply[36]]);
|
||||||
|
|
||||||
|
let hour = utils::byte_from_bcd(reply[37])?;
|
||||||
|
let minute = utils::byte_from_bcd(reply[38])?;
|
||||||
|
let second = utils::byte_from_bcd(reply[39])?;
|
||||||
|
let frame = utils::byte_from_bcd(reply[40])?;
|
||||||
|
|
||||||
|
let final_result = [track_number, hour as u16, minute as u16, second as u16, frame as u16];
|
||||||
|
|
||||||
|
self.change_descriptor_state(Descriptor::OperatingStatusBlock, DescriptorAction::Close);
|
||||||
|
|
||||||
|
Ok(final_result)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn eject_disc(&self) -> Result<(), Box<dyn Error>> {
|
||||||
|
let mut query = vec![0x18, 0xc1, 0xff, 0x60, 0x00];
|
||||||
|
let _reply = self.send_query(&mut query, false, false)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn can_eject_disc(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
|
let mut query = vec![0x18, 0xc1, 0xff, 0x60, 0x00];
|
||||||
|
match self.send_query(&mut query, true, false) {
|
||||||
|
Ok(_) => Ok(true),
|
||||||
|
Err(error) => Err(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn go_to_track(&self, track_number: u16) -> Result<u16, Box<dyn Error>> {
|
||||||
|
let mut query = vec![0x18, 0x50, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0b00, 0b00];
|
||||||
|
|
||||||
|
let bytes = track_number.to_le_bytes();
|
||||||
|
|
||||||
|
query[8] = bytes[1];
|
||||||
|
query[9] = bytes[0];
|
||||||
|
|
||||||
|
let reply = self.send_query(&mut query, false, false)?;
|
||||||
|
|
||||||
|
Ok(u16::from_be_bytes([reply[8], reply[9]]))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn go_to_time(&self, track_number: u16, hour: u8, minute: u8, second: u8, frame: u8) -> Result<u16, Box<dyn Error>> {
|
||||||
|
let mut query = vec![0x18, 0x50, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0b00, 0b00, 0b00, 0b00, 0b00, 0b00];
|
||||||
|
|
||||||
|
let bytes = track_number.to_le_bytes();
|
||||||
|
query[8] = bytes[1];
|
||||||
|
query[9] = bytes[0];
|
||||||
|
|
||||||
|
query[10] = utils::bcd_from_byte(hour)?;
|
||||||
|
query[11] = utils::bcd_from_byte(minute)?;
|
||||||
|
query[12] = utils::bcd_from_byte(second)?;
|
||||||
|
query[13] = utils::bcd_from_byte(frame)?;
|
||||||
|
|
||||||
|
let reply = self.send_query(&mut query, false, false)?;
|
||||||
|
|
||||||
|
Ok(u16::from_be_bytes([reply[8], reply[9]]))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _track_change(&self, direction: Track) -> Result<(), Box<dyn Error>> {
|
||||||
|
let mut query = vec![0x18, 0x50, 0xff, 0x10, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0b00, 0b00];
|
||||||
|
|
||||||
|
let direction_number = direction as u16;
|
||||||
|
let direction_bytes = direction_number.to_le_bytes();
|
||||||
|
|
||||||
|
query[8] = direction_bytes[1];
|
||||||
|
query[9] = direction_bytes[0];
|
||||||
|
|
||||||
|
let _ = self.send_query(&mut query, false, false);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next_track(&self) -> Result<(), Box<dyn Error>> {
|
||||||
|
self._track_change(Track::Next)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn previous_track(&self) -> Result<(), Box<dyn Error>> {
|
||||||
|
self._track_change(Track::Next)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn restart_track(&self) -> Result<(), Box<dyn Error>> {
|
||||||
|
self._track_change(Track::Next)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,3 +6,30 @@ pub fn check_result(result: Vec<u8>, expected: &[u8]) -> Result<(), Box<dyn Erro
|
||||||
false => Err("Response was not as expected!".into()),
|
false => Err("Response was not as expected!".into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn byte_from_bcd(byte: u8) -> Result<u8, Box<dyn Error>> {
|
||||||
|
let upper = (byte & 0xF0) >> 4;
|
||||||
|
let lower = byte & 0x0F;
|
||||||
|
|
||||||
|
if upper >= 10 {
|
||||||
|
return Err("Upper nybble out of range [0..9]".into())
|
||||||
|
}
|
||||||
|
|
||||||
|
if lower >= 10 {
|
||||||
|
return Err("Lower nybble out of range [0..9]".into())
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(upper * 10 + lower)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bcd_from_byte(byte: u8) -> Result<u8, Box<dyn Error>> {
|
||||||
|
let mut new_byte: u8 = 0;
|
||||||
|
|
||||||
|
let upper = (byte / 10) << 4;
|
||||||
|
let lower = byte % 10;
|
||||||
|
|
||||||
|
new_byte |= upper;
|
||||||
|
new_byte |= lower;
|
||||||
|
|
||||||
|
Ok(new_byte)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue