一步步寫網卡驅動(三)

        昨天說到DM9000的open函數, 我們先來分析下網卡的open函數需要完成什麼功能:

                1. 註冊中斷處理例程

                2. 初始化網卡

                3. 使能發送隊列

static int lydm9k_open(struct net_device *ndev)
{
    struct lydm9k_priv *priv = netdev_priv(ndev);
    unsigned long irqflags = priv->irq_res->flags & IRQF_TRIGGER_MASK;

    printdbg("invoked\n");

    irqflags |= IRQF_SHARED;
    if(request_irq(ndev->irq, dm9k_interrupt, irqflags, ndev->name, ndev)) // 註冊網卡中斷處理例程
    {
        dm9kmsg("request irq fail\n");
        return -EAGAIN;
    }
    // 初始化網卡
    dm9k_reset(priv);
    dm9k_init(priv);

    ndev->trans_start = 0;

    netif_start_queue(ndev); // 使能發送隊列

    return 0;
}

 

        先來看下網卡的初始化, 這部分都是硬件寄存器操作, 一兩句也說不清楚, 大家對DM9000感興趣的可以看下DM9000的datasheet, 只是想了解網卡驅動模型的只要知道這段代碼作用就可以了:

static void inline dm9k_reset(struct lydm9k_priv *priv)
{
    regw(priv, DM9000_NCR, NCR_RST | (1<<1));
    udelay(200); // at least 20 usecs
}

static void inline dm9k_init(struct lydm9k_priv *priv)
{
    int i;
    int oft;

    regw(priv, DM9000_GPCR, GPCR_GEP_CNTL);
    regw(priv, DM9000_GPR, 0);

    regw(priv, DM9000_RCR, RCR_RXEN |  RCR_DIS_CRC | RCR_DIS_LONG);
    regw(priv, DM9000_TCR, 0x00);
    regw(priv, DM9000_BPTR, 0x3f);
    regw(priv, DM9000_FCTR, 0x38);
    regw(priv, DM9000_FCR, 0xff);
    regw(priv, DM9000_SMCR, 0x00);
    regw(priv, DM9000_NSR, NSR_TX1END | NSR_TX2END | NSR_WAKEST);
    regw(priv, DM9000_ISR, 0x0f); // clear interrupt status

    regw(priv, DM9000_PAB0, priv->mac[0]);
    regw(priv, DM9000_PAB1, priv->mac[1]);
    regw(priv, DM9000_PAB2, priv->mac[2]);
    regw(priv, DM9000_PAB3, priv->mac[3]);
    regw(priv, DM9000_PAB4, priv->mac[4]);
    regw(priv, DM9000_PAB5, priv->mac[5]);

    for(i = 0, oft = DM9000_MAR; i<8; i++, oft++)
    {
        regw(priv, oft, 0xff);
    }

    regw(priv, DM9000_IMR, IMR_PRM | IMR_PTM | IMR_PAR);
}


        接着來看中斷處理例程, 從我們對IMR寄存器的設置看出我們需要關心的有網卡的接收中斷和發送完成中斷

static irqreturn_t dm9k_interrupt(int irq, void *dev_id)
{
    struct net_device *ndev = dev_id;
    struct lydm9k_priv *priv = netdev_priv(ndev);
    unsigned char int_status;
    unsigned char reg_save;

    /* Save previous register address */
    reg_save = readb(priv->io_reg);

    /* Disable all interrupts */
    regw(priv, DM9000_IMR, IMR_PAR);

    int_status = regr(priv, DM9000_ISR);
    regw(priv, DM9000_ISR, int_status);
    printdbg("int_status = %02x\n", int_status);

    // recv irq
    if(int_status & ISR_PRS)
    {
        while(0 == dm9k_recv_pkt(ndev));
    }

    // trans irq
    if(int_status & ISR_PTS)
    {
        dm9k_tx_done(ndev);
    }

    /* Re-enable interrupt mask */
    regw(priv, DM9000_IMR, IMR_PRM | IMR_PTM | IMR_PAR);

    /* Restore previous register address */
    writeb(reg_save, priv->io_reg);

    return IRQ_HANDLED;
}


        我們先來分析接收中斷, 當網卡接收到數據包之後, 我們需要從網卡里吧數據包讀出來, 並構造一個skb用來描述接收到的數據, 並將skb傳輸給網絡協議棧:

struct dm9000_rxhdr {
    u8 RxPktReady;
    u8 RxStatus;
    __le16 RxLen;
} __attribute__((__packed__));

 

