Разработка ядра Linux | страница 99



>if (request_irq(RTC_IRQ, rtc_interrupt, SA_INTERRUPT, "rtc", NULL) {

> printk(KERN_ERR "rtc: cannot register IRQ %d\n" , rtc_irq);

> return -EIO;

>}

Из данного примера видно, что номер линии прерывания — это константа >RTC_IRQ, значение которой определяется отдельно для каждой аппаратной платформы с помощью препроцессора. Например, для персональных компьютеров это значение всегда соответствует >IRQ 8. Второй параметр— это обработчик прерывания, >rtc_interrupt, при выполнении которого запрещены все прерывания в связи с указанием флага >SA_INTERRUPT. Из четвертого параметра можно заключить, что драйвер будет иметь имя >"rtc". Так как наше устройство не может использовать линию прерывания совместно с другими устройствами и обработчик прерывания не используется для каких-либо других целей, в качестве параметра >dev_id передается значение >NULL.

И наконец, собственно сам обработчик прерывания.

>/*

>* Очень маленький обработчик прерывания. Он выполняется с

>* установленным флагом SA_INTERRUPT, однако существует

>* возможность конфликта с выполнением функции set_rtc_mmss()

>* (обработчик прерывания rtc и обработчик прерывания системного

>* таймера могут выполняться одновременно на двух разных

>* процессорах). Следовательно, необходимо сериализировать доступ

>* к микросхеме с помощью спин-блокировки rtc_lock, что должно

>* быть сделано для всех аппаратных платформ в коде работы с

>* таймером. (Тело функции set_rtc_mmss() ищите в файлах

>* ./arch/XXXX/kernel/time.c)

>*/

>static irqreturn_t rtc_interrupt(int irq, void *dev_id,

> struct pt_regs *regs) {

> /*

> * Прерывание может оказаться прерыванием таймера, прерыванием

> * завершения обновления или периодическим прерыванием.

> * Состояние (причина) прерывания хранится в самом

> * младшем байте, а общее количество прерывание — в оставшейся

> * части переменной rtc_irq_data

> */

> spin_lock(&rtc_lock);


> rtc_irq_data += 0x100;

> rtc_irq_data &= ~0xff;

> rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) & 0xF0);


> if (rtc_status & RTC_TIMER_ON)

>  mod_timer(&rtc_irq_timer, jiffies + HZ/rtc_freq + 2*HZ/100);

> spin_unlock(&rtc_lock);


> /*

> * Теперь выполним остальные действия

> */

> spin_lock(&rtc_task_lock);

> if (rtc_callback)

>  rtc_callback->func(rtc_callback->private_data);

> spin_unlock(&rtc_task_lock);

> wake_up_interruptible(&rtc_wait);


> kill_fasync(&rtc_async_queue, SIGIO, POLL_IN);


> return IRQ_HANDLED;

>}

Эта функция вызывается всякий раз, когда система получает прерывание от устройства RTC. Прежде всего, следует обратить внимание на вызовы функций работы со спин-блокировками: первая группа вызовов гарантирует, что к переменной