pci初始化時,遍歷pci上的設置,如果BaseClassCode==1,則為大容量存儲控制器,包括硬盤控制器、固態硬盤控制器、光盤驅動控制器、RAID控制器等。
BaseAdder4為DMA控制器基地址,包含兩個控制器,主控制器,次控制器,每個占8字節。
dma只能使用物理地址,最好開啟硬盤狀態中斷。
具體過程見代碼
//主控制器占8位,次控制器占用8字節,意義相同
#define DMA_COMMAND_REG 0 // dma主控命令寄存器的偏移 1字節,第1、3字節保留,沒有用途
//第3位置為0時,表示讀扇區,DMA傳送方向為從IDE設備到內存;為1時,表示寫扇區,方向為從內存到IDE設備。
//第0位置為0時,表示停止DMA傳輸;為1時表示啟動DMA傳輸
//
#define DMA_STATUS_REG 2 // dma主控狀態寄存器的偏移
//第0位置為1時,正在進行DMA傳輸;第1位置為1時,表示DMA傳送出現了一個錯誤;
//第2位置為1時,IDE設備已產生一個中斷請求(DMA傳輸已完成);
//第5位置為1時,表示設備0(主盤)能夠執行DMA操作;
//第6位置為1時,表示設備1(從盤)能夠執行DMA操作;
//第7位置為1時,表示設備0和設備1不能同時執行DMA操作。#define DMA_PRD_ADDR_REG 4 // 物理區域描述符指針寄存器的偏移
//物理區域描述符表,連續排列,每個項占8字節,typedef struct {DWORD addr; //前4位為緩存區物理地址,WORD len; //當前塊長度,WORD EOT; //只使用最高位
}__attribute__((packed)) PRD_ADD;
#define pio_base_addr1 0x01F0 // 主ATA設備控制塊寄存器基地址
#define pio_base_addr2 0x03F0 // 主ATA命令命令塊寄存器基地址
void dma_read_sectors(DWORD bmcr_base_addr, DWORD lbaSector, PVOID buf,WORD len) {PRD_ADD prdBufAddr; //物理區域描述符地址// bufferaddr // 內存緩沖區地址// Start/Stop=0, 停止以前的DMA傳輸WritePortByte(bmcr_base_addr + DMA_COMMAND_REG, 0x00);// 清除主控狀態寄存器的Interrupt和Error位WritePortByte(bmcr_base_addr + DMA_STATUS_REG, 6);//物理區域描述符表,連續排列 EOT=1 表示為最后一個prdBufAddr.addr = (DWORD) buf; //這里應為物理地址prdBufAddr.len= len; //len應小于或等于0x200prdBufAddr.EOT = 0x8000; //最高位為EOT// 物理區域描述符的地址寫入PRDTRWritePortDword(bmcr_base_addr + DMA_PRD_ADDR_REG,MiGetPhysics(&prdBufAddr));// 主控命令寄存器的R/W=1, 表示寫入內存(讀取硬盤)WritePortByte(bmcr_base_addr + DMA_COMMAND_REG, 8);// 等待硬盤BSY=0和DRQ=0//busy_wait();// 設置設備/磁頭寄存器的DEV=0WritePortByte( pio_base_addr1 + 6, 00);// 等待硬盤BSY=0和DRQ=0//busy_wait();// 設備控制寄存器的nIEN=0, 允許中斷WritePortByte( pio_base_addr2 + 6, 00);// 設置ATA寄存器WritePortByte( pio_base_addr1 + 1, 00); // =00WritePortByte( pio_base_addr1 + 2, 1); // numSect扇區數量WritePortByte( pio_base_addr1 + 3, lbaSector >> 0); // LBA第7~0位WritePortByte( pio_base_addr1 + 4, lbaSector >> 8); // LBA第15~8位WritePortByte( pio_base_addr1 + 5, lbaSector >> 16); // LBA第23~16位// 設備/磁頭寄存器:LBA=1, DEV=0, LBA第27~24位WritePortByte( pio_base_addr1 + 6, 0x40 | (lbaSector >> 24));// 設置ATA命令寄存器WritePortByte( pio_base_addr1 + 7, 0x0C8); // 0C8h=Read DMA// 讀取主控命令寄存器和主控狀態寄存器ReadPortByte(bmcr_base_addr + DMA_COMMAND_REG);ReadPortByte(bmcr_base_addr + DMA_STATUS_REG);// 主控命令寄存器的R/W=1,Start/Stop=1, 啟動DMA傳輸WritePortByte(bmcr_base_addr + DMA_COMMAND_REG, 9);// 現在開始DMA數據傳送// 檢查主控狀態寄存器, Interrupt=1時,傳送結束//mov ecx, 4000hnotAsserted: while (!(ReadPortByte(bmcr_base_addr + DMA_STATUS_REG) & 4)) {hlt();};// 清除主控狀態寄存器的Interrupt位WritePortByte(bmcr_base_addr + DMA_STATUS_REG, 4);// 讀取主控狀態寄存器ReadPortByte(bmcr_base_addr + DMA_STATUS_REG);// 主控命令寄存器的Start/Stop=0, 結束DMA傳輸WritePortByte(bmcr_base_addr + DMA_COMMAND_REG, 00);
}