一步步写网卡驱动(三)

        昨天说到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, 到此我们的网卡应该就能正常的发送和接收数据了, 其他的一些相关细节, 我们以后有时间在慢慢完善, 现在就去试试我们自己写的网卡驱动吧!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值