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应小于或等于0x200
prdBufAddr.EOT = 0x8000; //最高位为EOT
// 物理区域描述符的地址写入PRDTR
WritePortDword(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=0
WritePortByte( 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); // =00
WritePortByte( 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, 4000h
notAsserted: 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);
}