В случае успеха функция request_irq()
возвращает нуль. Возврат ненулевого значения указывает на то, что произошла ошибка и указанный обработчик прерывания не был зарегистрирован. Наиболее часто встречающийся код ошибки — это значение -EBUSY
, что указывает на то, что данная линия запроса на прерывание уже занята (и или при текущем вызове, или при первом вызове не был указан флаг SA_SHIRQ
).
Следует обратить внимание, что функция request_irq()
может переходить в состояние ожидания (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)) {
printk(KERN_ERR "my_device: cannot register IRQ %d\n", irqn);
return -EIO;
}
В этом примере параметр irqn
— это запрошенный номер линии запроса на прерывание, параметр 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() |
Освободить указанный обработчик прерывания. Если с линией прерывания больше не связан ни один обработчик, то запретить указанную линию прерывания |
Написание обработчика прерывания
Следующее описание является типичным для обработчика прерывания.
static irqreturn_t intr_handler(int irq, void *dev_id,
struct pt_regs *regs);
Заметим, что оно должно соответствовать аргументу, который передается в функцию request_irq()
. Первый параметр, irq
, — это численное значение номера прерывания, которое обслуживается обработчиком. Сейчас этот параметр практически не используется, кроме разве что при печати сообщений. Для версий ядра, меньших 2.0, не было параметра dev_id
, поэтому параметр irq
использовался, чтобы различать устройства, которые обслуживаются одним драйвером, и поэтому используют один и тот же обработчик прерываний (как пример можно рассмотреть компьютер с несколькими контроллерами жесткого диска одного типа).
Читать дальше