Introduction
Un périphérique matériel ne doit pas obliger le processeur à vérifier constamment son état. Il peut signaler un événement au processeur en générant une interruption matérielle.
Le driver fournit alors une routine appelée ISR : Interrupt Service Routine.
1. Pourquoi les interruptions existent
Sans interruption, le processeur devrait faire du polling :
Code: Select all
while (!(READ_REGISTER_ULONG(status) & READY))
{
// attendre
}
Code: Select all
CPU lance l'opération
|
v
périphérique travaille
|
v
interruption
|
v
ISR du driver
Dans les anciens drivers WDM, l'API historique est :
IoConnectInterrupt
La connexion crée un objet KINTERRUPT et associe l'ISR du driver à l'interruption.
Exemple conceptuel :
Code: Select all
PKINTERRUPT interruptObject = NULL;
status = IoConnectInterrupt(
&interruptObject,
IsrRoutine,
DeviceExtension,
NULL,
vector,
irql,
irql,
interruptMode,
shareVector,
affinity,
FALSE
);
3. Prototype d'une ISR
Code: Select all
BOOLEAN IsrRoutine(
PKINTERRUPT Interrupt,
PVOID ServiceContext
);
Code: Select all
return TRUE; // interruption reconnue
return FALSE; // pas notre périphérique
Une interruption peut être partagée. L'ISR doit donc lire un registre de statut du périphérique.
Code: Select all
ULONG status = READ_REGISTER_ULONG(devExt->StatusRegister);
if (!(status & DEVICE_INTERRUPT_PENDING))
return FALSE;
Une ISR ne doit pas retourner TRUE simplement parce qu'elle a été appelée. Elle doit confirmer que le matériel concerné a réellement demandé l'interruption.
5. Acquitter l'interruption
De nombreux périphériques exigent que le driver efface ou acquitte le bit d'interruption.
Code: Select all
WRITE_REGISTER_ULONG(
devExt->InterruptStatus,
INTERRUPT_ACK
);
6. L'ISR doit rester très courte
Une ISR s'exécute à un IRQL élevé. Elle bloque temporairement certaines autres activités du système.
Une bonne ISR fait généralement seulement :
- Vérifier la source de l'interruption.
- Acquitter le matériel.
- Lire ou sauvegarder quelques informations urgentes.
- Déclencher un DPC.
- Retourner rapidement.
7. IRQL et ISR
L'ISR s'exécute au DIRQL correspondant à l'interruption.
À ce niveau :
- Pas d'attente bloquante.
- Pas d'accès à de la mémoire paginable.
- Le code utilisé par l'ISR doit résider en mémoire non paginée.
- Les opérations doivent être courtes et déterministes.
L'ISR peut partager des variables avec un DPC ou d'autres routines du driver.
Exemple :
Code: Select all
typedef struct _DEVICE_EXTENSION
{
volatile BOOLEAN InterruptPending;
volatile ULONG BytesTransferred;
PKINTERRUPT InterruptObject;
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
9. KeSynchronizeExecution
Pour exécuter une routine en synchronisation avec l'ISR :
Code: Select all
BOOLEAN result = KeSynchronizeExecution(
devExt->InterruptObject,
SynchronizeRoutine,
context
);
Code: Select all
BOOLEAN SynchronizeRoutine(PVOID Context)
{
PDEVICE_EXTENSION devExt =
(PDEVICE_EXTENSION)Context;
// accès synchronisé aux données partagées
return TRUE;
}
10. Spin lock d'interruption
Le système associe une synchronisation interne à l'objet d'interruption. Les routines prévues permettent de coordonner correctement le code s'exécutant au niveau du driver avec l'ISR.
Il ne faut pas utiliser un mécanisme bloquant classique depuis une ISR.
11. Level-triggered et edge-triggered
Deux grandes familles existent :
- Level-sensitive : l'interruption reste active tant que la condition matérielle existe.
- Latched / edge-triggered : l'événement est capturé lors d'une transition.
12. Déconnecter l'interruption
Lors de l'arrêt ou de la suppression du périphérique :
Code: Select all
if (devExt->InterruptObject)
{
IoDisconnectInterrupt(devExt->InterruptObject);
devExt->InterruptObject = NULL;
}
13. Exemple conceptuel d'ISR
Code: Select all
BOOLEAN IsrRoutine(PKINTERRUPT Interrupt, PVOID Context)
{
UNREFERENCED_PARAMETER(Interrupt);
PDEVICE_EXTENSION devExt =
(PDEVICE_EXTENSION)Context;
ULONG status =
READ_REGISTER_ULONG(devExt->StatusRegister);
if (!(status & INT_PENDING))
return FALSE;
WRITE_REGISTER_ULONG(
devExt->StatusRegister,
INT_ACK
);
devExt->InterruptPending = TRUE;
IoRequestDpc(
devExt->DeviceObject,
NULL,
NULL
);
return TRUE;
}
Code: Select all
Périphérique
|
v
Interruption
|
v
ISR
|
+--> vérifier la source
+--> acquitter le matériel
+--> sauvegarder l'état minimal
+--> demander un DPC
|
v
retour rapide
