Выбрать главу

Ниже приведен фрагмент программы, в котором реализовано подключение ISR к вектору аппаратного прерывания, идентифицируемого константой HW_SERIAL_IRQ:

#include <sys/neutrino.h>

int InterruptID;

const struct sigevent* intHandler(void *arg, int id) {

 ...

}

int main (int argc, char **argv) {

 interruptID =

  InterruptAttach(HW_SERIAL_IRQ, intHandler, sevent,

   sizeof(event), 0);

 if (interruptID == -1 {

  fprintf(stderr, "%s: ошибка подключения к IRQ %d\n",

   progname, W_SERIAL_IRQ);

  perror(NULL);

  exit(EXIT_FAILURE);

 }

 ...

 return (EXIT_SUCCESS);

}

Теперь, если по данному вектору произойдет прерывание, наш обработчик будет подвергнут диспетчеризации. При вызове функции InterruptAttach() ядро демаскирует указанный источник прерываний на уровне контроллера (если это прерывание еще не демаскировано, что имело бы место в случае многочисленных обработчиков одного и то же прерывания).

Отключение обработчика прерывания

Когда вы закончили с обработчиком прерывания, вы можете пожелать уничтожить связь между ним и вектором:

int InterruptDetach(int id);

Я сказал «можете», потому что обрабатывающие прерывания потоки, как правило, используются в серверах, а серверы обычно не завершаются. Это часто ведет к предрассудку, что хорошо организованному серверу никогда не понадобится самостоятельно вызывать InterruptDetach(). К тому же, при смерти потока или процесса ОС автоматически отключит все связанные с ним обработчики прерываний. Таким образом, если программа просто дойдет до конца main(), вызовет exit() или завершится по SIGSEGV, все ее ISR будут автоматически отключены от соответствующих векторов прерываний. (Впрочем, вы, вероятно, пожелаете сделать это несколько изящнее, запретив соответствующему устройству генерацию прерываний. Если же прерывание разделяемое, и его используют другие устройства, то здесь двух вариантов быть не может вообще — вы просто обязаны «убрать за собой», иначе у вас либо больше не будет прерываний (в режиме чувствительности по фронту), либо пойдет постоянный поток запросов на прерывание (в режиме чувствительности по уровню).

Продолжая вышеприведенный пример, если бы мы захотели отключиться от прерывания, то мы использовали бы следующий код:

void terminateInterrupts(void) {

 InterruptDetach(interruptID);

}

Если это последний ISR, связанный с данным вектором прерывания, то ядро автоматически произведет маскирование источника прерывания на уровне контроллера, чтобы таких прерываний больше не возникало.

Параметр flags

Последний параметр, flags, управляет различными дополнительными опциями:

_NTO_INTR_FLAGS_END

Указывает, что данный обработчик должен сработать после всех других обработчиков данного прерывания (если они есть).

_NTO_INTR_FLAGS_PROCESS

Указывает на то, что данный обработчик связан с процессом, а не с потоком. Что из этого вытекает, так это условие автоматического отключения обработчика. Если вы определяете этот флаг, обработчик будет автоматически отключен от источника прерывания при завершении процесса. Если этот флаг не определен, обработчик прерывания будет отключен от источника, когда завершится поток, подключивший его.

_NTO_INTR_FLAGS_TRK_MSK

Указывает, что ядро должно отследить, сколько раз данное прерывание было маскировано. Это приводит к несколько большей загрузке ядра, но это необходимо для корректного демаскирования источника прерываний при завершении потока или процесса.

Обработчик прерывания

Давайте рассмотрим собственно обработчик прерывания. В первом примере применим InterruptAttach(), а затем рассмотрим аналогичный случай, только с применением функции InterruptAttachEvent().

Применение функции InterruptAttach()

В продолжение примера приведем функцию intHandler() — наш обработчик прерывания. Она отвечает за микросхему последовательного порта 8250 (допустим, что она генерирует прерывание HW_SERIAL_IRQ).

/*

 * int1.c

*/

#include <stdio.h>

#include <sys/neutrino.h>

#define REG_RX   0

#define REG_II   2

#define REG_LS   5

#define REG_MS   6

#define IIR_MASK 0x07

#define IIR_MSR  0x00

#define IIR_THE  0x02

#define IIR_RX   0x04

#define IIR_LSR  0x06

#define IIR_MASK 0x07

volatile int serial_msr; // Сохраненное значение

                         // регистра состояния модема

volatile int serial_rx; // Сохраненное значение

                        // регистра приема

volatile int serial_lsr; // Сохраненное значение

                         // регистра состояния линии

static int base_reg = 0x2f8;

const struct sigevent* intHandler(void *arg, int id) {

 int iir;

 struct sigevent *event = (struct sigevent*)arg;

 /*

  * Определить (и очистить) источник прерывания

  * чтением регистра идентификации прерывания

 */

 iir = in8(base_reg + REG_II) & IIR_MASK;

 /* Нет прерывания? */

 if (iir & 1) {

  /* Значит, нет и события */

  return (NULL);

 }

 /*

  * Выяснить, что вызвало прерывание, и определить, надо ли

  * потоку что-нибудь с этим делать.

  * (Константы основаны на строении регистра

  * идентификации прерывания 8250.)

 */

 switch (iir) {

 case IIR_MSR:

  serial_msr = in8(base_reg + REG_MS);

  /* Разбудить поток */

  return (event);

  break;

 case IIR_THE:

  /* Ничего не делать */

  break;

 case IIR_RX:

  /* Считать символ */

  serial_rx = in8(base_reg + REG_RX);

  break;

 case IIR_LSR:

  /* Сохранить регистр состояния линии */