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

       

Драйвер ATA-устройства (жесткого диска)



Ресурсы, выделенные каналам, разместим в массиве:


u16 channels[4] = { CH0, CH1, CH2, CH3 };


Адресация к регистрам ATA-контроллера выполняется при помощи следующих макросов:


#define ATA_STATUS(x) (channels[x] + 7)
#define ATA_CURRENT(x) (channels[x] + 6)
#define ATA_HCYL(x) (channels[x] + 5)
#define ATA_LCYL(x) (channels[x] + 4)
#define ATA_SECTOR(x) (channels[x] + 3)
#define ATA_NSECTOR(x) (channels[x] + 2)
#define ATA_ERROR(x) (channels[x] + 1)
#define ATA_DATA(x) (channels[x])


где x - номер канала.


Для работы с портами ввода/вывода определим несколько макросов.


Макросы OUT_P_B и OUT_P_W выполняют запись байта/слова в порт:
#define OUT_P_B(val,port) asm("outb %%al, %%dx"::"a"(val),"d"(port))
#define OUT_P_W(val,port) asm("outw %%ax, %%dx"::"a"(val),"d"(port))


Макросы IN_P_B и IN_P_W выполняют чтение байта/слова из порта:
#define IN_P_B(val,port) asm("inb %%dx, %%al":"=a"(val):"d"(port))
#define IN_P_W(val,port) asm("inw %%dx, %%ax":"=a"(val):"d"(port))


void (*handler)(void);
Назначение этого указателя будет рассмотрено далее.


Следующие функции были подробно рассмотрены в [1]:


- проверка занятости устройства:
int hd_busy(u8 dev)
{
int t = 0;
unsigned char status;


do {
t++;
IN_P_B(status,ATA_STATUS(dev));
if(t & TIMEOUT) break;
} while (status & BSY);
return t;
}


- проверка готовности устройства к восприятию команд:
int hd_ready(u8 dev)
{
int t = 0;
unsigned char status;


do {
t++;
IN_P_B(status,ATA_STATUS(dev));
if(t & TIMEOUT) break;
} while (!(status & DRDY));


return t;
}


- проверка готовности устройства к обмену данными:
int hd_data_request(u8 dev)
{
unsigned char status;


IN_P_B(status, ATA_STATUS(dev));
if(status & DRQ) return 1;
return 0;
}


- фиксация ошибки выполнения команды:
int check_error(u8 dev)
{
unsigned char a;


IN_P_B(a, ATA_STATUS(dev));
if (a & ERR) return 1;
return 0;
}


В соответствии с алгоритмом, первая команда, посылаемая драйверу - это команда инициализации. Команда выполняется путем вызова функции инициализации, которая находится в теле драйвера:



/* Инициализация драйвера АТА */
int hd_init()
{
int i = 0, major = 0;

get_ata_info(); // опросить каналы на предмет наличия ATA-устройств
show_ata_info(); // отобразить информацию о найденых устройствах
get_pt_info(); // получить таблицу разделов с каждого устройства
major = reg_blkdev(MAJOR_ATA,"ATA",&hd_request); // зарегистрировать драйвер устройства

return major;
}

При выполнении инициализации драйвер опрашивает все каналы на предмет наличия ATA-устройств, отображает информацию о найденых устройствах, получает от каждого найденого устройства таблицу разделов и регистрируется в системе.

Опрос каналов выполняет функция get_ata_info(). Вот как она выглядит:
void get_ata_info()
{

Для поиска устройств организуем цикл из четырех итераций:

int dev = 0;
for(; dev < 4 ; dev++) {

Ожидаем освобождение устройства. Если таймаут исчерпан - на данном канале устройство отсутствует:

if(hd_busy(dev) & TIMEOUT) {
DEV_STAT(dev) = 0;
continue;
}

Если устройство на канале присутствует, то пытаемся получить от него информацию идентификации. Информация о наличии/отсутствии устройства на канале будет сохранена в поле status структуры struct dev_status_struct (см. раздел "Структуры и переменные"):

DEV_STAT(dev) = ATA;
if(get_ata_identity(dev) < 0) DEV_STAT(dev) = 0;
}
}

Функция получениe информации об устройстве имеет следующий вид:
int get_ata_identity(u8 dev)
{
int i = 0;

Ждем готовность устройства
if(hd_busy(dev) & TIMEOUT) return -1;

Устройство свободно, поэтому устанавливаем биты выбора устройства (ведущее/ведомое), режима работы (LBA):

if((dev == 0) (dev == 2))
OUT_P_B(0xE0,ATA_CURRENT(dev));
if((dev == 1) (dev == 3))
OUT_P_B(0xF0,ATA_CURRENT(dev));

и ожидаем готовность устройства к приёму команд:

if(hd_ready(dev) == TIMEOUT) return -1;

Дождавшись, отправляем ему (устройству) команду идентификации 0xEC:

OUT_P_B(0xEC,ATA_STATUS(dev));

Устройства ATAPI команду 0xEC (идентификация) отвергают. Если будет установлен бит ERR - на канале ATAPI:




if(check_error(dev)) return -1;

Ожидаем готовность устройства поделиться данными:

for(; i < TIMEOUT; i++) if(hd_data_request(dev)) break;
if(i == TIMEOUT) return -1;

Считываем информацию о жестком диске и сохраняем ее в поле hd структуры struct dev_status_struct (см. раздел "Структуры и переменные"):

asm(
" cld \n\t"
"1: inw %%dx, %%ax \n\t"
" stosw \n\t"
" decw %%cx \n\t"
" jnz 1b \n\t"
::"D"(&DEV_ID(dev)), "d"(ATA_DATA(dev)),"c"(0x100));

return 0;
}

Вывод информации об устройствах, подключенных к системе, выполняет функция show_ata_info():

void show_ata_info()
{
int i = 0;
for(; i < 4; i++) {
printf("ATA%d - ",i);
if(!DEV_STAT(i)) printf("none\n");
if(DEV_STAT(i) == ATA) {
printf("exists\n");
printf("\tType - ATA Disk drive\n");
printf("\tModel - %s\n",DEV_ID(i).model);
printf("\tLBA capacipty - %d\n",DEV_ID(i).lba_capacity);
}
}
}

Получаем от каждого устройства таблицу разделов:

void get_pt_info()
{
u8 dev;
u32 minor = 0;
int i = 0;
unsigned char buff[0x200];

Опрашиваем все ATA устройства и получаем от каждого таблицу разделов:

for(; i < 4; i++) {
dev = GET_DEV(minor);
if(DEV_STAT(CURRENT) != ATA) continue;
if(hd_request(minor,READ,0,1,buff) < 0) break;
memcpy(dev_status[dev].pt,(struct pt_struct *)(buff+0x1BE),PT_SIZE*4);
minor += 0x100;
}
return;
}

Считывание таблицы разделов с устройства выполняет функция-диспетчер hd_request, одним из параметров которой является младший номер устройства. Опрос устройств начинается с нулевого канала, при этом поле номера раздела равно нулю, что означает работу с устройством в RAW-режиме.

После того, как информация о таблице разделов собрана с каждого устройства, драйвер регистрируется в системе, вызвав функцию reg_blkdev. В параметрах функции указывается старший номер устройства, соответствующий позиции в таблице блочных устройств, имя драйвера и адрес функции-диспетчера. Функция регистрации входит в состав подсистемы ввода-вывода, которую мы рассмотрим ниже.


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