Jump to content

Writing Device Drivers - Interrupts: Difference between revisions

From EDM2
Created page with "by Steve Mastrianni IBM Developer Connection News Volume 3 ==Writing Device Drivers - Interrupts== I get a lot of calls asking "What is the interrupt system and how do I p..."
 
Ak120 (talk | contribs)
mNo edit summary
 
(5 intermediate revisions by 2 users not shown)
Line 1: Line 1:
by [[Steve Mastrianni]]
''by [[Steve Mastrianni]]''


IBM Developer Connection News Volume 3
IBM Developer Connection News Volume 3


==Writing Device Drivers - Interrupts==
==Writing Device Drivers - Interrupts==
I get a lot of calls asking "What is the interrupt system  
I get a lot of calls asking ''"What is the interrupt system and how do I program for it?"'' To answer those questions, this article provides a beginner's view of the interrupt system.  
and how do I program for it?"  To answer those questions,  
this article provides a beginner's view of the interrupt  
system.  


In the traditional Intel-based PC, the interrupt system  
In the traditional Intel-based PC, the interrupt system simply is a collection of electronics that let external events interrupt the program currently being executed. One of the electrical components, the Programmable Interrupt Controller (PIC) is notified of the external event, and in turn notifies the CPU that an interrupt has been received. The CPU saves the address of the code it was executing, then calls a special address assigned to that interrupt level, or IRQ, to perform the interrupt processing. When the interrupt processing is complete, the CPU resumes executing at the next instruction in the thread that was executing at the time of the interrupt. Of course, it's more complicated than that, but those are the basics.  
simply is a collection of electronics that let external  
events interrupt the program currently being executed.
One of the electrical components, the Programmable  
Interrupt Controller (PIC) is notified of the external  
event, and in turn notifies the CPU that an interrupt  
has been received. The CPU saves the address of the  
code it was executing, then calls a special address  
assigned to that interrupt level, or IRQ, to perform the  
interrupt processing. When the interrupt processing is  
complete, the CPU resumes executing at the next  
instruction in the thread that was executing at the time  
of the interrupt. Of course, it's more complicated than  
that, but those are the basics.  


Today, most PCs have 15 different interrupt levels,  
Today, most PCs have 15 different interrupt levels, provided by two 8-channel PICs. One IRQ, IRQ 2, is unavailable because it is used to cascade or ''piggyback'' the two PICs. Older PCs, such as the IBM XT, had only one PIC. Therefore, they were limited as to the number of interrupt adapters that could be installed. With larger systems, even 15 interrupts can sometimes be a limitation. ISA bus systems use edge triggered interrupts, a limitation which precludes the sharing of interrupt levels. MicroChannel and EISA bus machines use level triggered interrupts that let adapters share interrupt levels. OS/2 currently supports up to four Micro Channel or EISA adapters for each IRQ.  
provided by two 8-channel PICs. One IRQ, IRQ 2, is  
unavailable because it is used to cascade or piggyback  
the two PICs. Older PCs, such as the IBM XT, had only  
one PIC. Therefore, they were limited as to the number  
of interrupt adapters that could be installed. With  
larger systems, even 15 interrupts can sometimes be a  
limitation. ISA bus systems use edge triggered  
interrupts, a limitation which precludes the sharing of  
interrupt levels. MicroChannel and EISA bus machines use  
level triggered interrupts that let adapters share  
interrupt levels. OS/2 currently supports up to four  
Micro Channel or EISA adapters for each IRQ.  


In DOS, you could hook interrupts by replacing a pointer  
In DOS, you could ''hook'' interrupts by replacing a pointer in the interrupt jump table in the base page. In OS/2, however, you must register for the interrupt notification by calling the Device Helper routine, <tt>SetIRQ</tt>. One of the parameters to <tt>SetIRQ</tt> is the address of your driver's interrupt handler. The OS/2 kernel fields all interrupts, and notifies your driver that an interrupt has been received by calling your interrupt handler.  
in the interrupt jump table in the base page. In OS/2,  
however, you must register for the interrupt notification  
by calling the Device Helper routine, SetIRQ . One of the  
parameters to SetIRQ is the address of your driver's  
interrupt handler. The OS/2 kernel fields all interrupts,  
and notifies your driver that an interrupt has been  
received by calling your interrupt handler.  