static int dm9k_recv_pkt(struct net_device *ndev) {     unsigned char rx_status;     unsigned short rx_len;     struct dm9000_rxhdr rxhdr;     unsigned char *data;     struct sk_buff *skb;     struct lydm9k_priv *priv = netdev_priv(ndev);

    printdbg("invoked\n");

    /* Get most updated data */     rx_status = regr(priv, DM9000_MRCMDX);     printdbg("rx_status = 0x%x\n", rx_status);

    /* Status check: this byte must be 0 or 1 */     if (rx_status & DM9000_PKT_ERR) {         printdbg("recv pkt err\n");         regw(priv, DM9000_RCR, 0x00); /* Stop Device */         regw(priv, DM9000_ISR, IMR_PAR); /* Stop INT request */         return -1;     }

    if (!(rx_status & DM9000_PKT_RDY))     {         printdbg("there is no pkt\n");         return -1;     }

    inblk(priv, DM9000_MRCMD, &rxhdr, sizeof(rxhdr));     rx_len = le16_to_cpu(rxhdr.RxLen);     printdbg("recv 0x%x bytes\n", rx_len);

    skb = dev_alloc_skb(rx_len + 4);     if(NULL == skb)     {         dumpblk(priv, DM9000_MRCMD, rx_len);         return -ENOMEM;     }

    skb_reserve(skb, 2);     data = skb_put(skb, rx_len - 4);     inblk(priv, DM9000_MRCMD, data, rx_len);

    skb->protocol = eth_type_trans(skb, ndev);     skb->ip_summed = CHECKSUM_NONE;

#ifdef LYDM9000_DEBUG     for(i = 0; i<skb->len; i++)     {         printk("0x%02x\t", *(skb->data + i));     }     printk("\n"); #endif

    netif_rx(skb);

    return 0;

    return -1; }


        然後我們來分析數據包的發送, 當上層需要發送一個數據包時就會調用ndo_start_xmit將要發送的skb傳送到網卡驅動, 我們需要在驅動程序中實現將skb中的數據拷貝到網卡的發送緩衝區裏, 併發送, 在發送完成之前禁止下個數據包請求ndo_start_xmit發送(其實DM9000的發送緩衝區空間足夠容納2個數據包, 我們爲了簡單, 之實現發送一個skb)

static int lydm9k_start_xmit(struct sk_buff *skb, 
        struct net_device *ndev)
{
    struct lydm9k_priv *priv = netdev_priv(ndev);

    printdbg("invoked\n");

    dm9k_send_pkt(priv, skb->data, skb->len);

    netif_stop_queue(ndev);

    dev_kfree_skb(skb);

    return NETDEV_TX_OK;
}

 

static void dm9k_send_pkt(struct lydm9k_priv *priv,         unsigned char *data,         size_t len) {     regw(priv, DM9000_TXPLH, (len >> 8) & 0xff);     regw(priv, DM9000_TXPLL, len & 0xff);

    outblk(priv, DM9000_MWCMD, data, len);

    regw(priv, DM9000_TCR, TCR_TXREQ); }

 

        當數據包發送完成就會觸發發送完成中斷, 在中斷處理例程裏可以看到, 此時我們需要使能並喚醒發送隊列, 發送下個數據包

static void dm9k_tx_done(struct net_device *ndev)
{
    struct lydm9k_priv *priv = netdev_priv(ndev);
    unsigned char tx_status = regr(priv, DM9000_NSR);

    if(tx_status & (NSR_TX1END | NSR_TX2END))
    {
        netif_wake_queue(ndev);
    }
}


 

        天有不測風雲, 數據包可能會由於某種原因, 未能及時發送出去或發送完成中斷丟失, 此時驅動程序的ndo_tx_timeout函數就會被調用, 此時我們需要做的是重置網卡並使能發送隊列

static void lydm9k_timeout(struct net_device *ndev)
{
    struct lydm9k_priv *priv = netdev_priv(ndev);

    printdbg("invoked\n");

    netif_stop_queue(ndev);

    dm9k_reset(priv);
    dm9k_init(priv);

    ndev->trans_start = jiffies;

    netif_start_queue(ndev);
}


        OK, 到此我們的網卡應該就能正常的發送和接收數據了, 其他的一些相關細節, 我們以後有時間在慢慢完善, 現在就去試試我們自己寫的網卡驅動吧!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章