|
1 | | -#include "ata_pio.h" |
| 1 | +/*------------------------------------------------------------------------------ |
| 2 | + * Kintsugi OS Drivers source code |
| 3 | + * File: kernel/drivers/ata_pio.c |
| 4 | + * Title: Драйвер ATA PIO |
| 5 | + * Description: Реализация драйвера для работы с жесткими дисками через PIO mode. |
| 6 | + * ---------------------------------------------------------------------------*/ |
2 | 7 |
|
| 8 | +#include "ata_pio.h" |
3 | 9 | #include "lowlevel_io.h" |
| 10 | +#include "../kklibc/kklibc.h" |
4 | 11 | #include "screen.h" |
5 | 12 |
|
6 | | -// Источник: https://wiki.osdev.org/ATA_PIO_Mode |
| 13 | +// внутреннее API ядра |
| 14 | +static void ata_pio_select_drive(u8 drive); |
| 15 | +static void ata_pio_set_lba(u32 lba, u8 drive); |
7 | 16 |
|
8 | | -static void ata_wait_ready() { |
9 | | - while (port_byte_in(ATA_REG_STATUS) & ATA_SR_BSY) |
10 | | - ; |
11 | | -} |
| 17 | +// глобал переменные для хранения информации о дисках |
| 18 | +ata_disk_info_t ata_disks[2]; // 0 - master, 1 - slave |
12 | 19 |
|
13 | | -static void ata_cache_flush() { |
14 | | - ata_wait_ready(); |
15 | | - port_byte_out(ATA_REG_COMMAND, ATA_CMD_CACHE_FLUSH); |
16 | | - ata_wait_ready(); |
17 | | -} |
| 20 | +// внешнее api ядра |
18 | 21 |
|
19 | | -void ata_read_sectors(u32 lba, u32 sector_count, const u8* buffer) { |
20 | | - ata_wait_ready(); |
| 22 | +void ata_pio_init() { |
| 23 | + kprint("Initializing ATA PIO driver...\n"); |
21 | 24 |
|
22 | | - // Выбор диска (Master) |
23 | | - port_byte_out(ATA_REG_DRIVE_SELECT, 0xE0 | ((lba >> 24) & 0x0F)); |
| 25 | + for (int i = 0; i < 2; i++) { |
| 26 | + u8 drive = (i == 0) ? ATA_MASTER : ATA_SLAVE; |
24 | 27 |
|
25 | | - // Задержка для стабилизации контроллера |
26 | | - port_byte_out(ATA_REG_ERROR, 0x00); |
| 28 | + // чекаем наличие устройства |
| 29 | + ata_pio_select_drive(drive); |
| 30 | + u8 status = port_byte_in(ATA_PRIMARY_STATUS); |
27 | 31 |
|
28 | | - port_byte_out(ATA_REG_SECCOUNT, sector_count); // Количество секторов для чтения |
29 | | - port_byte_out(ATA_REG_LBA0, (u8)(lba & 0xFF)); |
30 | | - port_byte_out(ATA_REG_LBA1, (u8)((lba >> 8) & 0xFF)); |
31 | | - port_byte_out(ATA_REG_LBA2, (u8)((lba >> 16) & 0xFF)); |
32 | | - port_byte_out(ATA_REG_COMMAND, ATA_CMD_READ_SECTORS); |
| 32 | + if (status == 0xFF) { |
| 33 | + printf("Drive %d: no device connected\n", i); |
| 34 | + continue; |
| 35 | + } |
33 | 36 |
|
34 | | - ata_wait_ready(); |
| 37 | + int result = ata_pio_identify(drive, &ata_disks[i]); |
35 | 38 |
|
36 | | - for (u8 sector = 0; sector < sector_count; sector++) { |
37 | | - while (!(port_byte_in(ATA_REG_STATUS) & ATA_SR_DRQ)) |
38 | | - ; |
39 | | - // ПРИМЕЧАНИЕ: При чтении слов в little-endian порядок байт сохраняется корректно |
40 | | - rep_insw( |
41 | | - ATA_REG_DATA, (void*)(&buffer[sector * SECTOR_BYTES]), SECTOR_WORDS); // 256 слов (512 байт) |
42 | | - } |
43 | | -} |
44 | | - |
45 | | -void ata_write_sectors(u32 lba, u32 sector_count, const u8* buffer) { |
46 | | - ata_wait_ready(); |
47 | | - |
48 | | - // Выбор диска (Master) |
49 | | - port_byte_out(ATA_REG_DRIVE_SELECT, 0xE0 | ((lba >> 24) & 0x0F)); |
50 | | - |
51 | | - // Задержка для стабилизации контроллера |
52 | | - port_byte_out(ATA_REG_ERROR, 0x00); |
53 | | - |
54 | | - port_byte_out(ATA_REG_SECCOUNT, (u8)sector_count); // Количество секторов для записи |
55 | | - port_byte_out(ATA_REG_LBA0, (u8)(lba & 0xFF)); |
56 | | - port_byte_out(ATA_REG_LBA1, (u8)((lba >> 8) & 0xFF)); |
57 | | - port_byte_out(ATA_REG_LBA2, (u8)((lba >> 16) & 0xFF)); |
58 | | - port_byte_out(ATA_REG_COMMAND, ATA_CMD_WRITE_SECTORS); |
59 | | - |
60 | | - for (u8 sector = 0; sector < sector_count; sector++) { |
61 | | - while (!(port_byte_in(ATA_REG_STATUS) & ATA_SR_DRQ)) |
62 | | - ; |
63 | | - for (u32 sw = 0; sw < SECTOR_WORDS; sw++) { |
64 | | - // Преобразование в big-endian для контроллера |
65 | | - outsw(ATA_REG_DATA, |
66 | | - ((u16)buffer[sector * SECTOR_BYTES + sw * 2 + 1]) << 8 |
67 | | - | (u16)(buffer[sector * SECTOR_BYTES + sw * 2])); |
| 39 | + if (result == 0) { |
| 40 | + printf("Drive %d: %s %d MB\n", i, ata_disks[i].model, |
| 41 | + (ata_disks[i].size * 512) / (1024 * 1024)); |
| 42 | + } else { |
| 43 | + printf("Drive %d: identification failed (error %d)\n", i, result); |
68 | 44 | } |
69 | 45 | } |
| 46 | +} |
70 | 47 |
|
71 | | - ata_cache_flush(); |
| 48 | +static void ata_pio_select_drive(u8 drive) { |
| 49 | + // выюор диска (master/slave) и режима LBA |
| 50 | + port_byte_out(ATA_PRIMARY_DRIVE_SEL, drive | 0x40); // LBA mode |
| 51 | + // задержка для стабильности |
| 52 | + for (int i = 0; i < 4; i++) port_byte_in(ATA_PRIMARY_STATUS); |
72 | 53 | } |
73 | 54 |
|
74 | | -void ata_select_drive(int is_master) { |
75 | | - port_byte_out(ATA_REG_DRIVE_SELECT, is_master ? 0xA0 : 0xB0); |
| 55 | +static void ata_pio_set_lba(u32 lba, u8 drive) { |
| 56 | + // установочка LBA адреса |
| 57 | + port_byte_out(ATA_PRIMARY_LBA_LOW, (u8)(lba & 0xFF)); |
| 58 | + port_byte_out(ATA_PRIMARY_LBA_MID, (u8)((lba >> 8) & 0xFF)); |
| 59 | + port_byte_out(ATA_PRIMARY_LBA_HIGH, (u8)((lba >> 16) & 0xFF)); |
| 60 | + port_byte_out(ATA_PRIMARY_DRIVE_SEL, drive | 0x40 | ((lba >> 24) & 0x0F)); |
76 | 61 | } |
77 | 62 |
|
78 | | -void ata_identify() { |
79 | | - ata_select_drive(1); // Выбор master-диска |
| 63 | +int ata_pio_wait() { |
| 64 | + // ожидаем снятия флага BSY и установки флага DRDY |
| 65 | + int timeout = 100000; // таймаут для предотвращения зависания |
80 | 66 |
|
81 | | - port_byte_out(ATA_REG_SECCOUNT, 0); |
82 | | - port_byte_out(ATA_REG_LBA0, 0); |
83 | | - port_byte_out(ATA_REG_LBA1, 0); |
84 | | - port_byte_out(ATA_REG_LBA2, 0); |
85 | | - port_byte_out(ATA_REG_COMMAND, ATA_CMD_IDENTIFY); |
86 | | -} |
| 67 | + while (timeout-- > 0) { |
| 68 | + u8 status = port_byte_in(ATA_PRIMARY_STATUS); |
87 | 69 |
|
88 | | -int ata_wait() { |
89 | | - while (1) { |
90 | | - u8 status = port_byte_in(ATA_REG_STATUS); |
91 | | - if (!(status & 0x80)) { |
92 | | - return 1; // Проверка бита BSY (занято) |
| 70 | + // коли BSY снят и DRDY установлен - диск готов |
| 71 | + if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRDY)) { |
| 72 | + return 0; |
| 73 | + } |
| 74 | + |
| 75 | + // произошла ошибка |
| 76 | + if (status & ATA_SR_ERR) { |
| 77 | + return -1; |
93 | 78 | } |
94 | 79 | } |
| 80 | + |
| 81 | + return -2; // таймаут |
95 | 82 | } |
96 | 83 |
|
97 | | -void ata_read_buffer(u16* buffer) { |
98 | | - // Чтение всего сектора: 256 слов, 512 байт |
| 84 | +int ata_pio_identify(u8 drive, ata_disk_info_t* info) { |
| 85 | + ata_pio_select_drive(drive); |
| 86 | + |
| 87 | + u8 status = port_byte_in(ATA_PRIMARY_STATUS); |
| 88 | + if (status == 0xFF) { |
| 89 | + // нет устройства на этом канале |
| 90 | + return -3; |
| 91 | + } |
| 92 | + |
| 93 | + // ожидаем готовности диска |
| 94 | + if (ata_pio_wait() != 0) { |
| 95 | + return -1; |
| 96 | + } |
| 97 | + |
| 98 | + // отправляем IDENTIFY |
| 99 | + port_byte_out(ATA_PRIMARY_CMD, ATA_CMD_IDENTIFY); |
| 100 | + |
| 101 | + // ждем ответа |
| 102 | + if (ata_pio_wait() != 0) { |
| 103 | + return -2; |
| 104 | + } |
| 105 | + |
| 106 | + // процесс чтения данных (256 слов = 512 байт) |
| 107 | + u16 buffer[256]; |
99 | 108 | for (int i = 0; i < 256; i++) { |
100 | | - buffer[i] = port_word_in(ATA_REG_DATA); |
| 109 | + buffer[i] = port_word_in(ATA_PRIMARY_DATA); |
| 110 | + } |
| 111 | + |
| 112 | + // анализируем полученные данные |
| 113 | + info->signature = buffer[0]; |
| 114 | + info->capabilities = buffer[49]; |
| 115 | + info->command_sets = *((u32*)&buffer[83]); |
| 116 | + |
| 117 | + // получаем модель диска |
| 118 | + for (int i = 0; i < 20; i++) { |
| 119 | + info->model[i * 2] = (char)(buffer[27 + i] >> 8); |
| 120 | + info->model[i * 2 + 1] = (char)(buffer[27 + i] & 0xFF); |
| 121 | + } |
| 122 | + info->model[40] = '\0'; |
| 123 | + |
| 124 | + // размер диска в секторах |
| 125 | + if (info->command_sets & 0x40000000) { |
| 126 | + // 48-bit LBA |
| 127 | + info->size = *((u32*)&buffer[100]); |
| 128 | + } else { |
| 129 | + // 28-bit LBA |
| 130 | + info->size = *((u32*)&buffer[60]); |
101 | 131 | } |
102 | | -} |
103 | 132 |
|
104 | | -u32 ata_get_disk_size() { |
105 | | - // 1024^2 = 1048576 (1 МиБ) |
106 | | - u16 ata_buffer[256]; |
107 | | - ata_identify(); |
108 | | - ata_wait(); |
109 | | - ata_read_buffer(ata_buffer); |
110 | | - u32 total_sectors = ((u32)ata_buffer[61] << 16) | ata_buffer[60]; |
111 | | - return (u32)total_sectors * 512; // Конвертация в байты |
| 133 | + // тип диска |
| 134 | + if (buffer[0] == 0x8489 || buffer[0] == 0x8449) { |
| 135 | + info->type = ATA_DISK_PATAPI; |
| 136 | + } else { |
| 137 | + info->type = ATA_DISK_PATA; |
| 138 | + } |
| 139 | + |
| 140 | + return 0; |
112 | 141 | } |
113 | 142 |
|
114 | | -void ata_read_blocks(u32 block_num, const u8* buffer, u32 count) { |
115 | | - ata_read_sectors(block_num * SECTORS_PER_BLOCK, SECTORS_PER_BLOCK * count, buffer); |
| 143 | +int ata_pio_read_sectors(u8 drive, u32 lba, u8 num, u16* buffer) { |
| 144 | + if (num == 0) return 0; |
| 145 | + |
| 146 | + // выбор диск |
| 147 | + ata_pio_select_drive(drive); |
| 148 | + |
| 149 | + // колво количество секторов |
| 150 | + port_byte_out(ATA_PRIMARY_SECTOR_CNT, num); |
| 151 | + |
| 152 | + // сетаем LBA адрес |
| 153 | + ata_pio_set_lba(lba, drive); |
| 154 | + |
| 155 | + // сендим команду чтения |
| 156 | + port_byte_out(ATA_PRIMARY_CMD, ATA_CMD_READ_PIO); |
| 157 | + |
| 158 | + // Читаем сектора |
| 159 | + for (int sector = 0; sector < num; sector++) { |
| 160 | + // Ждем готовности данных |
| 161 | + if (ata_pio_wait() != 0) { |
| 162 | + return -1; |
| 163 | + } |
| 164 | + |
| 165 | + // Читаем сектор (256 слов = 512 байт) |
| 166 | + for (int i = 0; i < 256; i++) { |
| 167 | + buffer[sector * 256 + i] = port_word_in(ATA_PRIMARY_DATA); |
| 168 | + } |
| 169 | + |
| 170 | + // ждем между секторами |
| 171 | + for (int i = 0; i < 4; i++) port_byte_in(ATA_PRIMARY_STATUS); |
| 172 | + } |
| 173 | + |
| 174 | + return 0; |
116 | 175 | } |
117 | 176 |
|
118 | | -// неэффективные методы: запись происходит поверх буфера без учета длины |
119 | | -void ata_write_blocks(u32 block_num, const u8* buffer, u32 count) { |
120 | | - ata_write_sectors(block_num * SECTORS_PER_BLOCK, SECTORS_PER_BLOCK * count, buffer); |
| 177 | +int ata_pio_write_sectors(u8 drive, u32 lba, u8 num, u16* buffer) { |
| 178 | + if (num == 0) return 0; |
| 179 | + |
| 180 | + // диск |
| 181 | + ata_pio_select_drive(drive); |
| 182 | + |
| 183 | + // количество секторов |
| 184 | + port_byte_out(ATA_PRIMARY_SECTOR_CNT, num); |
| 185 | + |
| 186 | + // LBA адрес |
| 187 | + ata_pio_set_lba(lba, drive); |
| 188 | + |
| 189 | + // команда записи |
| 190 | + port_byte_out(ATA_PRIMARY_CMD, ATA_CMD_WRITE_PIO); |
| 191 | + |
| 192 | + // врайтим сектора |
| 193 | + for (int sector = 0; sector < num; sector++) { |
| 194 | + // ждем готовности к приему данных |
| 195 | + if (ata_pio_wait() != 0) { |
| 196 | + return -1; |
| 197 | + } |
| 198 | + |
| 199 | + // сектор (256 слов = 512 байт) |
| 200 | + for (int i = 0; i < 256; i++) { |
| 201 | + port_word_out(ATA_PRIMARY_DATA, buffer[sector * 256 + i]); |
| 202 | + } |
| 203 | + |
| 204 | + // оиждаем завершения записи |
| 205 | + if (ata_pio_wait() != 0) { |
| 206 | + return -2; |
| 207 | + } |
| 208 | + |
| 209 | + port_byte_out(ATA_PRIMARY_CMD, ATA_CMD_CACHE_FLUSH); |
| 210 | + ata_pio_wait(); // |
| 211 | + } |
| 212 | + |
| 213 | + return 0; |
121 | 214 | } |
0 commit comments