📄 ide_raw.c
字号:
/*** Copyright 2001, Travis Geiselbrecht. All rights reserved.** Copyright 2001-2002, Rob Judd <judd@ob-wan.com>** Distributed under the terms of the NewOS License.**** With acknowledgements to Hale Landis <hlandis@ibm.net>** who wrote the reference implementation.*/#include <kernel/arch/cpu.h>#include <kernel/sem.h>#include <kernel/debug.h>#include <string.h>#include "ide_private.h"#include "ide_raw.h"// Waste some time by reading the alternate status a few times.// This gives the drive time to set BUSY in the status register on// really fast systems. If we don't do this, a slow drive on a fast// system may not set BUSY fast enough and we would think it had// completed the command when it really had not even started yet.#define DELAY400NS { pio_inbyte(CB_ASTAT); pio_inbyte(CB_ASTAT); \ pio_inbyte(CB_ASTAT); pio_inbyte(CB_ASTAT); }// Standard ide base addresses. For pc-card (pcmcia) drives, use// unused contiguous address block { 100H < (base1=base2) < 3F0H }// Non-standard addresses also exist. For ATA via sound cards try:// pio_base2 = 168/368, irq 9 or 10// pio_base3 = 1e8/3e8, irq 11 or 12unsigned int pio_base0_addr1 = 0x1f0; // Command block, ide bus 0unsigned int pio_base0_addr2 = 0x3f0; // Control block, ide bus 0unsigned int pio_base1_addr1 = 0x170; // Command block, ide bus 1unsigned int pio_base1_addr2 = 0x370; // Control block, ide bus 1unsigned int pio_memory_seg = 0;unsigned int pio_reg_addrs[10];unsigned char pio_last_read[10];unsigned char pio_last_write[10];static uint8 pio_inbyte(uint16 port){// dprintf("inb 0x%x\n", pio_reg_addrs[port]); return in8(pio_reg_addrs[port]);}static uint16 pio_inword(uint16 port){// dprintf("inw 0x%x\n", pio_reg_addrs[port]); return in16(pio_reg_addrs[port]);}static void pio_outbyte(uint16 port, uint8 data){// dprintf("outb 0x%x, 0x%x\n", pio_reg_addrs[port], data); out8(data, pio_reg_addrs[port]);}static void pio_rep_inword(uint16 port, uint16 *addr, unsigned long count){ __asm__ __volatile__ ( "rep ; insw" : "=D" (addr), "=c" (count) : "d" (pio_reg_addrs[port]), "0" (addr), "1" (count) );}static void pio_rep_outword(uint16 port, uint16 *addr, unsigned long count){ __asm__ __volatile__ ( "rep ; outsw" : "=S" (addr), "=c" (count) : "d" (pio_reg_addrs[port]), "0" (addr), "1" (count) );}static void ide_reg_poll(){ while(1) { if ((pio_inbyte(CB_ASTAT) & CB_STAT_BSY) == 0) break; }}static bool ide_wait_busy(){ int i; for(i=0; i<100000; i++) { if((i % 10000) == 0) dprintf("CB_ASTAT = 0x%x\n", pio_inbyte(CB_ASTAT)); if ((pio_inbyte(CB_ASTAT) & CB_STAT_BSY) == 0) return true; } return false;}static int ide_select_device(int bus, int device){ uint8 status; int i; ide_device ide = devices[(bus*2) + device]; dprintf("0a\n"); // test for a known, valid device if(ide.device_type == (NO_DEVICE | UNKNOWN_DEVICE)) return NO_ERROR; dprintf("0b\n"); // See if we can get it's attention if(ide_wait_busy() == false) return ERR_DEV_TIMED_OUT; // Select required device pio_outbyte(CB_DH, device ? CB_DH_DEV1 : CB_DH_DEV0); DELAY400NS; for(i=0; i<10000; i++) { // read the device status status = pio_inbyte(CB_STAT); dprintf("0c status 0x%x\n", status); if (ide.device_type == ATA_DEVICE) { if ((status & (CB_STAT_BSY | CB_STAT_RDY | CB_STAT_SKC)) == (CB_STAT_RDY | CB_STAT_SKC)) return NO_ERROR; } else { if ((status & CB_STAT_BSY) == 0) return NO_ERROR; } } dprintf("0d\n"); return ERR_DEV_TIMED_OUT;}static void ide_delay(int bus, int device){ ide_device ide = devices[(bus*2) + device]; if(ide.device_type == ATAPI_DEVICE) thread_snooze(1000000); return;}static int reg_pio_data_in(int bus, int dev, int cmd, int fr, int sc, unsigned int cyl, int head, int sect, uint8 *output, uint16 numSect, unsigned int multiCnt){ unsigned char devHead; unsigned char devCtrl; unsigned char cylLow; unsigned char cylHigh; unsigned char status; uint16 *buffer = (uint16*)output; dprintf("reg_pio_data_in: bus %d dev %d cmd %d fr %d sc %d cyl %d head %d sect %d numSect %d multiCnt %d\n", bus, dev, cmd, fr, sc, cyl, head, sect, numSect, multiCnt); devCtrl = CB_DC_HD15 | CB_DC_NIEN; devHead = dev ? CB_DH_DEV1 : CB_DH_DEV0; devHead = devHead | (head & 0x4f); cylLow = cyl & 0x00ff; cylHigh = (cyl & 0xff00) >> 8; // these commands transfer only 1 sector if(cmd == (CMD_IDENTIFY_DEVICE | CMD_IDENTIFY_DEVICE_PACKET | CMD_READ_BUFFER)) numSect = 1; // multiCnt = 1 unless CMD_READ_MULTIPLE true if(cmd != CMD_READ_MULTIPLE || !multiCnt) multiCnt = 1; // select the drive if(ide_select_device(bus, dev) == ERR_DEV_TIMED_OUT) return ERR_DEV_TIMED_OUT; // set up the registers pio_outbyte(CB_DC, devCtrl); pio_outbyte(CB_FR, fr); pio_outbyte(CB_SC, sc); pio_outbyte(CB_SN, sect); pio_outbyte(CB_CL, cylLow); pio_outbyte(CB_CH, cylHigh); pio_outbyte(CB_DH, devHead); // Start the command. The drive should immediately set BUSY status. pio_outbyte(CB_CMD, cmd); DELAY400NS; while(1) { ide_delay(bus, dev); // ensure drive isn't still busy ide_reg_poll(); // check status once only per read status = pio_inbyte(CB_STAT); if((numSect < 1) && (status & CB_STAT_DRQ)) return ERR_DEV_BUSY; if (numSect < 1) break; if((status & (CB_STAT_BSY | CB_STAT_DRQ)) == CB_STAT_DRQ) { unsigned int wordCnt = multiCnt > numSect ? numSect : multiCnt; wordCnt = wordCnt * 256; pio_rep_inword(CB_DATA, buffer, wordCnt); DELAY400NS; numSect = numSect - multiCnt; buffer += wordCnt; } // catch all possible fault conditions if(status & CB_STAT_BSY) return ERR_DEV_BUSY; if(status & CB_STAT_DF) return ERR_DEV_BUSY; if(status & CB_STAT_ERR) return ERR_DEV_HARDWARE_ERROR; if((status & CB_STAT_DRQ) == 0) return ERR_DEV_BUSY; } return NO_ERROR;}static int reg_pio_data_out(int bus, int dev, int cmd, int fr, int sc, unsigned int cyl, int head, int sect, const uint8 *output, uint16 numSect, unsigned int multiCnt){ unsigned char devHead; unsigned char devCtrl; unsigned char cylLow; unsigned char cylHigh; unsigned char status; uint16 *buffer = (uint16*)output; devCtrl = CB_DC_HD15 | CB_DC_NIEN; devHead = dev ? CB_DH_DEV1 : CB_DH_DEV0; devHead = devHead | (head & 0x4f); cylLow = cyl & 0x00ff; cylHigh = (cyl & 0xff00) >> 8; if (cmd == CMD_WRITE_BUFFER) numSect = 1; // only Write Multiple and CFA Write Multiple W/O Erase uses multCnt if ((cmd != CMD_WRITE_MULTIPLE) && (cmd != CMD_CFA_WRITE_MULTIPLE_WO_ERASE)) multiCnt = 1; // select the drive if (ide_select_device(bus, dev) != NO_ERROR) return ERR_DEV_TIMED_OUT; dprintf("1\n"); // set up the registers pio_outbyte(CB_DC, devCtrl); pio_outbyte(CB_FR, fr); pio_outbyte(CB_SC, sc); pio_outbyte(CB_SN, sect); pio_outbyte(CB_CL, cylLow); pio_outbyte(CB_CH, cylHigh); pio_outbyte(CB_DH, devHead); // Start the command. The drive should immediately set BUSY status. pio_outbyte(CB_CMD, cmd); DELAY400NS; if (ide_wait_busy() == false) return ERR_DEV_TIMED_OUT; dprintf("2\n"); status = pio_inbyte(CB_STAT); while (1) { if ((status & (CB_STAT_BSY | CB_STAT_DRQ)) == CB_STAT_DRQ) { unsigned int wordCnt = multiCnt > numSect ? numSect : multiCnt; wordCnt = wordCnt * 256; pio_rep_outword(CB_DATA, buffer, wordCnt); DELAY400NS; numSect = numSect - multiCnt; buffer += wordCnt; } dprintf("3 status 0x%x\n", status); // check all possible fault conditions if(status & CB_STAT_BSY) return ERR_DEV_BUSY; if(status & CB_STAT_DF) return ERR_DEV_BUSY; if(status & CB_STAT_ERR) return ERR_DEV_HARDWARE_ERROR; if ((status & CB_STAT_DRQ) == 0) return ERR_DEV_BUSY; ide_delay(bus, dev); // ensure drive isn't still busy ide_reg_poll(); if(numSect < 1 && status & (CB_STAT_BSY | CB_STAT_DF | CB_STAT_ERR)) { dprintf("reg_pio_data_out(): status = 0x%x\n", status); return ERR_DEV_BUSY; } } dprintf("4\n"); return NO_ERROR;}static void ata_block_to_chs(uint32 block, ide_device *device, int *cylinder, int *head, int *sect){ *sect = (block % device->hardware_device.sectors) + 1; block /= device->hardware_device.sectors; *head = (block % device->hardware_device.heads);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -