Простой драйвер контроллера
Пример 10.2. Простой драйвер контроллера гибкого диска
/* Обработчики прерываний в зависимости от состояния */ void handle_spinup_interrupt(int irq, fdd_struct *fdd) {
if (motor_speed_ok(fdd)) wake_up_interruptible((&fdd->fdd_wait_queue);
void handle_seek_interrupt(int irq, fdd_struct *fdd) {
if (verify_track(fdd)) wake_up_interruptible((&fdd->fdd_wait_queue);
void handle_dma_interrupt(int irq, fdd_struct *fdd) {
/* Увеличить fdd->bytes_xfered на количество фактически переданных символов */
if (буфер полон/пуст) wake_up_interruptible(&fdd->fdd_wait_queue);
/* Основная нить драйвера */
static int fdd_write(struct inode * inode, struct file * file, char * buf, int count)
10. Драйверы внешних устройств
/* Получить идентификатор устройства: */ = MINOR ( inode->irdev) ;
unsigned long ccpy_size;
unsigned long total_bytes_written = 0;
unsigned long bytes_written;
int state;
/* Найти блок переменных состояния устройства */ struct fdd_struct *fdd = &fdd_table [minor] ;
do { copy_size = (count <= FDD__BUFFER_SIZE ?
count : FDD_BUFFER_SIZE) ;
/* Передать данные из пользовательского контекста */ memcpy_f rornfs (fdd->fdd_buf fer, buf, copy_size) ;
while (copy_size) { if ( !motor_speed_ok (fdd) ) { fdd->handler = handle__spinup_interrupt; turn_motor_on (fdd) ;
current->timeout = jiffies + FDD_INTERRUPT_TIMEOUT; interruptible_sleep_on (&fdd->fdd_wait_queue) ; if (current->signal & -current->blocked) { if (total_bytes_written)
return total_bytes_written; else
return -EINTR; /* Ничего не было записано, системный вызов был прерван, требуется повторная попытка */
if (fdd->current_track != CALCULATE_TRACK(file)) { fdd->handler = handle_seek_interrupt; seek_head (fdd, CALCU1ATE__TRACK (f ile) ) ; current->timeout = jiffies + FDD_INTERRUPTjriMEOUT; interruptible_sleep_on(&fdd->fdd__wait_queue); if (current->signal & ~current->blocked) ( if (total bytes written)
Введение в операционныесист^
return total_bytes_written; else
return -EINTR; /* Ничего не было записано, системный вызов был прерван, требуется повторная попытка */
fdd->handler = handle_dma_interrupt;
setup_fdd_dma(fdd->fdd_buffer+bytes_xfered, copy_size) issue_write_command(fdd) ;
current->timeout = jiffies + FDD_INTEKRUPT_TIMEOUT; interruptible_sleep_on (Sfdd->fdd_wait_queue) ;
bytes_written = fdd->bytes_xfered; fdd->bytes_written = 0;
if (current->signal & ~current->blocked) { if (total_bytes_written + bytes_written)
*
return total_bytes_written + bytes_written; else
return -EINTR; /* Ничего не было записано, системный вызов был прерван, требуется повторная попытка */
total_bytes_written += bytes_written; buf += bytes__written; count -= bytes_written;
} while (count > 0) ; return total bytes written;
/* Обработчик прерывания */ static void fdd_interrupt(int irq) { struct fdd_struct *fdd = &fdd_table[fdd_irq[irq]];
f (fdd->ha:idier != NULL) { fdd->handier(irq, fdd); fdd->handIer=MULL;
} else
{
/* He наше прерывание? */
}
}
Видно, что предлагаемый драйвер осуществляет обработку ошибок и формирование последующих команд в основной нити драйвера. Велик соблазн перенести эти функции или их часть в обработчик прерываний. Такое решение позволяет сократить интервал между последовательными командами, и, таким образом, возможно, повысить производительность работы устройства.
Однако слишком большое время, проводимое в обработчике прерывания, нежелательно с точки зрения других модулей системы, так как может увеличить реальное время реакции для них. Особенно важно это для систем, которые выключают планировщик на время обслуживания прерываний. Поэтому многие ОС накладывают ограничения на время обслуживания прерываний, и часто это ограничение исключает возможность формирования команд и произведения других сложных действий в обработчике.
Обработчик, таким образом, должен выполнять лишь те операции, которые требуется выполнить немедленно. В частности, многим устройствам требуется так или иначе объяснить, что прерывание обработано, чтобы они сняли сигнал запроса прерывания. Если этого не сделать, после возврата из обработчика и обусловленного этим снижения приоритета ЦПУ, обработчик будет вызван опять.
Впрочем, нередко предлагается путь к обходу и этого ограничения: обработчикам прерываний разрешено создавать высокоприоритетные нити, которые начнут исполняться сразу же после того, как будут обслужены все прерывания. В дальнейшем мы будем называть эти высокоприоритетные нити fork-процессами (этот термин используется в VMS. Другие ОС, хотя и используют аналогичные понятия, часто не имеют внятной терминологии для их описания).