The interrupt handler should perform it's operations and  
The interrupt handler should perform it's operations and reenable interrupts quickly. The last thing the interrupt handler should do before exiting the interrupt handler is to call <tt>DevHlp_EOI</tt>. This resets the interrupt logic, letting another interrupt occur. If you don't issue the <tt>EOI</tt>, your driver won't receive any more interrupts. If you leave the interrupts disabled when you exit your interrupt handler, the OS/2 kernel reenables them.  
reenable interrupts quickly. The last thing the  
interrupt handler should do before exiting the interrupt  
handler is to call DevHlp EOI . This resets the interrupt  
logic, letting another interrupt occur. If you don't  
issue the EOI, your driver won't receive any more  
interrupts. If you leave the interrupts disabled when  
you exit your interrupt handler, the OS/2 kernel  
reenables them.  


Always try to eliminate or limit the nesting of  
Always try to eliminate or limit the nesting of interrupts. Interrupt nesting will be necessary if your driver receives an interrupt while already processing an interrupt in the interrupt handler. The logic for nesting can be complicated and difficult to debug. The interrupt handler should never nest more than two interrupts. Use caution when designing interrupt handlers for Micro Channel and EISA bus systems because the interrupt handler is entered with interrupts enabled.
interrupts. Interrupt nesting will be necessary  
if your driver receives an interrupt while already  
processing an interrupt in the interrupt handler.
The logic for nesting can be complicated and  
difficult to debug. The interrupt handler should  
never nest more than two interrupts. Use caution  
when designing interrupt handlers for Micro Channel  
and EISA bus systems because the interrupt handler  
is entered with interrupts enabled.  


A question that I get most often from first-time driver writers is ''"I was debugging my device driver, and my driver received a IOCtl packet, category 5, function 0x48. My application never issued this. Where did it come from, and what does it mean?"''
A question that I get most often from first-time driver  
writers is "I was debugging my device driver, and my  
driver received a IOCtl packet, category 5, function 0x48.  
My application never issued this. Where did it come from,  
and what does it mean?  


The kernel issues the Category 5, Function 0x48 Code Page  
The kernel issues the Category 5, Function 0x48 Code Page <tt>IOCtl</tt>, with the purpose of finding out if your driver is a printer driver. If it is not, you can just return <tt>ERROR_BAD_COMMAND</tt>. Or, you can ignore it by returning <tt>DONE</tt>.  
IOCtl, with the purpose of finding out if your driver is  
a printer driver. If it is not, you can just return  
ERROR_BAD_COMMAND. Or, you can ignore it by returning DONE.  


==TIPS==
==Tips==
;Tip: For device driver development, use a machine with FAT partitions. Crashing an HPFS machine during driver development can result in files being damaged and lengthy reboots.
'''Tip: ''' For device driver development, use a machine with FAT  
partitions. Crashing an HPFS machine during driver  
development can result in files being damaged and lengthy  
reboots.  


'''Tip: ''' For debugging, instead of using an ASCII terminal,  
;Tip: For debugging, instead of using an ASCII terminal, use an old PC with a communications program, like Procomm or Telix. This allows you to enable data capture and keep a record or your debugging sessions. Keep a running record of the contents of all the registers by placing a breakpoint at the desired locations, and change the kernel debugger's default command to <tt>r;g</tt>. The r causes all the registers to appear and the g continues execution.
use an old PC with a communications program, like Procomm  
or Telix. This allows you to enable data capture and  
keep a record or your debugging sessions. Keep a running  
record of the contents of all the registers by placing a  
breakpoint at the desired locationsand change the  
kernel debugger's default command to r;g. The r causes  
all the registers to appear and the g continues execution.  


'''Tip: ''' Place your Init section at the end of your code.  
;Tip: Place your Init section at the end of your code. Return a pointer to the beginning of the Init section at the end of Init. This releases the memory occupied by the Init section. Don't try to save space or time during Init, neither one is an issue since Init is called only once during system boot, and the memory can be given back to OS/2 at the end of Init processing.
Return a pointer to the beginning of the Init section at  
 
