Архітектура драйвера
Драйвер, таким чином, складається з основної нитки, обробника переривання, і, можливо,
однієї або декількох високопріоритетних ниток, що створюються обробником.
Всі ці нитки спільно (і, як правило, гарантуючи те, що
взаємовиключає) виконують більш менш складний кінцевий автомат, стани якого
відповідають етапам виконання чергового запиту до пристрою.
Як правило, перший стан автомата обробляється основною ниткою
драйвера, а подальші — обробником переривань. У фінальному стані
автомата ми повідомляємо процес, породжувачу запит, що запит відпрацьований. Залежно
від протоколу взаємодії цього процесу основною ниткою драйвера, таке
повідомлення може здійснюватися як fork-процессом, так і пробудженням
основної нитки.
Драйвер IDE/ATA для Linux
У прикладі 10.5 приведена основна функція обробки запиту і
функція об робітки переривання, використовувана при записі декількох секторів.
Обоє ці функції викликаються драйвером контроллера IDE/ATA, який
є диспетчер запитів до підключених до контроллера пристроїв.
Структура *hwgroup є блоком змінних станів
контроллера пристрою. Ця структура містить також покажчик на
поточний запит до пристрою. Інформації, що міститься в цих структурах,
досить, аби чергова функція кінцевого автомата драйвера взнала
все, необхідне їй для виконання чергового етапу запиту. В даному
випадку кінцевий автомат вельми простий і складається з багатократного виклику
функції ide_multiwriteщо копіює в контроллер
черговий блок даних. Умовою завершення автомата служать помилка контроллера
або завершення запиту. Функції ide__dma_read, ide_dma_write,
ide_read і ide_write виконувані машиною
станів при обробці інших запитів не наводяться.
Приклад 10.5. Фрагменти драйвера диска IDE/ATA ОС
Linux 2.2, переклад коментарів автора
/*
* ide_multwrite() передає приводу блок з не більш, ніж mcount
* секторів як частина багатосекторної операції запису. *
* Повертає 0 при успіху. *
* Звернете увагу, що ми можемо бути викликані з двох контекстів -
* контексту do_rw і контексту IRQ. IRQ (Interrupt Request
* запит переривання)может статися в будь-якій
* момент після того, як ми виведемо повну кількість секторів
* тому ми повинні оновлювати стан _до_ того, як ми виведемо
* останню частину даних! */
int ide_multwrite (ide_drive__t *drive, unsigned int mcount) {
ide_hwgroup_t *hwgroup=
HWGROUP(drive);
'struct request *rq = &hwgroup->wrq;
do {
char *buffer;
int nsect = rq->current_nr_sectors;
if (nsect > mcount)
nsect = mcount; mcount -= nsect; buffer = rq->buffer;
rq->sector += nsect; rq->buffer += nsect « 9; rq->nr_sectors
-= nsect; rq->current nr sectors -= nsect;
/* Чи переходимо ми до наступного bh після цього? */ if (!rq->current_nr_sectors)
{
struct buffer_head *bh = rq->bh->b_reqnext;
/* Завершитися, якщо у нас кінчилися запити V if (!bh) {
mcount = 0; } else (
rq->bh = bh;
rq->current_nr_sectors = bh->b_size » 9;
rq->buffer = bh->b_data;
/*
* Тепер ми всі набудували, аби переривання
* знову викликало нас після останньої передачі. */
idedisk_output_data(drive, buffer, nsect«7); } while (mcount);
return 0;
/*
* multwrite_intr() — обробник переривання багатосекторного запису */
static ide_startstop_t multwrite_intr (ide_drive_t *drive) {
byte stat;
ir.t i;
ide_hwgroup_t *hwgroup = HWGROUP(drive);
struct request *rq = &hwgroup->wrq;
if (OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) { if (stat
& DRQ_STAT) { /*
* Привід вимагає даних. Пам'ятаємо що rq -
* копія запиту. */
if (rq->nr_sectors) {
if (ide_multwrite(drive, drive->mult_count))
return ide_stopped; «
ide_set__handler (drive &multwrite_intr, WAIT_CMD, NULL); return
ide_started; }
} else { /*
* Якщо копіювання всіх блоків завершилося
* ми можемо завершити вихідний запит. */
if ( ! rq->nr__sectors) { /* all done? */ rq =
hwgroup->rq; for (i =
rq->nr_sectors; i > 0;){ i
- = rq->current_nr_sectors; ide_end_request(1,
hwgroup); } return ide stopped;
return ide_stopped; /* Оригінальний код робив це тут (?) */
! ьнешних
[return ide_errcr(drive, "multwrite_intr", stat);
/*
i do rw disk() передає команди READ і WRITE приводу
* використовуючи LBA якщо підтримується, або CHS якщо немає, для адресації
* секторів. Функція do_rw_disk також передає спеціальні запити.
*/
static ide_startstop__t do_rw_disk (ide_drive_t *drive, struct request
*rq, unsigned long block)
{ if (IDE_CONTROL_REG)
OUT_BYTE (drive->ctl, IDE_CONTROL_REG) ; OUT_BYTE (rq->nr_sectors,
IDE_NSECTOR_REG) ; if (drive->select.b.lba) (
OUT_BYTE (block, IDE_SECTOR_REG) ;
OUT_BYTE (block»=8, IDE_LCYL_REG) ;
OUT_BYTE (block»=8, I DE_HC YL_REG ) ;
OUT_BYTE( ( (block»8) &0x0f) I drive->select . all,
IDE_SELECT_REG); } else f
unsigned int sect, head, cyl, track;
track = block / drive->sect;
sect = block % drive->sect + 1;
ODT^BYTE (sect, IDE__SECTOR_REG) ;
head = track % drive->head;
cyl = track / drive->head;
OUT__BYTE (cyl, IDE_LCYL_REG) ;
OUT_BYTE (cyl»8, IDE_HCYL_REG) ;
OUT_BYTE (head I drive->select .all, IDE_SELECT_REG) ;
if (rq->cmd == READ) { ^#ifdef CONFIG_BLK_DEV_IDEDMA
if (drive- >using_dma && ! (HWIF (drive) ->dmaproc (ide_dma_read,
drive))
return ide_started; #endif /* CONFIG_BLK_DEV_IDEDMA */
ide_set_handler (drive, iread_intr, WAIT_CMD, NULL) ;
OUT_BYTE(drive->mult_count ? WIN_MULTREAD : WIN_READ, IDE COMMAND REG);
''—-^
return ide started;
if (rq->cmd == WRITE) (
ide_startstop_t startstop; lifdef CONFIG_BLK_DEV_IDEDMA
if (drive->using_drna && !(HWIF(drive)->dmaproc(ide dma^write
drive)))
return ide_started; lendif /* CONFIG_BLK_DEV_IDEDMA */
OUT_BYTE(drive->mult_COUnt ? WIN_MULTWRITE : WIN_WRITE
IDE_COMMAND_REG); if (ide_wait_stat(Sstartstop, drive, DATA_READY, drive->bad_wstat
WAIT^DRQ)) ( printk(KERN_ERR "%s: no DRQ after issuing %s\n",
drive->na:r.e,
drive->mult_count ? "MULTWRITE" : "WRITE"); return
startstop;
if (!drive->unmask)
__cli(); /* лише локальне ЦПУ */
if (drive->mult_count) (
ide_hwgroup_t *hwgroup = HWGROUP(drive);
/*
* Ця частина виглядає негарно, тому що ми ПОВИННІ встановити
* обробник перед виведенням першого блоку даних.
* Якщо ми виявляємо помилку (зіпсований список буферів)
* у ide_multiwrite(),
* нам необхідно видалити обробник і таймер перед поверненням.
* На щастя, це НІКОЛИ не відбувається (правильно?).
* Здається, крім випадків, коли ми отримуємо помилку... */
hwgroup->wrq = *rq; /* scratchpad */
ide_set_handler (drive &multwrite_intr, WAIT__CMD, NULL);
if (ide_multwrite(drive, drive->mult_count)) {
unsigned long flags;
spin_lock_irqsave (&io__request_lock, flags) ;
hwgroup->handler = NULL;
del_timer(&hwgroup->timer);
spin unlock_irqrestore(&io_request_lock, flags);
return ide_stopped;
Глава 10. Драйвери зовнішніх
} else {
ide_set_handler (drive &write_intr, WAIT_CMD, NULL); idedisk_output_data(drive,
rq->buffer, SECTOR_WORDS);
}
i return ide_started;
)
i'-printk (KERN_ERR "%s: bad command: %d\n", drive->name,
rq->cmd)
ide_end_request(0, HWGROUP(drive)); return ide_stopped; |