2 * Basic support for controlling the 8259 Programmable Interrupt Controllers.
4 * Initially written by Michael Brown (mcb30).
11 #define DBG(...) printf ( __VA_ARGS__ )
16 /* Install a handler for the specified IRQ. Address of previous
17 * handler will be stored in previous_handler. Enabled/disabled state
18 * of IRQ will be preserved across call, therefore if the handler does
19 * chaining, ensure that either (a) IRQ is disabled before call, or
20 * (b) previous_handler points directly to the place that the handler
21 * picks up its chain-to address.
24 int install_irq_handler ( irq_t irq
, segoff_t
*handler
,
25 uint8_t *previously_enabled
,
26 segoff_t
*previous_handler
) {
27 segoff_t
*irq_vector
= IRQ_VECTOR ( irq
);
28 *previously_enabled
= irq_enabled ( irq
);
30 if ( irq
> IRQ_MAX
) {
31 DBG ( "Invalid IRQ number %d\n" );
35 previous_handler
->segment
= irq_vector
->segment
;
36 previous_handler
->offset
= irq_vector
->offset
;
37 if ( *previously_enabled
) disable_irq ( irq
);
38 DBG ( "Installing handler at %hx:%hx for IRQ %d, leaving %s\n",
39 handler
->segment
, handler
->offset
, irq
,
40 ( *previously_enabled
? "enabled" : "disabled" ) );
41 DBG ( "...(previous handler at %hx:%hx)\n",
42 previous_handler
->segment
, previous_handler
->offset
);
43 irq_vector
->segment
= handler
->segment
;
44 irq_vector
->offset
= handler
->offset
;
45 if ( *previously_enabled
) enable_irq ( irq
);
49 /* Remove handler for the specified IRQ. Routine checks that another
50 * handler has not been installed that chains to handler before
51 * uninstalling handler. Enabled/disabled state of the IRQ will be
52 * restored to that specified by previously_enabled.
55 int remove_irq_handler ( irq_t irq
, segoff_t
*handler
,
56 uint8_t *previously_enabled
,
57 segoff_t
*previous_handler
) {
58 segoff_t
*irq_vector
= IRQ_VECTOR ( irq
);
60 if ( irq
> IRQ_MAX
) {
61 DBG ( "Invalid IRQ number %d\n" );
64 if ( ( irq_vector
->segment
!= handler
->segment
) ||
65 ( irq_vector
->offset
!= handler
->offset
) ) {
66 DBG ( "Cannot remove handler for IRQ %d\n" );
70 DBG ( "Removing handler for IRQ %d\n", irq
);
72 irq_vector
->segment
= previous_handler
->segment
;
73 irq_vector
->offset
= previous_handler
->offset
;
74 if ( *previously_enabled
) enable_irq ( irq
);
78 /* Send specific EOI(s).
81 void send_specific_eoi ( irq_t irq
) {
82 DBG ( "Sending specific EOI for IRQ %d\n", irq
);
83 outb ( ICR_EOI_SPECIFIC
| ICR_VALUE(irq
), ICR_REG(irq
) );
84 if ( irq
>= IRQ_PIC_CUTOFF
) {
85 outb ( ICR_EOI_SPECIFIC
| ICR_VALUE(CHAINED_IRQ
),
86 ICR_REG(CHAINED_IRQ
) );
90 /* Dump current 8259 status: enabled IRQs and handler addresses.
94 void dump_irq_status (void) {
97 for ( irq
= 0; irq
< 16; irq
++ ) {
98 if ( irq_enabled ( irq
) ) {
99 printf ( "IRQ%d enabled, ISR at %hx:%hx\n", irq
,
100 IRQ_VECTOR(irq
)->segment
,
101 IRQ_VECTOR(irq
)->offset
);
107 /********************************************************************
108 * UNDI interrupt handling
109 * This essentially follows the defintion of the trivial interrupt
110 * handler routines. The text is assumed to locate in base memory.
112 void (*undi_irq_handler
)P((void)) = _undi_irq_handler
;
113 uint16_t volatile *undi_irq_trigger_count
= &_undi_irq_trigger_count
;
114 segoff_t
*undi_irq_chain_to
= &_undi_irq_chain_to
;
115 uint8_t *undi_irq_chain
= &_undi_irq_chain
;
116 irq_t undi_irq_installed_on
= IRQ_NONE
;
118 /* UNDI entry point and irq, used by interrupt handler
120 segoff_t
*pxenv_undi_entrypointsp
= &_pxenv_undi_entrypointsp
;
121 uint8_t *pxenv_undi_irq
= &_pxenv_undi_irq
;
123 /* Previous trigger count for undi IRQ handler */
124 static uint16_t undi_irq_previous_trigger_count
= 0;
126 /* Install the undi IRQ handler. Don't test as UNDI has not be opened.
129 int install_undi_irq_handler ( irq_t irq
, segoff_t entrypointsp
) {
130 segoff_t undi_irq_handler_segoff
= SEGOFF(undi_irq_handler
);
132 if ( undi_irq_installed_on
!= IRQ_NONE
) {
133 DBG ( "Can install undi IRQ handler only once\n" );
136 if ( SEGMENT(undi_irq_handler
) > 0xffff ) {
137 DBG ( "Trivial IRQ handler not in base memory\n" );
141 DBG ( "Installing undi IRQ handler on IRQ %d\n", irq
);
142 *pxenv_undi_entrypointsp
= entrypointsp
;
143 *pxenv_undi_irq
= irq
;
144 if ( ! install_irq_handler ( irq
, &undi_irq_handler_segoff
,
146 undi_irq_chain_to
) )
148 undi_irq_installed_on
= irq
;
150 DBG ( "Disabling undi IRQ %d\n", irq
);
152 *undi_irq_trigger_count
= 0;
153 undi_irq_previous_trigger_count
= 0;
154 DBG ( "UNDI IRQ handler installed successfully\n" );
158 /* Remove the undi IRQ handler.
161 int remove_undi_irq_handler ( irq_t irq
) {
162 segoff_t undi_irq_handler_segoff
= SEGOFF(undi_irq_handler
);
164 if ( undi_irq_installed_on
== IRQ_NONE
) return 1;
165 if ( irq
!= undi_irq_installed_on
) {
166 DBG ( "Cannot uninstall undi IRQ handler from IRQ %d; "
167 "is installed on IRQ %d\n", irq
,
168 undi_irq_installed_on
);
172 if ( ! remove_irq_handler ( irq
, &undi_irq_handler_segoff
,
174 undi_irq_chain_to
) )
177 if ( undi_irq_triggered ( undi_irq_installed_on
) ) {
178 DBG ( "Sending EOI for unwanted undi IRQ\n" );
179 send_specific_eoi ( undi_irq_installed_on
);
182 undi_irq_installed_on
= IRQ_NONE
;
186 /* Safe method to detect whether or not undi IRQ has been
187 * triggered. Using this call avoids potential race conditions. This
188 * call will return success only once per trigger.
191 int undi_irq_triggered ( irq_t irq
) {
192 uint16_t undi_irq_this_trigger_count
= *undi_irq_trigger_count
;
193 int triggered
= ( undi_irq_this_trigger_count
-
194 undi_irq_previous_trigger_count
);
196 /* irq is not used at present, but we have it in the API for
197 * future-proofing; in case we want the facility to have
198 * multiple undi IRQ handlers installed simultaneously.
200 * Avoid compiler warning about unused variable.
202 if ( irq
== IRQ_NONE
) {};
203 undi_irq_previous_trigger_count
= undi_irq_this_trigger_count
;
204 return triggered
? 1 : 0;