// // Created by rick on 03-02-21. // // https://wiki.osdev.org/PCI_IDE_Controller #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ATA_SR_BSY 0x80 // Busy #define ATA_SR_DRDY 0x40 // Drive ready #define ATA_SR_DF 0x20 // Drive write fault #define ATA_SR_DSC 0x10 // Drive seek complete #define ATA_SR_DRQ 0x08 // Data request ready #define ATA_SR_CORR 0x04 // Corrected data #define ATA_SR_IDX 0x02 // Index #define ATA_SR_ERR 0x01 // Error #define ATA_ER_BBK 0x80 // Bad block #define ATA_ER_UNC 0x40 // Uncorrectable data #define ATA_ER_MC 0x20 // Media changed #define ATA_ER_IDNF 0x10 // ID mark not found #define ATA_ER_MCR 0x08 // Media change request #define ATA_ER_ABRT 0x04 // Command aborted #define ATA_ER_TK0NF 0x02 // Track 0 not found #define ATA_ER_AMNF 0x01 // No address mark #define ATA_CMD_READ_PIO 0x20 #define ATA_CMD_READ_PIO_EXT 0x24 #define ATA_CMD_READ_DMA 0xC8 #define ATA_CMD_READ_DMA_EXT 0x25 #define ATA_CMD_WRITE_PIO 0x30 #define ATA_CMD_WRITE_PIO_EXT 0x34 #define ATA_CMD_WRITE_DMA 0xCA #define ATA_CMD_WRITE_DMA_EXT 0x35 #define ATA_CMD_CACHE_FLUSH 0xE7 #define ATA_CMD_CACHE_FLUSH_EXT 0xEA #define ATA_CMD_PACKET 0xA0 #define ATA_CMD_IDENTIFY_PACKET 0xA1 #define ATA_CMD_IDENTIFY 0xEC #define ATAPI_CMD_READ 0xA8 #define ATAPI_CMD_EJECT 0x1B #define ATA_IDENT_DEVICETYPE 0 #define ATA_IDENT_CYLINDERS 2 #define ATA_IDENT_HEADS 6 #define ATA_IDENT_SECTORS 12 #define ATA_IDENT_SERIAL 20 #define ATA_IDENT_MODEL 54 #define ATA_IDENT_CAPABILITIES 98 #define ATA_IDENT_FIELDVALID 106 #define ATA_IDENT_MAX_LBA 120 #define ATA_IDENT_COMMANDSETS 164 #define ATA_IDENT_MAX_LBA_EXT 200 #define ATA_CAP_LBA 0x200 #define ATA_LBA28_MAX 0x10000000 #define ATA_HDDDEVSEL_CHS 0b00000000 #define ATA_HDDDEVSEL_LBA 0b01000000 #define ATA_HDDDEVSEL_DEFAULT 0b10100000 #define IDE_ATA 0x00 #define IDE_ATAPI 0x01 #define ATA_MASTER 0x00 #define ATA_SLAVE 0x01 // base #define ATA_REG_DATA 0x00 #define ATA_REG_ERROR 0x01 #define ATA_REG_FEATURES 0x01 #define ATA_REG_SECCOUNT0 0x02 #define ATA_REG_LBA0 0x03 #define ATA_REG_LBA1 0x04 #define ATA_REG_LBA2 0x05 #define ATA_REG_HDDEVSEL 0x06 #define ATA_REG_COMMAND 0x07 #define ATA_REG_STATUS 0x07 // ?? #define ATA_REG_SECCOUNT1 0x12 #define ATA_REG_LBA3 0x13 #define ATA_REG_LBA4 0x14 #define ATA_REG_LBA5 0x15 // ctrl #define ATA_REG_CONTROL 0x22 #define ATA_REG_ALTSTATUS 0x22 #define ATA_REG_DEVADDRESS 0x23 // Channels: #define ATA_PRIMARY 0x00 #define ATA_SECONDARY 0x01 // Directions: #define ATA_READ 0x00 #define ATA_WRITE 0x01 struct ide_channel_registers { unsigned short base; // I/O Base. unsigned short ctrl; // Control Base unsigned short bmide; // Bus Master IDE unsigned char nIEN; // nIEN (No Interrupt); } channels[2]; unsigned char ide_buf[2048] = {0}; unsigned static char ide_irq_invoked = 0; unsigned static char atapi_packet[12] = {0xA8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; struct ide_device { unsigned char reserved; // 0 (Empty) or 1 (This Drive really exists). unsigned char channel; // 0 (Primary Channel) or 1 (Secondary Channel). unsigned char drive; // 0 (Master Drive) or 1 (Slave Drive). unsigned short type; // 0: ATA, 1:ATAPI. unsigned short signature; // Drive Signature unsigned short capabilities;// Features. unsigned int commandSets; // Command Sets Supported. unsigned int size; // Size in Sectors. unsigned char model[41]; // Model in string. } ide_devices[4]; typedef struct { uint8_t device_number: 2; uint8_t print_error: 1; } ide_block_device_info; mutex_t *ide_lock = NULL; uint8_t ide_read(uint8_t channel, uint8_t reg); void ide_write(uint8_t channel, uint8_t reg, uint8_t data); uint8_t ide_read(uint8_t channel, uint8_t reg) { uint8_t result; if (reg & 0x10) { ide_write(channel, ATA_REG_CONTROL, 0x80 | channels[channel].nIEN); result = port_byte_in(channels[channel].base + (reg & 0xF)); ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN); } else if (reg & 0x20) { result = port_byte_in(channels[channel].ctrl + (reg & 0xF)); } else { result = port_byte_in(channels[channel].base + reg); } // if (reg < 0x08) // result = port_byte_in(channels[channel].base + reg - 0x00); // else if (reg < 0x0C) // result = port_byte_in(channels[channel].base + reg - 0x06); // else if (reg < 0x0E) // result = port_byte_in(channels[channel].ctrl + reg - 0x0A); // else if (reg < 0x16) // result = port_byte_in(channels[channel].bmide + reg - 0x0E); // todo this case is not handled in new code // if (reg > 0x07 && reg < 0x0C) // ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN); return result; } void ide_write(uint8_t channel, uint8_t reg, uint8_t data) { if (reg & 0x10) { ide_write(channel, ATA_REG_CONTROL, 0x80 | channels[channel].nIEN); port_byte_out(channels[channel].base + (reg & 0xF), data); ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN); } else if (reg & 0x20) { port_byte_out(channels[channel].ctrl + (reg & 0xF), data); } else { port_byte_out(channels[channel].base + reg, data); } // if (reg > 0x07 && reg < 0x0C) // ide_write(channel, ATA_REG_CONTROL, 0x80 | channels[channel].nIEN); // if (reg < 0x08) // port_byte_out(channels[channel].base + reg - 0x00, data); // else if (reg < 0x0C) // port_byte_out(channels[channel].base + reg - 0x06, data); // else if (reg < 0x0E) // port_byte_out(channels[channel].ctrl + reg - 0x0A, data); // else if (reg < 0x16) // port_byte_out(channels[channel].bmide + reg - 0x0E, data); // todo this case is not handled // if (reg > 0x07 && reg < 0x0C) // ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN); } void ide_read_buffer(unsigned char channel, unsigned char reg, unsigned int *buffer, unsigned int quads) { /* WARNING: This code contains a serious bug. The inline assembly trashes ES and * ESP for all of the code the compiler generates between the inline * assembly blocks. */ if (reg & 0x10) { ide_write(channel, ATA_REG_CONTROL, 0x80 | channels[channel].nIEN); port_double_word_in_repeat(channels[channel].base + (reg & 0xF), buffer, quads); ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN); } else if (reg & 0x20) { port_double_word_in_repeat(channels[channel].base + (reg & 0xF), buffer, quads); } else { port_double_word_in_repeat(channels[channel].base + reg, buffer, quads); } // if (reg > 0x07 && reg < 0x0C) { // ide_write(channel, ATA_REG_CONTROL, 0x80 | channels[channel].nIEN); // } // this assembly is probably necessary when reading paging // asm("pushw %es; movw %ds, %ax; movw %ax, %es" ); // if (reg < 0x08) { // port_double_word_in_repeat(channels[channel].base + reg - 0x00, buffer, quads); // } else if (reg < 0x0C) { // port_double_word_in_repeat(channels[channel].base + reg - 0x06, buffer, quads); // } else if (reg < 0x0E) { // port_double_word_in_repeat(channels[channel].ctrl + reg - 0x0A, buffer, quads); // } else if (reg < 0x16) { // port_double_word_in_repeat(channels[channel].bmide + reg - 0x0E, buffer, quads); // } // asm("popw %es;"); // if (reg > 0x07 && reg < 0x0C) { // ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN); // } } unsigned char ide_polling(unsigned char channel, uint8_t advanced_check) { // (I) Delay 400 nanosecond for BSY to be set: // ------------------------------------------------- for (int i = 0; i < 4; i++) ide_read(channel, ATA_REG_ALTSTATUS); // Reading the Alternate Status port wastes 100ns; loop four times. // (II) Wait for BSY to be cleared: // ------------------------------------------------- while (ide_read(channel, ATA_REG_STATUS) & ATA_SR_BSY); // Wait for BSY to be zero. if (advanced_check) { unsigned char state = ide_read(channel, ATA_REG_STATUS); // Read Status Register. // (III) Check For Errors: // ------------------------------------------------- if (state & ATA_SR_ERR) return 2; // Error. // (IV) Check If Device fault: // ------------------------------------------------- if (state & ATA_SR_DF) return 1; // Device Fault. // (V) Check DRQ: // ------------------------------------------------- // BSY = 0; DF = 0; ERR = 0 so we should check for DRQ now. if ((state & ATA_SR_DRQ) == 0) return 3; // DRQ should be set } return 0; // No Error. } unsigned char ide_print_error(unsigned int drive, unsigned char err) { if (err == 0) return err; debug_backtrace(false); kprint("IDE:"); if (err == 1) { kprint("- Device Fault "); err = 19; } else if (err == 2) { unsigned char st = ide_read(ide_devices[drive].channel, ATA_REG_ERROR); if (st & ATA_ER_AMNF) { kprint("- No Address Mark Found - "); err = 7; } if (st & ATA_ER_TK0NF) { kprint("- No Media or Media Error - "); err = 3; } if (st & ATA_ER_ABRT) { kprint("- Command Aborted - "); err = 20; } if (st & ATA_ER_MCR) { kprint("- No Media or Media Error - "); err = 3; } if (st & ATA_ER_IDNF) { kprint("- ID mark not Found - "); err = 21; } if (st & ATA_ER_MC) { kprint("- No Media or Media Error - "); err = 3; } if (st & ATA_ER_UNC) { kprint("- Uncorrectable Data Error - "); err = 22; } if (st & ATA_ER_BBK) { kprint("- Bad Sectors - "); err = 13; } } else if (err == 3) { kprint("- Reads Nothing - "); err = 23; } else if (err == 4) { kprint("- Write Protected - "); err = 8; } else if (err & 0xF0) { if (err == 0xF1) { kprint("- Unsupported operation by driver - "); } else { kprint(" - Unknown driver error - "); } } printf(" - [%s %s] %s\n", ((const char *[]) {"Primary", "Secondary"}[ide_devices[drive].channel]), ((const char *[]) {"Master", "Slave"}[ide_devices[drive].drive]), &ide_devices[drive].model); return err; } void ide_fix_bar(bar_info *bar, uint32_t default_address, uint32_t size) { if (bar->address == 0x0) { // no need to actually write ti back bar->address = default_address; bar->prefetchable = 0; bar->type = 0; bar->is_io_space = 1; bar->size = size; } } bool ide_pci_init_channels(pci_device *device) { pci_config_write_byte(device->bus, device->slot, device->func, PCI_CONFIG_INTERRUPT_LINE, 0xFE); if (pci_config_read_byte(device->bus, device->slot, device->func, PCI_CONFIG_INTERRUPT_LINE) == 0xFE) { #ifdef IDE_ENABLE_INTERRUPT #error "Interrupt not supported" k_panics("NOT SUPPORTED"); #else pci_config_write_byte(device->bus, device->slot, device->func, PCI_CONFIG_INTERRUPT_LINE, PCI_INTERRUPT_LINE_DISABLED); #endif } pci_init_bar(device, 0); pci_init_bar(device, 1); pci_init_bar(device, 2); pci_init_bar(device, 3); pci_init_bar(device, 4); if (!device->bar0.present || !device->bar1.present || !device->bar2.present || !device->bar3.present || !device->bar4.present) { k_panics("IDE Missing bars"); return false; } ide_fix_bar(&device->bar0, 0x1F0, 8); ide_fix_bar(&device->bar1, 0x3F6, 4); ide_fix_bar(&device->bar2, 0x170, 8); ide_fix_bar(&device->bar3, 0x376, 4); channels[ATA_PRIMARY].base = device->bar0.address; channels[ATA_PRIMARY].ctrl = device->bar1.address; channels[ATA_SECONDARY].base = device->bar2.address; channels[ATA_SECONDARY].ctrl = device->bar3.address; channels[ATA_PRIMARY].bmide = device->bar4.address + 0; // Bus Master IDE channels[ATA_SECONDARY].bmide = device->bar4.address + 8; // Bus Master IDE return true; } uint8_t ide_block_dev_access(const block_device_t *device, uint8_t direction, uint32_t lba, uint8_t sectors, void *target) { ide_block_device_info *info = device->device_info; uint8_t result = ide_access(direction, info->device_number, lba, sectors, target); if (result != 0) { if (info->print_error) { ide_print_error(info->device_number, result); } return BLOCK_DEV_ACCESS_ERR; } return BLOCK_DEV_ACCESS_OK; } void ide_register_block_devices() { for (int i = 0; i < 3; ++i) { if (!ide_devices[i].reserved) { continue; } if (ide_devices[i].type == IDE_ATAPI) { // skip ATAPI for now continue; } ide_block_device_info *info = malloc(sizeof(ide_block_device_info)); // todo free for this one info->device_number = i; info->print_error = 1; block_device_t device = { .flags.present = 1, .flags.root_device = 1, .device_info = info, .num_lba = ide_devices[i].size, .block_size = 0x200, .access = ide_block_dev_access, }; sprintf(device.identifier, "ide%d", i); block_dev_register(&device); break; } } uint8_t att_used ide_pci_validate(const pci_device *device) { if (device->class != PCI_CLASS_MASS_STORAGE || device->subclass != PCI_SUB_CLASS_MASS_IDE || (device->programInterface != 0x8A && device->programInterface != 0x80)) { return PCI_VALIDATE_FAIL; } // todo other validations return PCI_VALIDATE_OK; } uint8_t att_used ide_pci_initialize(pci_device *device) { if (!ide_pci_init_channels(device)) { return PCI_INIT_FAIL; } if (ide_lock != NULL) { k_panics("IDE already initialized\n"); } ide_lock = mutex_create(); mutex_acquire(ide_lock); // disable IRQ ide_write(ATA_PRIMARY, ATA_REG_CONTROL, 2); ide_write(ATA_SECONDARY, ATA_REG_CONTROL, 2); int count = 0; // 3- Detect ATA-ATAPI Devices: for (int i = 0; i < 2; i++) { for (int j = 0; j < 2; j++) { unsigned char err = 0, type = IDE_ATA, status; ide_devices[count].reserved = 0; // Assuming that no drive here. // (I) Select Drive: ide_write(i, ATA_REG_HDDEVSEL, 0xA0 | (j << 4)); // Select Drive. pit_sleep(1); // Wait 1ms for drive select to work. // (II) Send ATA Identify Command: ide_write(i, ATA_REG_COMMAND, ATA_CMD_IDENTIFY); pit_sleep(1); // This function should be implemented in your OS. which waits for 1 ms. // it is based on System Timer Device Driver. // (III) Polling: if (ide_read(i, ATA_REG_STATUS) == 0) continue; // If Status = 0, No Device. while (1) { status = ide_read(i, ATA_REG_STATUS); if ((status & ATA_SR_ERR)) { err = 1; break; } // If Err, Device is not ATA. if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRQ)) break; // Everything is right. } // (IV) Probe for ATAPI Devices: if (err != 0) { unsigned char cl = ide_read(i, ATA_REG_LBA1); unsigned char ch = ide_read(i, ATA_REG_LBA2); if (cl == 0x14 && ch == 0xEB) { type = IDE_ATAPI; } else if (cl == 0x69 && ch == 0x96) { type = IDE_ATAPI; } else { continue; // Unknown Type (may not be a device). } ide_write(i, ATA_REG_COMMAND, ATA_CMD_IDENTIFY_PACKET); pit_sleep(1); } // (V) Read Identification Space of the Device: ide_read_buffer(i, ATA_REG_DATA, (unsigned int *) ide_buf, 128); // (VI) Read Device Parameters: ide_devices[count].reserved = 1; ide_devices[count].type = type; ide_devices[count].channel = i; ide_devices[count].drive = j; ide_devices[count].signature = *((unsigned short *) (ide_buf + ATA_IDENT_DEVICETYPE)); ide_devices[count].capabilities = *((unsigned short *) (ide_buf + ATA_IDENT_CAPABILITIES)); ide_devices[count].commandSets = *((unsigned int *) (ide_buf + ATA_IDENT_COMMANDSETS)); // (VII) Get Size: if (ide_devices[count].commandSets & (1 << 26)) // Device uses 48-Bit Addressing: ide_devices[count].size = *((unsigned int *) (ide_buf + ATA_IDENT_MAX_LBA_EXT)); else // Device uses CHS or 28-bit Addressing: ide_devices[count].size = *((unsigned int *) (ide_buf + ATA_IDENT_MAX_LBA)); // (VIII) String indicates model of device (like Western Digital HDD and SONY DVD-RW...): for (int k = 0; k < 40; k += 2) { ide_devices[count].model[k] = ide_buf[ATA_IDENT_MODEL + k + 1]; ide_devices[count].model[k + 1] = ide_buf[ATA_IDENT_MODEL + k]; } ide_devices[count].model[40] = 0; // Terminate String. count++; } } mutex_release(ide_lock); ide_register_block_devices(); return PCI_INIT_OK; } void ide_print_devices() { for (int i = 0; i < 4; i++) { if (ide_devices[i].reserved == 1) { printf("Drive %d: %s Drive %dMB - %s\n", i, ((const char *[]) {"ATA", "ATAPI"}[ide_devices[i].type]), ide_devices[i].size / 1024 / 2, ide_devices[i].model); } else { printf("Drive %d disconnected\n", i); } } } uint8_t ide_read_ata_access(uint8_t direction, uint8_t drive, uint32_t lba, uint8_t numsects, void *target) { uint8_t lba_mode /* 0: CHS, 1:LBA28, 2: LBA48 */, dma /* 0: No DMA, 1: DMA */, cmd; uint8_t lba_io[6]; uint8_t channel = ide_devices[drive].channel; uint8_t slavebit = ide_devices[drive].drive; uint32_t bus = channels[channel].base; uint32_t words = 256; uint16_t cyl, i; uint8_t head, sect, err; ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN = (ide_irq_invoked = 0x0) + 0x02); // setup LBA data if (lba >= ATA_LBA28_MAX) { // Sure Drive should support LBA in this case, or you are // giving a wrong LBA. // LBA48: lba_mode = 2; lba_io[0] = (lba & 0x000000FF) >> 0; lba_io[1] = (lba & 0x0000FF00) >> 8; lba_io[2] = (lba & 0x00FF0000) >> 16; lba_io[3] = (lba & 0xFF000000) >> 24; lba_io[4] = 0; // LBA28 is integer, so 32-bits are enough to access 2TB. lba_io[5] = 0; // LBA28 is integer, so 32-bits are enough to access 2TB. head = 0; // Lower 4-bits of HDDEVSEL are not used here. } else if (ide_devices[drive].capabilities & ATA_CAP_LBA) { // Drive supports LBA? // LBA28: lba_mode = 1; lba_io[0] = (lba & 0x00000FF) >> 0; lba_io[1] = (lba & 0x000FF00) >> 8; lba_io[2] = (lba & 0x0FF0000) >> 16; lba_io[3] = 0; // These Registers are not used here. lba_io[4] = 0; // These Registers are not used here. lba_io[5] = 0; // These Registers are not used here. head = (lba & 0xF000000) >> 24; } else { // CHS: lba_mode = 0; sect = (lba % 63) + 1; cyl = (lba + 1 - sect) / (16 * 63); lba_io[0] = sect; lba_io[1] = (cyl >> 0) & 0xFF; lba_io[2] = (cyl >> 8) & 0xFF; lba_io[3] = 0; lba_io[4] = 0; lba_io[5] = 0; head = (lba + 1 - sect) % (16 * 63) / (63); // Head number is written to HDDEVSEL lower 4-bits. } // check if DMA is available dma = 0; // maybe later // wait for the drive to have time for us while (ide_read(channel, ATA_REG_STATUS) & ATA_SR_BSY) {} if (lba_mode == 0) { ide_write(channel, ATA_REG_HDDEVSEL, ATA_HDDDEVSEL_DEFAULT | ATA_HDDDEVSEL_CHS | (slavebit << 4) | head); // Drive & CHS. } else { ide_write(channel, ATA_REG_HDDEVSEL, ATA_HDDDEVSEL_DEFAULT | ATA_HDDDEVSEL_LBA | (slavebit << 4) | head); // Drive & LBA } // store data if (lba_mode == 2) { ide_write(channel, ATA_REG_SECCOUNT1, 0); ide_write(channel, ATA_REG_LBA3, lba_io[3]); ide_write(channel, ATA_REG_LBA4, lba_io[4]); ide_write(channel, ATA_REG_LBA5, lba_io[5]); } ide_write(channel, ATA_REG_SECCOUNT0, numsects); ide_write(channel, ATA_REG_LBA0, lba_io[0]); ide_write(channel, ATA_REG_LBA1, lba_io[1]); ide_write(channel, ATA_REG_LBA2, lba_io[2]); // pick correct ccommand if (lba_mode == 0 && dma == 0 && direction == 0) cmd = ATA_CMD_READ_PIO; if (lba_mode == 1 && dma == 0 && direction == 0) cmd = ATA_CMD_READ_PIO; if (lba_mode == 2 && dma == 0 && direction == 0) cmd = ATA_CMD_READ_PIO_EXT; // if (lba_mode == 0 && dma == 1 && direction == 0) cmd = ATA_CMD_READ_DMA; // if (lba_mode == 1 && dma == 1 && direction == 0) cmd = ATA_CMD_READ_DMA; // if (lba_mode == 2 && dma == 1 && direction == 0) cmd = ATA_CMD_READ_DMA_EXT; if (lba_mode == 0 && dma == 0 && direction == 1) cmd = ATA_CMD_WRITE_PIO; if (lba_mode == 1 && dma == 0 && direction == 1) cmd = ATA_CMD_WRITE_PIO; if (lba_mode == 2 && dma == 0 && direction == 1) cmd = ATA_CMD_WRITE_PIO_EXT; // if (lba_mode == 0 && dma == 1 && direction == 1) cmd = ATA_CMD_WRITE_DMA; // if (lba_mode == 1 && dma == 1 && direction == 1) cmd = ATA_CMD_WRITE_DMA; // if (lba_mode == 2 && dma == 1 && direction == 1) cmd = ATA_CMD_WRITE_DMA_EXT; ide_write(channel, ATA_REG_COMMAND, cmd); // Send the Command. void *cur_addr = target; // read response if (dma) { if (direction == 0); // DMA Read. else; // DMA Write. } else { if (direction == 0) { // PIO Read. for (i = 0; i < numsects; i++) { if ((err = ide_polling(channel, 1))) { ide_print_error(drive, err); return err; // Polling, set error and exit if there is. } port_word_in_repeat(bus, cur_addr, words); cur_addr += (words * 2); } } else { // PIO Write. for (i = 0; i < numsects; i++) { ide_polling(channel, 0); // Polling. port_word_out_repeat(bus, cur_addr, words); cur_addr += (words * 2); } ide_write(channel, ATA_REG_COMMAND, (char[]) {ATA_CMD_CACHE_FLUSH, ATA_CMD_CACHE_FLUSH, ATA_CMD_CACHE_FLUSH_EXT}[lba_mode]); ide_polling(channel, 0); // Polling. } } return 0; } uint8_t ide_access(uint8_t direction, uint8_t drive, uint32_t lba, uint8_t numsects, void *target) { if (drive > 3 || ide_devices[drive].reserved == 0 || ide_devices[drive].type == IDE_ATAPI) { return 0xF1; } mutex_acquire(ide_lock); uint8_t result = ide_read_ata_access(direction, drive, lba, numsects, target); mutex_release(ide_lock); return result; } PCI_DRIVER(900) = { .name = "pci-ide", .description = "Default PCI IDE Driver", .validatable = true, .initialisable = true, .match.class = PCI_CLASS_MASS_STORAGE, .match.subclass = PCI_SUB_CLASS_MASS_IDE, .mask.class = true, .mask.subclass = true, .validate = ide_pci_validate, .initialize = ide_pci_initialize, };