#![no_std]
#![feature(abi_x86_interrupt)]
extern crate alloc;
#[macro_use] extern crate log;
use core::fmt;
use bitflags::bitflags;
use spin::Mutex;
use alloc::{
boxed::Box,
format,
string::{String, ToString},
sync::Arc
};
use port_io::{Port, PortReadOnly, PortWriteOnly};
use pci::PciDevice;
use storage_device::{StorageDevice, StorageDeviceRef, StorageController};
use io::{BlockIo, BlockReader, BlockWriter, IoError, KnownLength};
use x86_64::structures::idt::InterruptStackFrame;
const SECTOR_SIZE_IN_BYTES: usize = 512;
const DEFAULT_PRIMARY_CHANNEL_DATA_PORT: u16 = 0x1F0;
const DEFAULT_PRIMARY_CHANNEL_CONTROL_PORT: u16 = 0x3F6;
const DEFAULT_SECONDARY_CHANNEL_DATA_PORT: u16 = 0x170;
const DEFAULT_SECONDARY_CHANNEL_CONTROL_PORT: u16 = 0x376;
const MAX_LBA_28_VALUE: usize = (1 << 28) - 1;
const PCI_BAR_PORT_MASK: u16 = 0xFFFC;
bitflags! {
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct AtaError: u8 {
const BAD_BLOCK = 0x80;
const UNCORRECTABLE_DATA = 0x40;
const MEDIA_CHANGED = 0x20;
const ID_MARK_NOT_FOUND = 0x10;
const MEDIA_CHANGE_REQUEST = 0x08;
const COMMAND_ABORTED = 0x04;
const TRACK_0_NOT_FOUND = 0x02;
const ADDRESS_MARK_NOT_FOUND = 0x01;
}
}
bitflags! {
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct AtaStatus: u8 {
const BUSY = 0x80;
const DRIVE_READY = 0x40;
const DRIVE_WRITE_FAULT = 0x20;
const DRIVE_SEEK_COMPLETE = 0x10;
const DATA_REQUEST_READY = 0x08;
const CORRECTED_DATA = 0x04;
const INDEX = 0x02;
const ERROR = 0x01;
}
}
bitflags! {
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
struct AtaControl: u8 {
const HOB = 0x80;
const SRST = 0x04;
const NIEN = 0x02;
}
}
#[allow(dead_code)]
#[repr(u8)]
enum AtaCommand {
ReadPio = 0x20,
ReadPioExt = 0x24,
ReadDma = 0xC8,
ReadDmaExt = 0x25,
WritePio = 0x30,
WritePioExt = 0x34,
WriteDma = 0xCA,
WriteDmaExt = 0x35,
CacheFlush = 0xE7,
CacheFlushExt = 0xEA,
Packet = 0xA0,
IdentifyPacket = 0xA1,
IdentifyDevice = 0xEC,
}
pub enum AtaDeviceType {
Pata,
PataPi,
Sata,
SataPi,
}
impl AtaDeviceType {
fn from_lba(lba_mid: u8, lba_high: u8) -> Option<AtaDeviceType> {
match (lba_mid, lba_high) {
(0x00, 0x00) => Some(AtaDeviceType::Pata),
(0x14, 0xEB) => Some(AtaDeviceType::PataPi),
(0x3C, 0xC3) => Some(AtaDeviceType::Sata),
(0x69, 0x96) => Some(AtaDeviceType::SataPi),
_ => None,
}
}
}
#[derive(Copy, Clone, Debug)]
#[repr(u8)]
enum BusDriveSelect {
Master = 0 << 4,
Slave = 1 << 4,
}
#[allow(unused)]
struct AtaBusMaster {
command: Port<u8>,
status: Port<u8>,
prdt_address: Port<u32>,
}
#[derive(Debug)]
struct AtaBus {
data: Port<u16>,
error: PortReadOnly<u8>,
_features: PortWriteOnly<u8>,
sector_count: Port<u8>,
lba_low: Port<u8>,
lba_mid: Port<u8>,
lba_high: Port<u8>,
drive_select: Port<u8>,
command: PortWriteOnly<u8>,
status: PortReadOnly<u8>, alternate_status: PortReadOnly<u8>,
control: PortWriteOnly<u8>,
_drive_address: Port<u8>,
}
impl AtaBus {
fn new(data_bar: u16, control_bar: u16) -> AtaBus {
let data_bar = data_bar & PCI_BAR_PORT_MASK;
let control_bar = control_bar & PCI_BAR_PORT_MASK;
AtaBus {
data: Port::new(data_bar),
error: PortReadOnly::new(data_bar + 1),
_features: PortWriteOnly::new(data_bar + 1),
sector_count: Port::new(data_bar + 2),
lba_low: Port::new(data_bar + 3),
lba_mid: Port::new(data_bar + 4),
lba_high: Port::new(data_bar + 5),
drive_select: Port::new(data_bar + 6),
command: PortWriteOnly::new(data_bar + 7),
status: PortReadOnly::new(data_bar + 7),
alternate_status: PortReadOnly::new(control_bar + 2),
control: PortWriteOnly::new(control_bar + 2),
_drive_address: Port::new(control_bar + 3),
}
}
fn read_pio(&mut self,
buffer: &mut [u8],
which: BusDriveSelect,
lba_start: usize,
sector_count: usize
) -> Result<usize, &'static str> {
if sector_count == 0 {
return Ok(0);
}
let using_lba_28 = lba_start <= MAX_LBA_28_VALUE;
self.wait_for_data_done().map_err(|_| "error before issuing read pio command")?;
if using_lba_28 {
unsafe {
self.drive_select.write(0xE0 | (which as u8) | ((lba_start >> 24) as u8 & 0x0F));
self.sector_count.write(sector_count as u8);
self.lba_high.write((lba_start >> 16) as u8);
self.lba_mid.write( (lba_start >> 8) as u8);
self.lba_low.write( lba_start as u8);
self.command.write(AtaCommand::ReadPio as u8);
}
} else {
unsafe {
self.drive_select.write(0x40 | (which as u8));
self.sector_count.write((sector_count >> 8) as u8);
self.lba_high.write((lba_start >> 40) as u8);
self.lba_mid.write( (lba_start >> 32) as u8);
self.lba_low.write( (lba_start >> 24) as u8);
self.sector_count.write(sector_count as u8);
self.lba_high.write((lba_start >> 16) as u8);
self.lba_mid.write( (lba_start >> 8) as u8);
self.lba_low.write( lba_start as u8);
self.command.write(AtaCommand::ReadPioExt as u8);
}
}
let mut buffer_offset = 0;
for _lba in lba_start .. (lba_start + sector_count) {
self.wait_for_data_ready().map_err(|_| "error during data read")?;
for chunk in buffer[buffer_offset .. (buffer_offset + SECTOR_SIZE_IN_BYTES)].chunks_exact_mut(2) {
let word: u16 = self.data.read();
chunk[0] = word as u8;
chunk[1] = (word >> 8) as u8;
}
buffer_offset += SECTOR_SIZE_IN_BYTES;
}
self.wait_for_data_done().map_err(|_| "error after data read")?;
Ok(sector_count)
}
fn write_pio(&mut self,
buffer: &[u8],
which: BusDriveSelect,
lba_start: usize,
sector_count: usize
) -> Result<usize, &'static str> {
if sector_count == 0 {
return Ok(0);
}
let using_lba_28 = lba_start <= MAX_LBA_28_VALUE;
self.wait_for_data_done().map_err(|_| "error before issuing write command")?;
if using_lba_28 {
unsafe {
self.drive_select.write(0xE0 | (which as u8) | ((lba_start >> 24) as u8 & 0x0F));
self.sector_count.write(sector_count as u8);
self.lba_high.write((lba_start >> 16) as u8);
self.lba_mid.write( (lba_start >> 8) as u8);
self.lba_low.write( lba_start as u8);
self.command.write(AtaCommand::WritePio as u8);
}
} else {
unsafe {
self.drive_select.write(0x40 | (which as u8));
self.sector_count.write((sector_count >> 8) as u8);
self.lba_high.write((lba_start >> 40) as u8);
self.lba_mid.write( (lba_start >> 32) as u8);
self.lba_low.write( (lba_start >> 24) as u8);
self.sector_count.write(sector_count as u8);
self.lba_high.write((lba_start >> 16) as u8);
self.lba_mid.write( (lba_start >> 8) as u8);
self.lba_low.write( lba_start as u8);
self.command.write(AtaCommand::WritePioExt as u8);
}
}
let mut buffer_offset = 0;
for _lba in lba_start .. (lba_start + sector_count) {
self.wait_for_data_ready().map_err(|_| "error during data write")?;
for chunk in buffer[buffer_offset .. (buffer_offset + SECTOR_SIZE_IN_BYTES)].chunks_exact(2) {
let word = (chunk[1] as u16) << 8 | (chunk[0] as u16);
unsafe { self.data.write(word); }
}
buffer_offset += SECTOR_SIZE_IN_BYTES;
}
self.wait_for_data_done().map_err(|_| "error after data write")?;
let cache_flush_cmd = if using_lba_28 { AtaCommand::CacheFlush } else { AtaCommand::CacheFlushExt };
unsafe { self.command.write(cache_flush_cmd as u8) };
self.wait_for_data_done().map_err(|_| "error after cache flush after data write")?;
Ok(sector_count)
}
fn identify_drive(&mut self, which: BusDriveSelect) -> Result<AtaIdentifyData, &'static str> {
self.wait_for_data_done().map_err(|_| "error before issuing identify command")?;
unsafe {
self.drive_select.write(0xA0 | which as u8);
self.sector_count.write(0);
self.lba_high.write(0);
self.lba_mid.write(0);
self.lba_low.write(0);
self.command.write(AtaCommand::IdentifyDevice as u8);
}
if self.status().is_empty() {
return Err("drive did not exist");
}
while self.status().intersects(AtaStatus::BUSY) {
if self.lba_mid.read() != 0 || self.lba_high.read() != 0 {
return Err("drive was not ATA");
}
}
match AtaDeviceType::from_lba(self.lba_mid.read(), self.lba_high.read()) {
Some(AtaDeviceType::Pata) => { }, Some(AtaDeviceType::PataPi) => return Err("drive was an unsupported PATAPI device"),
Some(AtaDeviceType::Sata) => return Err("drive was an unsupported SATA device"),
Some(AtaDeviceType::SataPi) => return Err("drive was an unsupported SATAPI device"),
_ => return Err("drive was an unknown device type"),
};
let mut buffer: [u8; SECTOR_SIZE_IN_BYTES] = [0; SECTOR_SIZE_IN_BYTES];
self.wait_for_data_ready().map_err(|_| "error before identify data read")?;
for chunk in buffer.chunks_exact_mut(2) {
let word: u16 = self.data.read();
chunk[0] = word as u8;
chunk[1] = (word >> 8) as u8;
}
self.wait_for_data_done().map_err(|_| "error after identify data read")?;
Ok(AtaIdentifyData::new(buffer))
}
fn wait_for_data_ready(&self) -> Result<(), ()> {
let mut _loop_counter = 0;
loop {
let status = self.status();
_loop_counter += 1;
if status.intersects(AtaStatus::ERROR | AtaStatus::DRIVE_WRITE_FAULT) {
return Err(());
}
if status.intersects(AtaStatus::BUSY) {
if _loop_counter % 1_000_000 == 0 {
warn!("AtaBus::wait_for_data_ready() has been busy waiting for a long time... is there a device/driver problem? (status: {:?})", status);
}
continue;
}
if status.intersects(AtaStatus::DATA_REQUEST_READY) {
return Ok(()); }
}
}
fn wait_for_data_done(&self) -> Result<(), ()> {
let mut _loop_counter = 0;
loop {
let status = self.status();
_loop_counter += 1;
if status.intersects(AtaStatus::ERROR | AtaStatus::DRIVE_WRITE_FAULT) {
return Err(());
}
if status.intersects(AtaStatus::BUSY) {
if _loop_counter % 1_000_000 == 0 {
warn!("AtaBus::wait_for_data_done() has been busy waiting for a long time... is there a device/driver problem? (status: {:?})", status);
}
continue;
}
if !status.intersects(AtaStatus::DATA_REQUEST_READY) {
return Ok(()); }
}
}
fn status(&self) -> AtaStatus {
self.alternate_status.read();
self.alternate_status.read();
self.alternate_status.read();
self.alternate_status.read();
AtaStatus::from_bits_truncate(self.status.read())
}
#[allow(dead_code)]
fn error(&self) -> AtaError {
AtaError::from_bits_truncate(self.error.read())
}
fn software_reset(&mut self) {
unsafe { self.control.write(AtaControl::SRST.bits()); }
for _ in 0..10 {
self.status(); }
unsafe { self.control.write(0); }
}
}
#[derive(Debug)]
pub struct AtaDrive {
bus: Arc<Mutex<AtaBus>>,
identify_data: AtaIdentifyData,
master_slave: BusDriveSelect,
}
impl AtaDrive {
fn new(bus: Arc<Mutex<AtaBus>>, which: BusDriveSelect) -> Result<AtaDrive, &'static str> {
bus.lock().software_reset();
let identify_data = bus.lock().identify_drive(which)?;
if identify_data.capabilities & 0x200 == 0 {
return Err("drive is an ancient CHS device that doesn't support LBA addressing mode, but we don't support CHS.");
}
Ok(AtaDrive {
bus,
identify_data,
master_slave: which,
})
}
pub fn read_pio(&mut self, buffer: &mut [u8], offset_in_sectors: usize) -> Result<usize, &'static str> {
if offset_in_sectors > self.size_in_blocks() {
return Err("offset_in_sectors was out of bounds");
}
let length_in_bytes = buffer.len();
if length_in_bytes % SECTOR_SIZE_IN_BYTES != 0 {
return Err("The buffer length must be a multiple of sector size (512) bytes. ATA drives can only read at sector granularity.");
}
let lba_start = offset_in_sectors;
let lba_end = lba_start + (length_in_bytes / SECTOR_SIZE_IN_BYTES);
let sector_count = lba_end - lba_start;
if sector_count > (self.identify_data.max_blocks_per_transfer as usize) {
error!("AtaDrive::read_pio(): cannot read {} sectors, drive has a max of {} sectors per transfer.",
sector_count, self.identify_data.max_blocks_per_transfer
);
return Err("AtaDrive::read_pio(): cannot read more sectors than the drive's max");
}
self.bus.lock().read_pio(buffer, self.master_slave, lba_start, sector_count)
}
pub fn write_pio(&mut self, buffer: &[u8], offset_in_sectors: usize) -> Result<usize, &'static str> {
if offset_in_sectors > self.size_in_blocks() {
return Err("offset_in_sectors was out of bounds");
}
let length_in_bytes = buffer.len();
if length_in_bytes % SECTOR_SIZE_IN_BYTES != 0 {
return Err("The buffer length must be a multiple of sector size (512) bytes. ATA drives can only write at sector granularity.");
}
let lba_start = offset_in_sectors;
let lba_end = lba_start + (length_in_bytes / SECTOR_SIZE_IN_BYTES);
let sector_count = lba_end - lba_start;
if sector_count > (self.identify_data.max_blocks_per_transfer as usize) {
error!("AtaDrive::write_pio(): cannot write {} sectors, drive has a max of {} sectors per transfer.",
sector_count, self.identify_data.max_blocks_per_transfer
);
return Err("AtaDrive::write_pio(): cannot write more sectors than the drive's max");
}
self.bus.lock().write_pio(buffer, self.master_slave, lba_start, sector_count)
}
pub fn is_master(&self) -> bool {
match self.master_slave {
BusDriveSelect::Master => true,
BusDriveSelect::Slave => false,
}
}
}
impl StorageDevice for AtaDrive {
fn size_in_blocks(&self) -> usize {
if self.identify_data.user_addressable_sectors != 0 {
self.identify_data.user_addressable_sectors as usize
} else {
self.identify_data.max_48_bit_lba as usize
}
}
}
impl BlockIo for AtaDrive {
fn block_size(&self) -> usize { SECTOR_SIZE_IN_BYTES }
}
impl KnownLength for AtaDrive {
fn len(&self) -> usize { self.block_size() * self.size_in_blocks() }
}
impl BlockReader for AtaDrive {
fn read_blocks(&mut self, buffer: &mut [u8], block_offset: usize) -> Result<usize, IoError> {
self.read_pio(buffer, block_offset).map_err(|_e| IoError::InvalidInput)
}
}
impl BlockWriter for AtaDrive {
fn write_blocks(&mut self, buffer: &[u8], block_offset: usize) -> Result<usize, IoError> {
self.write_pio(buffer, block_offset).map_err(|_e| IoError::InvalidInput)
}
fn flush(&mut self) -> Result<(), IoError> { Ok(()) }
}
pub type AtaDriveRef = Arc<Mutex<AtaDrive>>;
#[derive(Debug)]
pub struct IdeController {
pub primary_master: Option<AtaDriveRef>,
pub primary_slave: Option<AtaDriveRef>,
pub secondary_master: Option<AtaDriveRef>,
pub secondary_slave: Option<AtaDriveRef>,
}
impl IdeController {
pub fn new(pci_device: &PciDevice) -> Result<IdeController, &'static str> {
let primary_bus_data_port = match pci_device.bars[0] {
0x0 | 0x1 => DEFAULT_PRIMARY_CHANNEL_DATA_PORT,
other => {
warn!("Untested rare condition: ATA drive PCI BAR0 was special address value: {:#X}", other);
other as u16
}
};
let primary_bus_control_port = match pci_device.bars[1] {
0x0 | 0x1 => DEFAULT_PRIMARY_CHANNEL_CONTROL_PORT,
other => {
warn!("Untested rare condition: ATA drive PCI BAR1 was special address value: {:#X}", other);
other as u16
}
};
let secondary_bus_data_port = match pci_device.bars[2] {
0x0 | 0x1 => DEFAULT_SECONDARY_CHANNEL_DATA_PORT,
other => {
warn!("Untested rare condition: ATA drive PCI BAR2 was special address value: {:#X}", other);
other as u16
}
};
let secondary_bus_control_port = match pci_device.bars[3] {
0x0 | 0x1 => DEFAULT_SECONDARY_CHANNEL_CONTROL_PORT,
other => {
warn!("Untested rare condition: ATA drive PCI BAR3 was special address value: {:#X}", other);
other as u16
}
};
let _bus_master_base = pci_device.bars[4];
interrupts::register_interrupt(ATA_PRIMARY_IRQ, primary_ata_handler).map_err(|e| {
error!("ATA Primary Bus IRQ {:#X} was already in use by handler {:#X}! Sharing IRQs is currently unsupported.",
ATA_PRIMARY_IRQ, e,
);
"ATA Primary Bus IRQ was already in use! Sharing IRQs is currently unsupported."
})?;
interrupts::register_interrupt(ATA_SECONDARY_IRQ, secondary_ata_handler).map_err(|e| {
error!("ATA Secondary Bus IRQ {:#X} was already in use by handler {:#X}! Sharing IRQs is currently unsupported.",
ATA_SECONDARY_IRQ, e,
);
"ATA Secondary Bus IRQ was already in use! Sharing IRQs is currently unsupported."
})?;
let primary_bus = Arc::new(Mutex::new(AtaBus::new(primary_bus_data_port, primary_bus_control_port)));
let secondary_bus = Arc::new(Mutex::new(AtaBus::new(secondary_bus_data_port, secondary_bus_control_port)));
let primary_master = AtaDrive::new(Arc::clone(&primary_bus), BusDriveSelect::Master);
let primary_slave = AtaDrive::new(primary_bus, BusDriveSelect::Slave);
let secondary_master = AtaDrive::new(Arc::clone(&secondary_bus), BusDriveSelect::Master);
let secondary_slave = AtaDrive::new(secondary_bus, BusDriveSelect::Slave);
let drive_fmt = |drive: &Result<AtaDrive, &str>| -> String {
match drive {
Ok(d) => format!("drive initialized, size: {} sectors", d.size_in_blocks()),
Err(e) => e.to_string(),
}
};
info!("ATA drive controller at {}: \n\
--> primary master: {} \n\
--> primary slave: {} \n\
--> secondary master: {} \n\
--> secondary slave: {}",
pci_device.location,
drive_fmt(&primary_master),
drive_fmt(&primary_slave),
drive_fmt(&secondary_master),
drive_fmt(&secondary_slave),
);
Ok( IdeController {
primary_master: primary_master.ok().map(|d| Arc::new(Mutex::new(d))),
primary_slave: primary_slave.ok().map(|d| Arc::new(Mutex::new(d))),
secondary_master: secondary_master.ok().map(|d| Arc::new(Mutex::new(d))),
secondary_slave: secondary_slave.ok().map(|d| Arc::new(Mutex::new(d))),
})
}
pub fn iter(&self) -> IdeControllerIter {
IdeControllerIter {
next: NextDrive::PrimaryMaster,
controller: self,
}
}
}
impl StorageController for IdeController {
fn devices<'c>(&'c self) -> Box<(dyn Iterator<Item = StorageDeviceRef> + 'c)> {
Box::new(
self.iter().map(|ata_drive_ref| Arc::clone(ata_drive_ref) as StorageDeviceRef)
)
}
}
#[derive(Clone)]
enum NextDrive {
PrimaryMaster,
PrimarySlave,
SecondaryMaster,
SecondarySlave,
}
#[derive(Clone)]
pub struct IdeControllerIter<'c> {
next: NextDrive,
controller: &'c IdeController,
}
impl<'c> Iterator for IdeControllerIter<'c> {
type Item = &'c AtaDriveRef;
fn next(&mut self) -> Option<Self::Item> {
match self.next {
NextDrive::PrimaryMaster => {
self.next = NextDrive::PrimarySlave;
if self.controller.primary_master.is_some() {
self.controller.primary_master.as_ref()
} else {
self.next()
}
}
NextDrive::PrimarySlave => {
self.next = NextDrive::SecondaryMaster;
if self.controller.primary_slave.is_some() {
self.controller.primary_slave.as_ref()
} else {
self.next()
}
}
NextDrive::SecondaryMaster => {
self.next = NextDrive::SecondarySlave;
if self.controller.secondary_master.is_some() {
self.controller.secondary_master.as_ref()
} else {
self.next()
}
}
NextDrive::SecondarySlave => {
if self.controller.secondary_slave.is_some() {
self.controller.secondary_slave.as_ref()
} else {
None
}
}
}
}
}
const ATA_PRIMARY_IRQ: u8 = interrupts::IRQ_BASE_OFFSET + 0xE;
const ATA_SECONDARY_IRQ: u8 = interrupts::IRQ_BASE_OFFSET + 0xF;
extern "x86-interrupt" fn primary_ata_handler(_stack_frame: InterruptStackFrame ) {
info!("Primary ATA Interrupt ({:#X})", ATA_PRIMARY_IRQ);
interrupts::eoi(ATA_PRIMARY_IRQ);
}
extern "x86-interrupt" fn secondary_ata_handler(_stack_frame: InterruptStackFrame ) {
info!("Secondary ATA Interrupt ({:#X})", ATA_SECONDARY_IRQ);
interrupts::eoi(ATA_SECONDARY_IRQ);
}
#[derive(Copy, Clone, Debug, Default)]
#[repr(packed)]
pub struct AtaIdentifyData {
pub general_configuration: u16,
pub num_cylinders: u16,
pub specific_configuration: u16,
pub num_heads: u16,
_reserved1: [u16; 2],
pub num_sectors_per_track: u16,
pub vendor_unique1: [u16; 3],
pub serial_number: AtaSerialNumber,
_reserved2: [u16; 3],
pub firmware_version: AtaFirmwareVersion,
pub model_number: AtaModelNumber,
pub max_blocks_per_transfer: u8,
pub vendor_unique2: u8,
pub trusted_computing: u16,
pub capabilities: u16,
_reserved3: u16, _reserved4: [u16; 2],
pub translation_fields_valid: u8,
pub free_fall_control_sensitivity: u8,
pub num_current_cylinders: u16,
pub num_current_heads: u16,
pub current_sectors_per_track: u16,
pub current_sector_capacity: u32,
pub current_multi_sector_setting: u8,
pub ext_command_supported: u8,
pub user_addressable_sectors: u32,
_reserved5: u16,
pub multiword_dma_support: u8,
pub multiword_dma_active: u8,
pub advanced_pio_modes: u8,
_reserved6: u8,
pub minimum_mw_transfer_cycle_time: u16,
pub recommended_mw_transfer_cycle_time: u16,
pub minimum_pio_cycle_time: u16,
pub minimum_pio_cycle_time_io_ready: u16,
pub additional_supported: u16,
_reserved7: [u16; 5],
pub queue_depth: u16,
pub serial_ata_capabilities: u32,
pub serial_ata_features_supported: u16,
pub serial_ata_features_enabled: u16,
pub major_revision: u16,
pub minor_revision: u16,
pub command_set_support: [u16; 3],
pub command_set_active: [u16; 3],
pub ultra_dma_support: u8,
pub ultra_dma_active: u8,
pub normal_security_erase_unit: u16,
pub enhanced_security_erase_unit: u16,
pub current_apm_level: u8,
_reserved8: u8,
pub master_password_id: u16,
pub hardware_reset_result: u16,
pub current_acoustic_value: u8,
pub recommended_acoustic_value: u8,
pub stream_min_request_size: u16,
pub streaming_transfer_time_dma: u16,
pub streaming_access_latency_dma_pio: u16,
pub streaming_perf_granularity: u32,
pub max_48_bit_lba: u64,
pub streaming_transfer_time: u16,
pub dsm_cap: u16,
pub physical_logical_sector_size: u16,
pub inter_seek_delay: u16,
pub world_wide_name: [u16; 4],
pub reserved_for_world_wide_name_128: [u16; 4],
pub reserved_for_tlc_technical_report: u16,
pub words_per_logical_sector: u32,
pub command_set_support_ext: u16,
pub command_set_active_ext: u16,
pub reserved_for_expanded_support_and_active: [u16; 6],
pub msn_support: u16,
pub security_status: u16,
_reserved9: [u16; 31],
pub cfa_power_mode1: u16,
_reserved10: [u16; 7],
pub nominal_form_factor: u16,
pub data_set_management_feature: u16,
pub additional_product_id: [u16; 4],
_reserved11: [u16; 2],
pub current_media_serial_number: [u16; 30],
pub sct_command_transport: u16,
_reserved12: [u16; 2],
pub block_alignment: u16,
pub write_read_verify_sector_count_mode_3_only: [u16; 2],
pub write_read_verify_sector_count_mode_2_only: [u16; 2],
pub nv_cache_capabilities: u16,
pub nv_cache_size_lsw: u16,
pub nv_cache_size_msw: u16,
pub nominal_media_rotation_rate: u16,
_reserved13: u16,
pub nv_cache_time_to_spin_up_in_seconds: u8,
_reserved14: u8,
pub write_read_verify_sector_count_mode: u8,
_reserved15: u8,
_reserved16: u16,
pub transport_major_version: u16,
pub transport_minor_version: u16,
_reserved17: [u16; 6],
pub extended_num_of_user_addressable_sectors: u64,
pub min_blocks_per_download_microcode: u16,
pub max_blocks_per_download_microcode: u16,
_reserved18: [u16; 19],
pub signature: u8,
pub checksum: u8,
}
impl AtaIdentifyData {
fn new(arr: [u8; SECTOR_SIZE_IN_BYTES])-> AtaIdentifyData {
let mut identify_data: AtaIdentifyData = unsafe { core::mem::transmute(arr) };
Self::flip_bytes(&mut identify_data.serial_number.0);
Self::flip_bytes(&mut identify_data.firmware_version.0);
Self::flip_bytes(&mut identify_data.model_number.0);
identify_data
}
fn flip_bytes(bytes: &mut [u8]) {
for pair in bytes.chunks_mut(2) {
pair.swap(0, 1);
}
}
}
#[derive(Copy, Clone, Default)]
#[repr(packed)]
pub struct AtaSerialNumber([u8; 20]);
impl fmt::Display for AtaSerialNumber {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
core::str::from_utf8(&self.0)
.map_err(|_| fmt::Error)
.and_then(|s| write!(f, "{s}"))
}
}
impl fmt::Debug for AtaSerialNumber {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "\"{self}\"")
}
}
#[derive(Copy, Clone)]
#[repr(packed)]
pub struct AtaModelNumber([u8; 40]);
impl Default for AtaModelNumber {
fn default() -> Self {
AtaModelNumber([0; 40])
}
}
impl fmt::Display for AtaModelNumber {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
core::str::from_utf8(&self.0)
.map_err(|_| fmt::Error)
.and_then(|s| write!(f, "{s}"))
}
}
impl fmt::Debug for AtaModelNumber {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "\"{self}\"")
}
}
#[derive(Copy, Clone, Default)]
#[repr(packed)]
pub struct AtaFirmwareVersion([u8; 8]);
impl fmt::Display for AtaFirmwareVersion {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
core::str::from_utf8(&self.0)
.map_err(|_| fmt::Error)
.and_then(|s| write!(f, "{s}"))
}
}
impl fmt::Debug for AtaFirmwareVersion {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "\"{self}\"")
}
}