Скелет драйвера последовательного
Пример 10.1. Скелет драйвера последовательного устройства для ОС Linux
f* Основная нить драйвера */
static int foo_write(struct inode * inode, struct file * file, char * buf, int count)
Щ
/* Получить идентификатор устройства: */
к/с в операционные систв
unsigned int minor = MINOR(inode->i_rdev); unsigned long copy size; unsigned long total_bytes_written = 0; unsigned long bytes__written;
/* Найти блок переменных состояния устройства */ struct foo_struct *foo = &foo_table[minor];
do { copy_size = (count <= FOO_BUFFER_SIZE ?
count : FOOJ3UFFER_'SIZE) ;
/* Передать данные из пользовательского контекста */ memcpy_fromfs(foo->foo_buffer, buf, copy_size);
while (copy_size) {
/* Здесь мы должны инициализировать прерывания*/
if (some_error_has_occured) { /* Здесь мы должны обработать ошибку */
current->timeout = jiffies + FOO_INTERRUPT_TIMEOUT;
/* Установить таймаут на случай, если прерывание будет пропущено */
interruptible_sleep_on (&f oo->foo_wait_queue) ;
if (some_error_has_occured) { /* Здесь мы должны обработать ошибку */
bytes_written = foo->bytes_xfered; foo->bytes_written = 0;
if (current->signal H ~current->blocked) { if (total_bytes_written + bytes__written)
return total_bytes_written + bytes_written; else
return -EINTR; /* Ничего не было записано, системный вызов был прерван, требуется повторная попытка */
O- Драйверы внешних устройств
total_byr.c5_v;r:.i.U-.r. т= bytes_written; buf += bytes_written; count -= bytes_written;
) while (count > 0) ; return total_bytes_written;
/* Обработчик прерывания */ static void foo__interrupt (int irq)
{ struct foo_struct *foo = &foo__table [foo_irq[irq] ] ;
/* Здесь необходимо выполнить все действия, которые должны быть выполнены по прерыванию.
Флаг в foo__table указывает, осуществляется операция чтения или записи. */
/* Увеличить foo->bytes_xfered на количество фактически переданных символов * /
if (буфер полон/пуст) wake_up_interruptible (&foo->foo_wait_queue) ;
}
Примечание
Примечание
Обратите внимание, что кроме инициализации устройства драйвер перед засыпанием еще устанавливает "будильник" — таймер, который должен разбудить процесс через заданный интервал времени. Это необходимо на случай, если произойдет аппаратная ошибка и устройство не сгенерирует прерывания. Если бы такой будильник не устанавливался, драйвер в случае ошибки мог бы заснуть навсегда, заблокировав при этом пользовательский процесс. В нашем случае таймер также используется, чтобы разбудить процесс, если прерывание произойдет до вызова interruptible_sleep_on основной нитью.
Многие устройства, однако, требуют для исполнения некоторых, даже относительно простых, операций, несколько команд и несколько прерываний. Так, при записи данных посредством контроллера гибких дисков, драйвер должен:
- включить мотор дисковода;
- дождаться, пока диск разгонится до рабочей скорости (большинство контроллеров генерируют по этому случаю прерывание);
- дать устройству команду на перемещение считывающей головки;
- дождаться прерывания по концу операции перемещения;
- запрограммировать ПДП и инициировать операцию записи;
- дождаться прерывания, сигнализирующего о конце операции.
Лишь после этого можно будет передать данные программе. Наивная реализация таких многошаговых операций могла бы выглядеть так (за основу по-прежнему взят код из [HOWTO khg], обработка ошибок опущена), как показано в примере 10.2.