Der Interrupt-Handler

Die meisten Hardware-Schnittstellen werden mit Hilfe eines Interrupt-Handlers gesteuert. Die Schnittstelle unterbricht den Prozessor, um eines von zwei möglichen Ereignissen mitzuteilen: Entweder ist ein neues Paket angekommen, oder die Übertragung eines ausgehenden Paketes wurde abgeschlossen. Diese Generalisierung ist nicht immer ganz richtig, trifft aber bei allen Problemen der asynchronen Paket-Übertragung zu. Das Parallel Line Internet Protocol (PLIP) und das Point-to-Point Protocol (PPP) sind Beispiele für Schnittstellen, die nicht in diese Generalisierung passen. Sie haben es mit den gleichen Ereignissen zu tun, aber die Interrupt-Verarbeitung auf der unteren Ebene ist geringfügig anders.

Die normale Interrupt-Routine kann den Unterschied zwischen einem Interrupt wegen eines neu eingetroffenen Paketes und einem Interrupt wegen einer vollständig durchgeführten Übertragung anhand eines Status-Registers auf dem physikalischen Gerät selbst feststellen. snull arbeitet ähnlich, wenn auch dieser Zustandswert in der Software implementiert wird und in dev->priv steht. Der Interrupt-Handler für eine Netzwerk-Schnittstelle sieht folgendermaßen aus:


 
void snull_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
    int statusword;
    struct snull_priv *priv;
    /*
     * Wie ueblich den "Geraete"-Zeiger auf gemeinsam genutzte Handler
     * ueberpruefen, dann "struct net_device* dev" zuweisen.
     */
    struct net_device *dev = (struct net_device *)dev_id;
    /* ... und bei der Hardware ueberpruefen, ob das unser Geraet ist */

    if (!dev /*paranoid*/ ) return;

    /* Das Geraet sperren */
    priv = (struct snull_priv *) dev->priv;
    spin_lock(&priv->lock);

    /* Statuswert holen: richtige Geraete verwenden I/O-Anweisungen */
    statusword = priv->status;
    if (statusword & SNULL_RX_INTR) {
        /* an snull_rx zur Bearbeitung senden */
        snull_rx(dev, priv->rx_packetlen, priv->rx_packetdata);
    }
    if (statusword & SNULL_TX_INTR) {
        /* eine Uebertragung ist erledigt: den skb freigeben */
        priv->stats.tx_packets++;
        priv->stats.tx_bytes += priv->tx_packetlen;
        dev_kfree_skb(priv->skb);
    }

    /* Das Geraet freigeben und fertig */
    spin_unlock(&priv->lock);
    return;
}

Die erste Aufgabe des Handlers ist es, den Zeiger auf die richtige net_device-Struktur zu finden. Dieser Zeiger stammt normalerweise aus dem Zeiger dev_id, der als Argument übergeben wird.

Der interessante Teil des Handlers behandelt abgeschlossene Übertragungen. In diesem Fall wird die Statistik aktualisiert und dev_kfree_skb aufgerufen, um den (nicht mehr benötigten) Socket-Buffer an das System zurückzugeben. Wenn Ihr Treiber die Übertragungswarteschlange vorübergehend angehalten hat, ist dies auch die richtige Stelle, um sie mit netif_wake_queue wieder in Gang zu bringen.

Zum Empfangen der Pakete ist dagegen keine besondere Interrupt-Behandlung notwendig. Es muß lediglich snull_rx aufgerufen werden.