the end of Init. This releases the memory occupied by  
[[Category:Developer Connection News Volume 3]][[Category:Driver Articles]]
the Init section. Don't try to save space or time  
during Init, neither one is an issue since Init is called  
only once during system boot, and the memory can be given  
back to OS/2 at the end of Init processing.

Latest revision as of 02:50, 7 December 2019

by Steve Mastrianni

IBM Developer Connection News Volume 3

Writing Device Drivers - Interrupts

I get a lot of calls asking "What is the interrupt system and how do I program for it?" To answer those questions, this article provides a beginner's view of the interrupt system.

In the traditional Intel-based PC, the interrupt system simply is a collection of electronics that let external events interrupt the program currently being executed. One of the electrical components, the Programmable Interrupt Controller (PIC) is notified of the external event, and in turn notifies the CPU that an interrupt has been received. The CPU saves the address of the code it was executing, then calls a special address assigned to that interrupt level, or IRQ, to perform the interrupt processing. When the interrupt processing is complete, the CPU resumes executing at the next instruction in the thread that was executing at the time of the interrupt. Of course, it's more complicated than that, but those are the basics.

Today, most PCs have 15 different interrupt levels, provided by two 8-channel PICs. One IRQ, IRQ 2, is unavailable because it is used to cascade or piggyback the two PICs. Older PCs, such as the IBM XT, had only one PIC. Therefore, they were limited as to the number of interrupt adapters that could be installed. With larger systems, even 15 interrupts can sometimes be a limitation. ISA bus systems use edge triggered interrupts, a limitation which precludes the sharing of interrupt levels. MicroChannel and EISA bus machines use level triggered interrupts that let adapters share interrupt levels. OS/2 currently supports up to four Micro Channel or EISA adapters for each IRQ.

In DOS, you could hook interrupts by replacing a pointer in the interrupt jump table in the base page. In OS/2, however, you must register for the interrupt notification by calling the Device Helper routine, SetIRQ. One of the parameters to SetIRQ is the address of your driver's interrupt handler. The OS/2 kernel fields all interrupts, and notifies your driver that an interrupt has been received by calling your interrupt handler.

The interrupt handler should perform it's operations and reenable interrupts quickly. The last thing the interrupt handler should do before exiting the interrupt handler is to call DevHlp_EOI. This resets the interrupt logic, letting another interrupt occur. If you don't issue the EOI, your driver won't receive any more interrupts. If you leave the interrupts disabled when you exit your interrupt handler, the OS/2 kernel reenables them.

Always try to eliminate or limit the nesting of interrupts. Interrupt nesting will be necessary if your driver receives an interrupt while already processing an interrupt in the interrupt handler. The logic for nesting can be complicated and difficult to debug. The interrupt handler should never nest more than two interrupts. Use caution when designing interrupt handlers for Micro Channel and EISA bus systems because the interrupt handler is entered with interrupts enabled.

A question that I get most often from first-time driver writers is "I was debugging my device driver, and my driver received a IOCtl packet, category 5, function 0x48. My application never issued this. Where did it come from, and what does it mean?"

The kernel issues the Category 5, Function 0x48 Code Page IOCtl, with the purpose of finding out if your driver is a printer driver. If it is not, you can just return ERROR_BAD_COMMAND. Or, you can ignore it by returning DONE.

Tips

Tip
For device driver development, use a machine with FAT partitions. Crashing an HPFS machine during driver development can result in files being damaged and lengthy reboots.
Tip
For debugging, instead of using an ASCII terminal, use an old PC with a communications program, like Procomm or Telix. This allows you to enable data capture and keep a record or your debugging sessions. Keep a running record of the contents of all the registers by placing a breakpoint at the desired locations, and change the kernel debugger's default command to r;g. The r causes all the registers to appear and the g continues execution.
Tip
Place your Init section at the end of your code. Return a pointer to the beginning of the Init section at the end of Init. This releases the memory occupied by the Init section. Don't try to save space or time during Init, neither one is an issue since Init is called only once during system boot, and the memory can be given back to OS/2 at the end of Init processing.