Функция-диспетчер.
По определению, данная функция принимает запросы подсистемы 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;
}
Рассмотрение драйвера жесткого диска на этом завершим, и переходим к рассмотрению подсистемы ввода-вывода.