use byteorder::{LE, ReadBytesExt, WriteBytesExt as _};
use num_enum::{FromPrimitive, IntoPrimitive};
use serialport::{ClearBuffer, SerialPort, SerialPortInfo, SerialPortType, UsbPortInfo};
use std::{
borrow::Cow,
error::Error,
fmt,
io::{self, BufRead, BufReader, Read, Write as _},
time::Duration,
};
pub use {config::*, flux::*};
mod config;
mod flux;
#[derive(Debug)]
pub struct Greaseweazle {
serial: BufReader<Box<dyn SerialPort>>,
write_buf: Vec<u8>,
}
impl Greaseweazle {
pub fn new<'a>(serial_path: impl Into<Cow<'a, str>>) -> Result<Self, io::Error> {
let mut serial = serialport::new(serial_path, BaudRate::ClearComms.into())
.timeout(Duration::from_secs(60))
.dtr_on_open(true)
.open()?;
serial.set_baud_rate(BaudRate::Normal.into())?;
serial.clear(ClearBuffer::All)?;
serial.write_request_to_send(true)?;
Ok(Self {
serial: BufReader::new(serial),
write_buf: Vec::with_capacity(1024),
})
}
#[inline]
pub fn serial(&self) -> &dyn SerialPort {
self.serial.get_ref().as_ref()
}
#[inline]
pub fn serial_mut(&mut self) -> &mut dyn SerialPort {
self.serial.get_mut().as_mut()
}
pub fn reset(&mut self) -> Result<(), CommandError> {
self.send_command(Command::Reset, |_write| Ok(()))
}
pub fn get_firmware_info(&mut self) -> Result<FirmwareInfo, CommandError> {
self.send_command(Command::GetInfo, |write| {
write.write_u8(GetInfo::Firmware.into())?;
Ok(())
})?;
let mut buf = [0; 32];
self.serial.read_exact(&mut buf)?;
FirmwareInfo::read_from(buf.as_slice()).map_err(Into::into)
}
pub fn set_bus_type(&mut self, bus_type: BusType) -> Result<(), CommandError> {
self.send_command(Command::SetBusType, |write| {
write.write_u8(bus_type.into())?;
Ok(())
})
}
pub fn get_drive_info(&mut self, drive_num: Option<u8>) -> Result<DriveInfo, CommandError> {
if let Some(drive_num) = drive_num {
let code = u8::from(GetInfo::Drive)
.checked_add(drive_num)
.ok_or(GreaseweazleError::InvalidDriveNumber)?;
self.send_command(Command::GetInfo, |write| {
write.write_u8(code)?;
Ok(())
})?;
} else {
self.send_command(Command::GetInfo, |write| {
write.write_u8(GetInfo::CurrentDrive.into())?;
Ok(())
})?;
}
let mut buf = [0; 32];
self.serial.read_exact(&mut buf)?;
DriveInfo::read_from(buf.as_slice()).map_err(Into::into)
}
pub fn get_delays(&mut self) -> Result<Delays, CommandError> {
let mut value_count = 8;
while let Err(err) = self.send_command(Command::GetParams, |write| {
write.write_u8(Param::Delays.into())?;
write.write_u8(value_count * 2)?;
Ok(())
}) {
if value_count > 5
&& matches!(
err,
CommandError::GreaseweazleError(GreaseweazleError::BadCommand)
)
{
value_count -= 1;
} else {
return Err(err);
}
}
Ok(Delays {
select_us: self.serial.read_u16::<LE>()?,
step_us: self.serial.read_u16::<LE>()?,
seek_settle_ms: self.serial.read_u16::<LE>()?,
motor_ms: self.serial.read_u16::<LE>()?,
watchdog_ms: self.serial.read_u16::<LE>()?,
pre_write_us: if value_count >= 6 {
Some(self.serial.read_u16::<LE>()?)
} else {
None
},
post_write_us: if value_count >= 7 {
Some(self.serial.read_u16::<LE>()?)
} else {
None
},
index_mask_us: if value_count >= 8 {
Some(self.serial.read_u16::<LE>()?)
} else {
None
},
})
}
pub fn set_delays(&mut self, delays: Delays) -> Result<(), CommandError> {
self.send_command(Command::SetParams, |write| {
write.write_u8(Param::Delays.into())?;
let Delays {
select_us: select,
step_us: step,
seek_settle_ms: seek_settle,
motor_ms: motor,
watchdog_ms: watchdog,
pre_write_us: pre_write,
post_write_us: post_write,
index_mask_us: index_mask,
} = delays;
write.write_u16::<LE>(select)?;
write.write_u16::<LE>(step)?;
write.write_u16::<LE>(seek_settle)?;
write.write_u16::<LE>(motor)?;
write.write_u16::<LE>(watchdog)?;
let Some(pre_write) = pre_write else {
return Ok(());
};
write.write_u16::<LE>(pre_write)?;
let Some(post_write) = post_write else {
return Ok(());
};
write.write_u16::<LE>(post_write)?;
let Some(index_mask) = index_mask else {
return Ok(());
};
write.write_u16::<LE>(index_mask)?;
Ok(())
})
}
pub fn get_pin(&mut self, pin: Pin) -> Result<PinLevel, CommandError> {
self.send_command(Command::GetPin, |write| {
write.write_u8(pin.into())?;
Ok(())
})?;
Ok(self.serial.read_u8()?.into())
}
pub fn set_pin(&mut self, pin: Pin, level: PinLevel) -> Result<(), CommandError> {
self.send_command(Command::SetPin, |write| {
write.write_u8(pin.into())?;
write.write_u8(level.into())?;
Ok(())
})
}
pub fn select_drive(&mut self, drive_num: Option<u8>) -> Result<(), CommandError> {
if let Some(drive_num) = drive_num {
self.send_command(Command::Select, |write| {
write.write_u8(drive_num)?;
Ok(())
})
} else {
self.send_command(Command::Deselect, |_write| Ok(()))
}
}
pub fn select_head(&mut self, head: u8) -> Result<(), CommandError> {
self.send_command(Command::Head, |write| {
write.write_u8(head)?;
Ok(())
})
}
pub fn set_drive_motor(&mut self, drive_num: u8, on: bool) -> Result<(), CommandError> {
self.send_command(Command::Motor, |write| {
write.write_u8(drive_num)?;
write.write_u8(on.into())?;
Ok(())
})
}
pub fn seek(&mut self, cylinder: i16) -> Result<(), CommandError> {
if let Ok(cylinder) = i8::try_from(cylinder) {
self.send_command(Command::Seek, |write| {
write.write_i8(cylinder)?;
Ok(())
})
} else {
self.send_command(Command::Seek, |write| {
write.write_i16::<LE>(cylinder)?;
Ok(())
})
}
}
pub fn no_click_step(&mut self) -> Result<(), CommandError> {
self.send_command(Command::NoClickStep, |_write| Ok(()))
}
pub fn read_flux(
&mut self,
duration: Ticks,
indexes: u16,
) -> Result<EncodedFlux, CommandError> {
self.send_command(Command::ReadFlux, |write| {
write.write_u32::<LE>(duration.into())?;
write.write_u16::<LE>(indexes)?;
Ok(())
})?;
let mut buf = Vec::new();
self.serial.read_until(0, &mut buf)?;
self.send_command(Command::GetFluxStatus, |_write| Ok(()))?;
Ok(EncodedFlux::from_encoded_bytes(buf))
}
pub fn write_flux(
&mut self,
flux: &EncodedFlux,
start_at_index: bool,
stop_at_index: bool,
hard_sector_period: Ticks,
) -> Result<(), CommandError> {
if hard_sector_period.0 == 0 {
self.send_command(Command::WriteFlux, |write| {
write.write_u8(start_at_index.into())?;
write.write_u8(stop_at_index.into())?;
Ok(())
})?;
} else {
self.send_command(Command::WriteFlux, |write| {
write.write_u8(start_at_index.into())?;
write.write_u8(stop_at_index.into())?;
write.write_u32::<LE>(hard_sector_period.into())?;
Ok(())
})?;
}
self.serial.get_mut().write_all(flux.as_bytes())?;
self.serial.read_u8()?; self.send_command(Command::GetFluxStatus, |_write| Ok(()))?;
Ok(())
}
pub fn erase_flux(&mut self, duration: Ticks) -> Result<(), CommandError> {
self.send_command(Command::EraseFlux, |write| {
write.write_u32::<LE>(duration.into())?;
Ok(())
})?;
self.serial.read_u8()?; self.send_command(Command::GetFluxStatus, |_write| Ok(()))?;
Ok(())
}
pub fn source_bytes(&mut self, buf: &mut [u8], seed: u32) -> Result<(), CommandError> {
self.send_command(Command::SourceBytes, |write| {
write.write_u32::<LE>(buf.len().try_into().expect("buffer was too long"))?;
write.write_u32::<LE>(seed)?;
Ok(())
})?;
self.serial.read_exact(buf).map_err(Into::into)
}
pub fn sink_bytes(&mut self, buf: &[u8], seed: u32) -> Result<(), CommandError> {
self.send_command(Command::SinkBytes, |write| {
write.write_u32::<LE>(buf.len().try_into().expect("buffer was too long"))?;
write.write_u32::<LE>(seed)?;
Ok(())
})?;
self.serial.get_mut().write_all(buf)?;
let ack_code = self.serial.read_u8()?;
if ack_code != 0 {
return Err(GreaseweazleError::from(ack_code).into());
}
Ok(())
}
pub fn get_bandwidth_stats(&mut self) -> Result<BandwidthStats, CommandError> {
self.send_command(Command::GetInfo, |write| {
write.write_u8(GetInfo::BandwidthStats.into())?;
Ok(())
})?;
let mut buf = [0; 32];
self.serial.read_exact(&mut buf)?;
BandwidthStats::read_from(buf.as_slice()).map_err(Into::into)
}
fn send_command(
&mut self,
command: Command,
write_data: impl FnOnce(&mut Vec<u8>) -> Result<(), io::Error>,
) -> Result<(), CommandError> {
let sent_command = u8::from(command);
self.write_buf.clear();
self.write_buf.write_u8(sent_command)?;
self.write_buf.write_u8(0)?;
write_data(&mut self.write_buf)?;
self.write_buf[1] = self
.write_buf
.len()
.try_into()
.expect("command was too long");
let serial = self.serial.get_mut();
serial.write_all(&self.write_buf)?;
serial.flush()?;
#[cfg(feature = "log")]
log::trace!("Sent command `{command:?}` with data: {:?}", self.write_buf);
let mut response = [0; 2];
self.serial.read_exact(&mut response)?;
let mut response = response.as_slice();
let received_command = response.read_u8()?;
let result = match response.read_u8()? {
0 => {
#[cfg(feature = "log")]
log::trace!("Received command `{received_command:?}` response `Ok`");
Ok(())
}
code => {
let err = GreaseweazleError::from(code);
#[cfg(feature = "log")]
log::trace!("Received command `{received_command:?}` response `{err:?}`");
Err(err.into())
}
};
if received_command != sent_command {
return Err(CommandError::ResponseCommandMismatch {
sent_command,
received_command,
});
}
result
}
}
#[derive(Debug)]
#[non_exhaustive]
pub enum CommandError {
IoError(io::Error),
GreaseweazleError(GreaseweazleError),
ResponseCommandMismatch {
sent_command: u8,
received_command: u8,
},
}
impl fmt::Display for CommandError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match self {
Self::IoError(_) => {
write!(f, "an I/O error occurred while sending/receiving data")
}
Self::GreaseweazleError(_) => {
write!(f, "the Greaseweazle responded with an error code")
}
Self::ResponseCommandMismatch { .. } => {
write!(
f,
"the command code returned by the Greaseweazle does not match the code of the \
command that was sent."
)
}
}
}
}
impl Error for CommandError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::IoError(error) => Some(error),
Self::GreaseweazleError(error) => Some(error),
Self::ResponseCommandMismatch { .. } => None,
}
}
}
impl From<io::Error> for CommandError {
fn from(value: io::Error) -> Self {
Self::IoError(value)
}
}
impl From<GreaseweazleError> for CommandError {
fn from(value: GreaseweazleError) -> Self {
Self::GreaseweazleError(value)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive, IntoPrimitive)]
#[non_exhaustive]
#[repr(u8)]
pub enum GreaseweazleError {
BadCommand = 1,
NoIndex = 2,
NoTrack0 = 3,
FluxOverflow = 4,
FluxUnderflow = 5,
WriteProtected = 6,
NoDriveSelected = 7,
NoBusTypeSelected = 8,
InvalidDriveNumber = 9,
InvalidPin = 10,
InvalidCylinder = 11,
OutOfSram = 12,
OutOfFlash = 13,
#[num_enum(catch_all)]
Unknown(u8),
}
pub fn enumerate() -> Result<Vec<SerialPortInfo>, io::Error> {
let mut infos = serialport::available_ports()?;
infos.retain(|info| {
let SerialPortType::UsbPort(info) = &info.port_type else {
return false;
};
let &UsbPortInfo {
vid,
pid,
ref serial_number,
ref manufacturer,
ref product,
} = info;
if vid == 0x1209 {
if pid == 0x4d69 {
return true;
}
if pid == 0x0001
&& serial_number
.as_deref()
.is_some_and(|serial_number| serial_number.starts_with("GW"))
{
return true;
}
}
if let Some(product) = product {
if product == "Greaseweazle" && manufacturer.as_deref() == Some("Keir Fraser") {
return true;
}
if product.to_ascii_lowercase().contains("gw-compat") {
return true;
}
}
false
});
Ok(infos)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[repr(transparent)]
pub struct Ticks(pub u32);
impl Ticks {
#[inline]
pub fn from_duration(duration: Duration, sample_freq: u32) -> Self {
Self::try_from_duration(duration, sample_freq).expect("overflow occurred")
}
#[inline]
pub fn try_from_duration(duration: Duration, sample_freq: u32) -> Option<Self> {
Some(Self(
u32::try_from(duration.saturating_mul(sample_freq).as_secs()).ok()?,
))
}
#[inline]
pub fn to_duration(self, sample_freq: u32) -> Duration {
Duration::from_secs(self.0.into()) / sample_freq
}
}
impl From<u32> for Ticks {
#[inline]
fn from(value: u32) -> Self {
Self(value)
}
}
impl From<Ticks> for u32 {
#[inline]
fn from(value: Ticks) -> Self {
value.0
}
}
impl fmt::Display for GreaseweazleError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match self {
Self::BadCommand => write!(f, "bad command"),
Self::NoIndex => write!(f, "no index"),
Self::NoTrack0 => write!(f, "track 0 not found"),
Self::FluxOverflow => write!(f, "flux overflow"),
Self::FluxUnderflow => write!(f, "flux underflow"),
Self::WriteProtected => write!(f, "disk is write protected"),
Self::NoDriveSelected => write!(f, "no drive selected"),
Self::NoBusTypeSelected => write!(f, "no bus type selected"),
Self::InvalidDriveNumber => write!(f, "invalid drive number"),
Self::InvalidPin => write!(f, "invalid pin"),
Self::InvalidCylinder => write!(f, "invalid cylinder"),
Self::OutOfSram => write!(f, "out of SRAM"),
Self::OutOfFlash => write!(f, "out of flash"),
Self::Unknown(code) => write!(f, "unknown error {code}"),
}
}
}
impl Error for GreaseweazleError {}
#[derive(Debug, Clone, Copy, FromPrimitive, IntoPrimitive)]
#[non_exhaustive]
#[repr(u8)]
pub enum Pin {
DensitySelect = 2,
#[allow(non_camel_case_types)]
HeadLoad_InUse = 4,
#[allow(non_camel_case_types)]
DriveSelect3 = 6,
Index = 8,
Track0 = 26,
WriteProtect = 28,
#[allow(non_camel_case_types)]
DiskChange_Ready = 34,
#[num_enum(catch_all)]
Other(u8),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive, IntoPrimitive)]
#[repr(u8)]
pub enum PinLevel {
Low = 0,
#[num_enum(default)]
High = 1,
}
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub struct BandwidthStats {
pub min: Bandwidth,
pub max: Bandwidth,
}
impl BandwidthStats {
pub(crate) fn read_from(mut read: impl Read) -> Result<Self, io::Error> {
Ok(Self {
min: Bandwidth::read_from(&mut read)?,
max: Bandwidth::read_from(&mut read)?,
})
}
}
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub struct Bandwidth {
pub bytes: u32,
pub per_us: u32,
}
impl Bandwidth {
fn read_from(mut read: impl Read) -> Result<Self, io::Error> {
Ok(Self {
bytes: read.read_u32::<LE>()?,
per_us: read.read_u32::<LE>()?,
})
}
#[inline]
pub const fn as_bits_per_sec(&self) -> f64 {
self.bytes as f64 / self.per_us as f64 * 8_000_000.0
}
}
#[derive(IntoPrimitive)]
#[repr(u32)]
pub(crate) enum BaudRate {
Normal = 9600,
ClearComms = 10000,
}
#[derive(Debug, Clone, Copy, IntoPrimitive)]
#[non_exhaustive]
#[repr(u8)]
pub(crate) enum Command {
GetInfo = 0,
#[allow(dead_code)]
Update = 1,
Seek = 2,
Head = 3,
SetParams = 4,
GetParams = 5,
Motor = 6,
ReadFlux = 7,
WriteFlux = 8,
GetFluxStatus = 9,
#[allow(dead_code)]
SwitchFwMode = 11,
Select = 12,
Deselect = 13,
SetBusType = 14,
SetPin = 15,
Reset = 16,
EraseFlux = 17,
SourceBytes = 18,
SinkBytes = 19,
GetPin = 20,
#[allow(dead_code)]
TestMode = 21,
NoClickStep = 22,
}