IDE: Implement writing using Bus Mastering DMA
This commit is contained in:
parent
9329059510
commit
c6f7f7665c
|
@ -76,9 +76,15 @@ const CMD_IDENTIFY: u8 = 0xEC;
|
|||
/// ATA read using LBA28 DMA command
|
||||
const CMD_READ_DMA: u8 = 0xC8;
|
||||
|
||||
/// ATA write using LBA28 DMA command
|
||||
const CMD_WRITE_DMA: u8 = 0xCA;
|
||||
|
||||
/// ATA read using LBA48 DMA command
|
||||
const CMD_READ_DMA_EXT: u8 = 0x25;
|
||||
|
||||
/// ATA write using LBA48 DMA command
|
||||
const CMD_WRITE_DMA_EXT: u8 = 0x35;
|
||||
|
||||
pub struct PciIde {
|
||||
device_info: PciDeviceInfo,
|
||||
ide_devices: Vec<IdeDevice>,
|
||||
|
@ -311,14 +317,14 @@ impl PciIde {
|
|||
loop {
|
||||
let status = unsafe { self.bmi_status(channel) };
|
||||
|
||||
// FIXME: error handling
|
||||
|
||||
// Bit 2 (INT) set?
|
||||
if (status >> 2) & 1 == 1 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: error handling
|
||||
|
||||
unsafe {
|
||||
// Stop DMA
|
||||
self.stop(channel);
|
||||
|
@ -335,6 +341,91 @@ impl PciIde {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write(
|
||||
&mut self,
|
||||
channel: Channel,
|
||||
drive: Drive,
|
||||
lba: u64,
|
||||
data: &[u8],
|
||||
) -> Result<(), TryFromIntError> {
|
||||
// FIXME: make this an error
|
||||
assert!(data.len() % SECTOR_SIZE as usize == 0);
|
||||
|
||||
let lba48_support = self
|
||||
.ide_devices
|
||||
.iter()
|
||||
.find(|d| d.channel == channel && d.drive == drive)
|
||||
.map(|d| d.lba48_support)
|
||||
.unwrap(); // FIXME: make this an error
|
||||
let lba48 = lba > 0xFFFFFFF && lba48_support;
|
||||
|
||||
// FIXME: make this an error
|
||||
assert!((lba48 && lba > 0xFFFFFFF) || (!lba48 && lba <= 0xFFFFFFF));
|
||||
|
||||
let byte_count = data.len() as u16;
|
||||
let sector_count = byte_count / SECTOR_SIZE;
|
||||
|
||||
// prepare PRD table
|
||||
let prd = PRDT_START as *mut PhysRegionDescriptor;
|
||||
unsafe {
|
||||
(*prd).data_buffer = self.buffer_frames.as_ref().unwrap()[0]
|
||||
.start_address()
|
||||
.as_u64()
|
||||
.try_into()?;
|
||||
(*prd).byte_count = byte_count;
|
||||
// this is the end of table
|
||||
(*prd).eot = 1 << 7;
|
||||
// this byte is reserved, we should probably set it to 0
|
||||
(*prd)._0 = 0;
|
||||
}
|
||||
|
||||
// copy the data over to the DMA buffer
|
||||
for i in 0..byte_count {
|
||||
let addr = (BUFFER_START + i as u64) as *mut u8;
|
||||
unsafe {
|
||||
*addr = *data.get(i as usize).unwrap_or(&0);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
self.load_prdt(channel);
|
||||
self.stop(channel);
|
||||
self.set_write(channel);
|
||||
self.clear_bmi_status(channel);
|
||||
select_drive(drive, channel);
|
||||
set_lba(channel, lba, sector_count, lba48);
|
||||
|
||||
if lba48 {
|
||||
ata_send_command(CMD_WRITE_DMA_EXT, channel);
|
||||
} else {
|
||||
ata_send_command(CMD_WRITE_DMA, channel);
|
||||
}
|
||||
|
||||
self.start(channel);
|
||||
}
|
||||
|
||||
loop {
|
||||
let status = unsafe { self.bmi_status(channel) };
|
||||
|
||||
// FIXME: error handling
|
||||
|
||||
// Bit 2 (INT) set?
|
||||
if (status >> 2) & 1 == 1 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
// Stop DMA
|
||||
self.stop(channel);
|
||||
|
||||
// Clear the interrupt bit
|
||||
self.clear_bmi_status(channel);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn device_info(&self) -> PciDeviceInfo {
|
||||
self.device_info
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue