может переходить в состояние ожидания (sleep) и, соответственно, не может вызываться из контекста прерывания, или в других ситуациях, когда код не может блокироваться. Распространенной ошибкой является мнение, что функцию
request_irq
можно безопасно вызывать в случаях, когда нельзя переходить в состояние ожидания. Это происходит отчасти от того, что действительно сразу непонятно, почему функция
request_irq
должна чего-то ожидать. Дело в том. что при регистрации происходит добавление информации о линии прерывания в каталоге
/proc/irq
. Функция
proc_mkdir
используется для создания новых элементов на файловой системе
procfs
. Эта функция вызывает функцию
proc_create
для создания новых элементов файловой системы
procfs
,
которая в свою очередь вызывает функцию
kmalloc
для выделения памяти. Как будет показано в главе 11, "Управление памятью", функция
kmalloc
может переходить в состояние ожидания. Вот так вот!
Для регистрации линии прерывания и инсталляции обработчика в коде драйвера можно использовать следующий вызов.
if (request_irq(irqn, my_interrupt, SA_SHIRQ, "my_device", dev)) {
— это запрошенный номер линии запроса на прерывание, параметр
my_interrupt
— это обработчик этой линии прерывания, линия запроса на прерывание может быть совместно используемой, имя устройства —
"my_device"
,
dev
— значение параметра
dev_id
. В случае ошибки код печатает сообщение, что произошла ошибка, и возвращается из выполняющейся функции. Если функция регистрации возвращает нулевое значение, то обработчик прерывания инсталлирован успешно. С этого момента обработчик прерывания будет вызываться в ответ на приходящие прерывания. Важно произвести инициализацию оборудования и регистрацию обработчика прерывания в правильной последовательности, чтобы предотвратить возможность вызова обработчика до того момента, пока оборудование не инициализировано.
Освобождение обработчика прерывания
Для освобождения линии прерывания необходимо вызвать функцию
void free_irq(unsigned int irq, void *dev_id);
Если указанная линия не является совместно используемой, то эта функция удаляет обработчик и запрещает линию прерывания. Если линия запроса на прерывание является совместно используемой, то удаляется обработчик, соответствующий параметру
dev_id
. Линия запроса на прерывание также запрещается, когда удаляется последний обработчик. Теперь понятно, почему важно передавать уникальное значение параметра
dev_id
. При использовании совместно используемых прерываний требуется уникальный идентификатор для того, чтобы отличать друг от друга различные обработчики, связанные с одним номером прерывания, и позволить функции
free_irq
удалять правильный обработчик. В любом случае, если параметр
dev_id
не равен значению
NULL
, то он должен соответствовать тому обработчику, который удаляется.
Вызов функции
free_irq
должен производиться из контекста процесса.
Таблица 6.1. Список функций управления регистрацией прерываний
Функция
Описание
request_irq
Зарегистрировать заданный обработчик прерывания для заданной линии прерывания
free_irq
Освободить указанный обработчик прерывания. Если с линией прерывания больше не связан ни один обработчик, то запретить указанную линию прерывания
Написание обработчика прерывания
Следующее описание является типичным для обработчика прерывания.
Заметим, что оно должно соответствовать аргументу, который передается в функцию
request_irq
. Первый параметр,
irq
, — это численное значение номера прерывания, которое обслуживается обработчиком. Сейчас этот параметр практически не используется, кроме разве что при печати сообщений. Для версий ядра, меньших 2.0, не было параметра
dev_id
, поэтому параметр
irq
использовался, чтобы различать устройства, которые обслуживаются одним драйвером, и поэтому используют один и тот же обработчик прерываний (как пример можно рассмотреть компьютер с несколькими контроллерами жесткого диска одного типа).
Второй параметр,
dev_id
, — это указатель, равный значению, которое было передано в функцию
request_irq
при регистрации обработчика прерывания. Если значение этого параметра является уникальным, что необходимо для поддержки совместно используемых прерываний, то его можно использовать как идентификатор для того, чтобы отличать друг от друга различные устройства, которые потенциально могут использовать один обработчик. В связи с тем, что структура (контекст) устройства (device structure) является как уникальной, так и, возможно, полезной при использовании в обработчике, обычно в качестве параметра
dev_id
передают указатель на эту структуру.
Последний параметр,
regs
, — это указатель на структуру, содержащую значения регистров процессора и состояние процессора, которые были сохранены перед началом обслуживания прерывания. Этот параметр используется редко, в основном для отладки. Сейчас разработчики начинают склоняться к мысли, что этот параметр нужно убрать. В существующих обработчиках прерываний он используется мало, и если его убрать, то не будет больших разочарований.
Возвращаемое значение обработчиков прерываний имеет специальный тип
irqreturn_t
. Обработчик может возвращать два специальных значения:
IRQ_NONE
или
IRQ_HANDLED
. Первое значение возвращается, если обработчик прерывания обнаружил, что устройство, которое он обслуживает, не является источником прерывания. Второе значение возвращается, если обработчик вызван правильно и устройство, которое он обслуживает, является источником прерывания. Кроме этого, может быть использован макрос
IRQ_RETVAL(x)
. Если значение параметра
x
не равно нулю, то макрос возвращает значение
IRQ_HANDLED
, иначе возвращается значение, равное
IRQ_NONE
. Эти специальные значения позволяют дать ядру информацию о том, генерирует ли устройство паразитные (необрабатываемые) прерывания. Если все обработчики прерывания, которые обслуживают данную линию, возвращают значение
IRQ_NONE
, то ядро может обнаружить проблему. Заметим, что этот странный тип возвращаемого значения,
irqreturn_t
, просто соответствует типу
int
. Подстановка типа используется для того, чтобы обеспечить совместимость с более ранними версиями ядра, у которых не было подобной функции. До серии ядер 2.6 обработчик прерывания имел возвращаемое значение типа
void
. В коде новых драйверов можно применить переопределение типа
typedef irqreturn_t
в тип
void
и драйверы могут работать с ядрами серии 2.4 без дальнейшей модификации.
Обработчик прерываний может помечаться как
static
, так как он никогда не вызывается непосредственно в других файлах кода.
Роль обработчика прерывания зависит только от устройства и тех причин, по которым это устройство генерирует прерывания. Минимально, обработчик прерывания должен отправить устройству подтверждение о том, что прерывание получено. Дли более сложных устройств необходимо дополнительно отправить и принять данные, а также выполнить другую более сложную работу. Как уже упоминалось, сложная работа должна по возможности выполняться обработчиком нижней половины прерывания, которые будут рассмотрены в следующей главе.
Реентерабельность и обработчики прерываний
Обработчики прерываний в операционной системе Linux не обязаны быть реентерабельными. Когда выполняется некоторый обработчик прерывания, соответствующая линия запроса на прерывание маскируется на всех процессорах, что предотвращает возможность приема запроса на прерывание с этой пинии. Обычно все остальные прерывания в этот момент разрешены, поэтому другие прерывания могут обслуживаться, тогда как текущая линия всегда является запрещенной. Следовательно, никакой обработчик прерываний никогда не вызывается параллельно самому себе для обработки вложенных запросов на прерывание. Это позволяет значительно упростить написание обработчиков прерываний.
Совместно используемые обработчики
Совместно используемые (shared) обработчики выполняются практически так же, как и не совместно используемые. Существует, однако, три главных отличия.
• Флаг
SA_SHIRQ
должен быть установлен в параметре
flags
при вызове функции
request_irq
.
• Аргумент
dev_id
этой же функции должен быть уникальным для каждого зарегистрированного обработчика. Достаточным является передача указателя на структуру, которая описывает устройство. Обычно так и поступают, поскольку структура контекста устройства является уникальной для каждого устройства, и, кроме того, данные этой структуры потенциально могут быть полезными при выполнении обработчика. Для совместно используемого обработчика нельзя присваивать параметру