Файловая система EXT2

Функция-диспетчер.



По определению, данная функция принимает запросы подсистемы I/O на чтения/запись данных.
Для выполнения команды функция-диспетчер формирует запрос к устройству, который представляет собой структуру следующего вида:


struct ata_request {
u8 dev; /* номер канала (устройства) : 0,1,2,3 */
u16 *buff; /* указатель на буфер с данными для чтения/записи (r/w) на устройство */
u32 nlba; /* номер логического сектора для r/w */
u32 nsect; /* число секторов для r/w */
u8 err; /* индикатор ошибки выполнения команды*/
u8 lock; /* флаг блокировки буфера данных на время выполнения поступившей команды */
u8 complite; /* флаг завершения операции (команды) */
} dev_r;


#define CURRENT dev_r.dev


Функция выглядит следующим образом:
int hd_request(u32 minor, u8 cmd, u32 start_sect, u32 s_count, u8 *buff)
{


Параметры функции-диспетчера:
- u32 minor - младший номер устройства;
- u8 cmd - команда, подлежащая выполнению. Их у нас целых три - READ, WRITE, STAT (см. раздел "Структуры и переменные");
- u32 start_sect - адрес стартового сектора для чтения/записи данных. Адрес задается в формате LBA и по сути является порядковым номером сектора на устройстве;
- u32 s_count - число секторов для чтения/записи;
- u8 *buff - указатель на буфер, куда необходимо поместить прочитанные с устройства данные, если поступила команда READ. Если поступила команда WRITE, по этому адресу будут находится данные, которые надо записать на устройство.


Извлекаем из младшего номера номер устройства и номер раздела на устройстве:


u16 part = GET_PART(minor);
u8 command;
CURRENT = GET_DEV(minor);


Проверяем, присутствует ли в системе устройство, с которого мы пытаемся прочесть данные (или записать):


if(DEV_STAT(CURRENT) != ATA) return -1;


Работать можно только с основными разделами, или со всем устройством в RAW-режиме, поэтому проверяем номер раздела. Он не должен быть больше четырех:


if(part > 4) return -1;


Проверяем, не заблокирован ли буфер для данных в структуре запроса struct ata_request. Если нет - блокируем его на время выполнения запроса, выставив флаг lock:



while(dev_r.lock) continue;
dev_r.lock = 1;

Заполняем поля структуры запроса значениями:

dev_r.nlba = start_sect; /* стартовый сектор */
dev_r.nsect = 1; /* число cекторов для чтения/записи */
dev_r.buff = (unsigned short *)buff;

Определяем, какая команда поступила:

switch(cmd) {

case STAT:
return stat_hd(part);
break;

case READ:
command = 0x20;
handler = &intr_read;
break;

case WRITE:
command = 0x30;
handler = &intr_write;
break;

default:
printf("Unknown command\n");
dev_r.lock = 0;
return -1;
break;
}

Если приходит команда STAT, драйвер просто вернет подсистеме I/O информацию о характеристиках раздела устройства, такую как размер блока и число блоков на разделе устройстве, вызвав функцию stat_hd:

int stat_hd(u16 part)
{
device_info_t dev_i;

dev_i.block_size = BLK_SIZE;
dev_i.blocks_num = DEV_ID(CURRENT).lba_capacity/(BLK_SIZE/BYTE_PER_SECT);

if(part != 0)
dev_i.blocks_num = DEV_PT(CURRENT,(part-1)).sect_total/(BLK_SIZE/BYTE_PER_SECT);

memcpy(dev_r.buff, (u16 *)&dev_i, sizeof(device_info_t));

dev_r.lock = 0;
return 0;
}

Если поступила команда чтения/записи - выполняем её:

do_command(s_count, command);

По окончании выполнения команды сбрасываем флаг "Операция завершена" и разблокируем буфер данных:
dev_r.complite = 0;
dev_r.lock = 0;
return 0;
}

Выполнение поступившей команды осуществляется путем вызова функции do_command:
int do_command(u32 count, u8 com)
{
for(;;) {

Посылаем устройству команду com и вызываем соответствующую функцию для чтения/записи, на которую настрооен указатель handler():

send_command(com);
handler();

Ожидаем установки флага завершения операции и проверяем, нет ли ошибки:

while(!(dev_r.complite)) continue;
if(dev_r.err) return -1;

Уменьшаем счетчик секторов. Если он равен нулю - завершаем выполнение команды и выходим из цикла. Если нет - считываем следующий сектор и смещаем указатель в буфере на 512 байт (размер сектора):

count--;
if(!count) break;
dev_r.nlba++;
dev_r.buff += 0x100;
}




return 0;
}

Команду чтения/ записи данных устройству посылает функция send_command:

void send_command(u8 cmd)
{
hd_busy(CURRENT);

Выбираем устройства (ведущее/ведомое).
Ведущее устройство:

if((CURRENT == 0) (CURRENT == 2))
OUT_P_B(0xE0|((dev_r.nlba & 0x0F000000) >> 24),ATA_CURRENT(CURRENT));

Ведомое устройство:

if((CURRENT == 1) (CURRENT == 3))
OUT_P_B(0xF0|((dev_r.nlba & 0x0F000000) >> 24),ATA_CURRENT(CURRENT));

hd_ready(CURRENT);

OUT_P_B(dev_r.nsect,ATA_NSECTOR(CURRENT));
OUT_P_B((dev_r.nlba & 0x000000FF),ATA_SECTOR(CURRENT));
OUT_P_B(((dev_r.nlba & 0x0000FF00) >> 8),ATA_LCYL(CURRENT));
OUT_P_B(((dev_r.nlba & 0x00FF0000) >> 16),ATA_HCYL(CURRENT));
OUT_P_B(cmd,ATA_STATUS(CURRENT));

return;
}

Чтение данных с устройства выполняет функция intr_read:
void intr_read()
{
int i = 0;
dev_r.complite = 0;

hd_busy(CURRENT);
if(check_error(CURRENT)) {
dev_r.err = 1;
return;
}

while(!(hd_data_request(CURRENT))) continue;

for(;i < 0x100; i++)
IN_P_W(dev_r.buff[i],ATA_DATA(CURRENT));

dev_r.complite = 1;
return;
}

Запись данных на устройство выполняет функция intr_write:
void intr_write()
{
int i = 0;
dev_r.complite = 0;

hd_busy(CURRENT);
if(check_error(CURRENT)) {
dev_r.err = 1;
return;
}

while(!(hd_data_request(CURRENT))) continue;

for(;i < 0x100; i++)
OUT_P_W(dev_r.buff[i],ATA_DATA(CURRENT));

dev_r.complite = 1;
return;
}

Рассмотрение драйвера жесткого диска на этом завершим, и переходим к рассмотрению подсистемы ввода-вывода.


Содержание раздела