disk_atapi.rs 4.2 KB
Newer Older
1 2
#![allow(dead_code)]

3 4
use std::ptr;

5 6 7 8 9 10 11 12 13
use byteorder::{ByteOrder, BigEndian};

use syscall::io::Dma;
use syscall::error::{Result, ENOSYS, Error};

use super::hba::{HbaPort, HbaCmdTable, HbaCmdHeader};
use super::Disk;

const SCSI_READ_CAPACITY: u8 = 0x25;
14
const SCSI_READ10: u8 = 0x28;
15 16 17 18 19 20 21 22

pub struct DiskATAPI {
    id: usize,
    port: &'static mut HbaPort,
    size: u64,
    clb: Dma<[HbaCmdHeader; 32]>,
    ctbas: [Dma<HbaCmdTable>; 32],
    _fb: Dma<[u8; 256]>,
23 24
    // Just using the same buffer size as DiskATA
    // Although the sector size is different (and varies)
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
    buf: Dma<[u8; 256 * 512]>
}

impl DiskATAPI {
    pub fn new(id: usize, port: &'static mut HbaPort) -> Result<Self> {
        let mut clb = Dma::zeroed()?;
        let mut ctbas = [
            Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?,
            Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?,
            Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?,
            Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?,
            Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?,
            Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?,
            Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?,
            Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?,
        ];
        let mut fb = Dma::zeroed()?;
        let buf = Dma::zeroed()?;

        port.init(&mut clb, &mut ctbas, &mut fb);

        let size = unsafe { port.identify_packet(&mut clb, &mut ctbas).unwrap_or(0) };

        Ok(DiskATAPI {
            id: id,
            port: port,
            size: size,
            clb: clb,
            ctbas: ctbas,
            _fb: fb,
            buf: buf
        })
    }
58 59 60 61 62 63 64 65

    fn read_capacity(&mut self) -> Result<(u32, u32)> {
        // TODO: only query when needed (disk changed)

        let mut cmd = [0; 16];
        cmd[0] = SCSI_READ_CAPACITY;
        self.port.packet(&cmd, 8, &mut self.clb, &mut self.ctbas, &mut self.buf)?;

66 67
        // Instead of a count, contains number of last LBA, so add 1
        let blk_count = BigEndian::read_u32(&self.buf[0..4]) + 1;
68 69 70 71
        let blk_size = BigEndian::read_u32(&self.buf[4..8]);

        Ok((blk_count, blk_size))
    }
72 73 74 75 76 77 78 79
}

impl Disk for DiskATAPI {
    fn id(&self) -> usize {
        self.id
    }

    fn size(&mut self) -> u64 {
80 81 82
        match self.read_capacity() {
            Ok((blk_count, blk_size)) => (blk_count as u64) * (blk_size as u64),
            Err(_) => 0 // XXX
83 84 85
        }
    }

86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
    fn read(&mut self, block: u64, buffer: &mut [u8]) -> Result<usize> {
        // TODO: Handle audio CDs, which use special READ CD command
 
        let blk_len = self.block_length()?;
        let sectors = buffer.len() as u32 / blk_len;

        fn read10_cmd(block: u32, count: u16) -> [u8; 16] {
            let mut cmd = [0; 16];
            cmd[0] = SCSI_READ10;
            BigEndian::write_u32(&mut cmd[2..6], block as u32);
            BigEndian::write_u16(&mut cmd[7..9], count as u16);
            cmd
        }

        let mut sector = 0;
        let buf_len = (256 * 512) / blk_len;
        let buf_size = buf_len * blk_len;
        while sectors - sector >= buf_len {
            let cmd = read10_cmd(block as u32 + sector, buf_len as u16);
            self.port.packet(&cmd, buf_size, &mut self.clb, &mut self.ctbas, &mut self.buf)?;

            unsafe { ptr::copy(self.buf.as_ptr(), buffer.as_mut_ptr().offset(sector as isize * blk_len as isize), buf_size as usize); }

            sector += blk_len;
        }
        if sector < sectors {
            let cmd = read10_cmd(block as u32 + sector, (sectors - sector) as u16);
            self.port.packet(&cmd, buf_size, &mut self.clb, &mut self.ctbas, &mut self.buf)?;

            unsafe { ptr::copy(self.buf.as_ptr(), buffer.as_mut_ptr().offset(sector as isize * blk_len as isize), ((sectors - sector) * blk_len) as usize); }

            sector += sectors - sector;
        }

        Ok((sector * blk_len) as usize)
121 122 123 124 125 126
    }

    fn write(&mut self, _block: u64, _buffer: &[u8]) -> Result<usize> {
        Err(Error::new(ENOSYS)) // TODO: Implement writting
    }
    
127 128 129
    fn block_length(&mut self) -> Result<u32> {
        Ok(self.read_capacity()?.1)
    }
130
}