linux kernel有线网卡驱动enc28j60分析 二

1、上一章节我们已经分析到中断处理函数会调度一个工作队列,将更多的处理交给了一个work去处理。接下来我们进一步分析中断下半部都做了些什么处理。

static void enc28j60_irq_work_handler(struct work_struct *work)
{
    struct enc28j60_net *priv =
        container_of(work, struct enc28j60_net, irq_work);
    struct net_device *ndev = priv->netdev;
    int intflags, loop;

    if (netif_msg_intr(priv))
        printk(KERN_DEBUG DRV_NAME ": %s() enter\n", __func__);
    /* disable further interrupts */
    locked_reg_bfclr(priv, EIE, EIE_INTIE);

    do {
        loop = 0;
        intflags = locked_regb_read(priv, EIR);
        /* DMA interrupt handler (not currently used) */
        if ((intflags & EIR_DMAIF) != 0) {
            loop++;
            if (netif_msg_intr(priv))
                printk(KERN_DEBUG DRV_NAME
                    ": intDMA(%d)\n", loop);
            locked_reg_bfclr(priv, EIR, EIR_DMAIF);
        }
        /* LINK changed handler */
        if ((intflags & EIR_LINKIF) != 0) {
            loop++;
            if (netif_msg_intr(priv))
                printk(KERN_DEBUG DRV_NAME
                    ": intLINK(%d)\n", loop);
            enc28j60_check_link_status(ndev);
            /* read PHIR to clear the flag */
            enc28j60_phy_read(priv, PHIR);
        }
        /* TX complete handler */
        if (((intflags & EIR_TXIF) != 0) &&
            ((intflags & EIR_TXERIF) == 0)) {
            bool err = false;
            loop++;
            if (netif_msg_intr(priv))
                printk(KERN_DEBUG DRV_NAME
                    ": intTX(%d)\n", loop);
            priv->tx_retry_count = 0;
            if (locked_regb_read(priv, ESTAT) & ESTAT_TXABRT) {
                if (netif_msg_tx_err(priv))
                    dev_err(&ndev->dev,
                        "Tx Error (aborted)\n");
                err = true;
            }
            if (netif_msg_tx_done(priv)) {
                u8 tsv[TSV_SIZE];
                enc28j60_read_tsv(priv, tsv);
                enc28j60_dump_tsv(priv, "Tx Done", tsv);
            }
            enc28j60_tx_clear(ndev, err);
            locked_reg_bfclr(priv, EIR, EIR_TXIF);
        }
        /* TX Error handler */
        if ((intflags & EIR_TXERIF) != 0) {
            u8 tsv[TSV_SIZE];

            loop++;
            if (netif_msg_intr(priv))
                printk(KERN_DEBUG DRV_NAME
                    ": intTXErr(%d)\n", loop);
            locked_reg_bfclr(priv, ECON1, ECON1_TXRTS);
            enc28j60_read_tsv(priv, tsv);
            if (netif_msg_tx_err(priv))
                enc28j60_dump_tsv(priv, "Tx Error", tsv);
            /* Reset TX logic */
            mutex_lock(&priv->lock);
            nolock_reg_bfset(priv, ECON1, ECON1_TXRST);
            nolock_reg_bfclr(priv, ECON1, ECON1_TXRST);
            nolock_txfifo_init(priv, TXSTART_INIT, TXEND_INIT);
            mutex_unlock(&priv->lock);
            /* Transmit Late collision check for retransmit */
            if (TSV_GETBIT(tsv, TSV_TXLATECOLLISION)) {
                if (netif_msg_tx_err(priv))
                    printk(KERN_DEBUG DRV_NAME
                        ": LateCollision TXErr (%d)\n",
                        priv->tx_retry_count);
                if (priv->tx_retry_count++ < MAX_TX_RETRYCOUNT)
                    locked_reg_bfset(priv, ECON1,
                               ECON1_TXRTS);
                else
                    enc28j60_tx_clear(ndev, true);
            } else
                enc28j60_tx_clear(ndev, true);
            locked_reg_bfclr(priv, EIR, EIR_TXERIF | EIR_TXIF);
        }
        /* RX Error handler */
        if ((intflags & EIR_RXERIF) != 0) {
            loop++;
            if (netif_msg_intr(priv))
                printk(KERN_DEBUG DRV_NAME
                    ": intRXErr(%d)\n", loop);
            /* Check free FIFO space to flag RX overrun */
            if (enc28j60_get_free_rxfifo(priv) <= 0) {
                if (netif_msg_rx_err(priv))
                    printk(KERN_DEBUG DRV_NAME
                        ": RX Overrun\n");
                ndev->stats.rx_dropped++;
            }
            locked_reg_bfclr(priv, EIR, EIR_RXERIF);
        }
        /* RX handler */
        if (enc28j60_rx_interrupt(ndev))
            loop++;
    } while (loop);

    /* re-enable interrupts */
    locked_reg_bfset(priv, EIE, EIE_INTIE);
    if (netif_msg_intr(priv))
        printk(KERN_DEBUG DRV_NAME ": %s() exit\n", __func__);
}

在中断下半部处理函数中,会读取读取中断寄存器EIR来判断是哪一类中断,总共有这么几类中断:
EIR_DMAIF:DMA复制或校验和计算已完成
EIR_LINKIF:连接状态发生改变产生中断
EIR_TXIF: 发送完成中断
EIR_TXERIF:发送错误中断
EIR_RXERIF:接收错误中断
上述这些中断都是一些状态以及异常情况的中断,还有一个最重要的中断就是接收的数据包后产生中断。

static int enc28j60_rx_interrupt(struct net_device *ndev)
{
    struct enc28j60_net *priv = netdev_priv(ndev);
    int pk_counter, ret;

    pk_counter = locked_regb_read(priv, EPKTCNT);
    if (pk_counter && netif_msg_intr(priv))
        printk(KERN_DEBUG DRV_NAME ": intRX, pk_cnt: %d\n", pk_counter);
    if (pk_counter > priv->max_pk_counter) {
        /* update statistics */
        priv->max_pk_counter = pk_counter;
        if (netif_msg_rx_status(priv) && priv->max_pk_counter > 1)
            printk(KERN_DEBUG DRV_NAME ": RX max_pk_cnt: %d\n",
                priv->max_pk_counter);
    }
    ret = pk_counter;
    while (pk_counter-- > 0)
        enc28j60_hw_rx(ndev);

    return ret;
}

代码中通过读取寄存器EPKTCNT获取有多少数据包待处理,然后通过调用enc28j60_hw_rx处理接收到的数据包。

2、报文接收处理

/*
 * Hardware receive function.
 * Read the buffer memory, update the FIFO pointer to free the buffer,
 * check the status vector and decrement the packet counter.
 */
static void enc28j60_hw_rx(struct net_device *ndev)
{
    struct enc28j60_net *priv = netdev_priv(ndev);
    struct sk_buff *skb = NULL;
    u16 erxrdpt, next_packet, rxstat;
    u8 rsv[RSV_SIZE];
    int len;

    if (netif_msg_rx_status(priv))
        printk(KERN_DEBUG DRV_NAME ": RX pk_addr:0x%04x\n",
            priv->next_pk_ptr);

    if (unlikely(priv->next_pk_ptr > RXEND_INIT)) {
        if (netif_msg_rx_err(priv))
            dev_err(&ndev->dev,
                "%s() Invalid packet address!! 0x%04x\n",
                __func__, priv->next_pk_ptr);
        /* packet address corrupted: reset RX logic */
        mutex_lock(&priv->lock);
        nolock_reg_bfclr(priv, ECON1, ECON1_RXEN);
        nolock_reg_bfset(priv, ECON1, ECON1_RXRST);
        nolock_reg_bfclr(priv, ECON1, ECON1_RXRST);
        nolock_rxfifo_init(priv, RXSTART_INIT, RXEND_INIT);
        nolock_reg_bfclr(priv, EIR, EIR_RXERIF);
        nolock_reg_bfset(priv, ECON1, ECON1_RXEN);
        mutex_unlock(&priv->lock);
        ndev->stats.rx_errors++;
        return;
    }
    /* Read next packet pointer and rx status vector */
    enc28j60_mem_read(priv, priv->next_pk_ptr, sizeof(rsv), rsv);

    next_packet = rsv[1];
    next_packet <<= 8;
    next_packet |= rsv[0];

    len = rsv[3];
    len <<= 8;
    len |= rsv[2];

    rxstat = rsv[5];
    rxstat <<= 8;
    rxstat |= rsv[4];

    if (netif_msg_rx_status(priv))
        enc28j60_dump_rsv(priv, __func__, next_packet, len, rxstat);

    if (!RSV_GETBIT(rxstat, RSV_RXOK) || len > MAX_FRAMELEN) {
        if (netif_msg_rx_err(priv))
            dev_err(&ndev->dev, "Rx Error (%04x)\n", rxstat);
        ndev->stats.rx_errors++;
        if (RSV_GETBIT(rxstat, RSV_CRCERROR))
            ndev->stats.rx_crc_errors++;
        if (RSV_GETBIT(rxstat, RSV_LENCHECKERR))
            ndev->stats.rx_frame_errors++;
        if (len > MAX_FRAMELEN)
            ndev->stats.rx_over_errors++;
    } else {
        skb = netdev_alloc_skb(ndev, len + NET_IP_ALIGN);
        if (!skb) {
            if (netif_msg_rx_err(priv))
                dev_err(&ndev->dev,
                    "out of memory for Rx'd frame\n");
            ndev->stats.rx_dropped++;
        } else {
            skb_reserve(skb, NET_IP_ALIGN);
            /* copy the packet from the receive buffer */
            enc28j60_mem_read(priv,
                rx_packet_start(priv->next_pk_ptr),
                len, skb_put(skb, len));
            if (netif_msg_pktdata(priv))
                dump_packet(__func__, skb->len, skb->data);
            skb->protocol = eth_type_trans(skb, ndev);
            /* update statistics */
            ndev->stats.rx_packets++;
            ndev->stats.rx_bytes += len;
            netif_rx_ni(skb);
        }
    }
    /*
     * Move the RX read pointer to the start of the next
     * received packet.
     * This frees the memory we just read out
     */
    erxrdpt = erxrdpt_workaround(next_packet, RXSTART_INIT, RXEND_INIT);
    if (netif_msg_hw(priv))
        printk(KERN_DEBUG DRV_NAME ": %s() ERXRDPT:0x%04x\n",
            __func__, erxrdpt);

    mutex_lock(&priv->lock);
    nolock_regw_write(priv, ERXRDPTL, erxrdpt);
#ifdef CONFIG_ENC28J60_WRITEVERIFY
    if (netif_msg_drv(priv)) {
        u16 reg;
        reg = nolock_regw_read(priv, ERXRDPTL);
        if (reg != erxrdpt)
            printk(KERN_DEBUG DRV_NAME ": %s() ERXRDPT verify "
                "error (0x%04x - 0x%04x)\n", __func__,
                reg, erxrdpt);
    }
#endif
    priv->next_pk_ptr = next_packet;
    /* we are done with this packet, decrement the packet counter */
    nolock_reg_bfset(priv, ECON2, ECON2_PKTDEC);
    mutex_unlock(&priv->lock);
}

上述代码中最重要的是下面这一段

{
        skb = netdev_alloc_skb(ndev, len + NET_IP_ALIGN);
        if (!skb) {
            if (netif_msg_rx_err(priv))
                dev_err(&ndev->dev,
                    "out of memory for Rx'd frame\n");
            ndev->stats.rx_dropped++;
        } else {
            skb_reserve(skb, NET_IP_ALIGN);
            /* copy the packet from the receive buffer */
            enc28j60_mem_read(priv,
                rx_packet_start(priv->next_pk_ptr),
                len, skb_put(skb, len));
            if (netif_msg_pktdata(priv))
                dump_packet(__func__, skb->len, skb->data);
            skb->protocol = eth_type_trans(skb, ndev);
            /* update statistics */
            ndev->stats.rx_packets++;
            ndev->stats.rx_bytes += len;
            netif_rx_ni(skb);
        }
    }

上面这段代码中通过skb = netdev_alloc_skb(ndev, len + NET_IP_ALIGN);申请了一个sk_buffer,这是一个非常重要的结构体。与内核协议栈通信就需要构造这么一个结构体上传。
enc28j60_mem_read(priv, rx_packet_start(priv->next_pk_ptr), len, skb_put(skb, len));这是将数据从网卡的memory里读出来,保存到sk_buffer里面的内存空间里。netif_rx_ni(skb);调用内核的接口,将构造好的sk_buffer发送给内核协议栈,至此整个接收报文流程完毕。

3、报文发送
报文发送是由上层协议栈调用我们驱动注册的函数接口来发包的,不知道大家是否还记得我们之前在probe过程的时候,动态申请了net_device结构体,并对其填充了netdev_ops,填充的内容如下:

static const struct net_device_ops enc28j60_netdev_ops = {
    .ndo_open       = enc28j60_net_open,
    .ndo_stop       = enc28j60_net_close,
    .ndo_start_xmit     = enc28j60_send_packet,
    .ndo_set_rx_mode    = enc28j60_set_multicast_list,
    .ndo_set_mac_address    = enc28j60_set_mac_address,
    .ndo_tx_timeout     = enc28j60_tx_timeout,
    .ndo_validate_addr  = eth_validate_addr,
};

ndo_start_xmit这个函数指针是用来发送报文用的。发送函数如下:

static netdev_tx_t enc28j60_send_packet(struct sk_buff *skb,
                    struct net_device *dev)
{
    struct enc28j60_net *priv = netdev_priv(dev);

    if (netif_msg_tx_queued(priv))
        printk(KERN_DEBUG DRV_NAME ": %s() enter\n", __func__);

    /* If some error occurs while trying to transmit this
     * packet, you should return '1' from this function.
     * In such a case you _may not_ do anything to the
     * SKB, it is still owned by the network queueing
     * layer when an error is returned.  This means you
     * may not modify any SKB fields, you may not free
     * the SKB, etc.
     */
    netif_stop_queue(dev);

    /* Remember the skb for deferred processing */
    priv->tx_skb = skb;
    schedule_work(&priv->tx_work);

    return NETDEV_TX_OK;
}

该函数中,一进来就使用netif_stop_queue来禁止上层应用再对该网络设备发包。然后又调度了一个工作队列来进一步处理。
之前probe过程时将tx_workenc28j60_tx_work_handler绑定;

INIT_WORK(&priv->tx_work, enc28j60_tx_work_handler);
static void enc28j60_tx_work_handler(struct work_struct *work)
{
    struct enc28j60_net *priv =
        container_of(work, struct enc28j60_net, tx_work);

    /* actual delivery of data */
    enc28j60_hw_tx(priv);
}

需要继续看enc28j60_hw_tx函数。

/*
 * Hardware transmit function.
 * Fill the buffer memory and send the contents of the transmit buffer
 * onto the network
 */
static void enc28j60_hw_tx(struct enc28j60_net *priv)
{
    BUG_ON(!priv->tx_skb);

    if (netif_msg_tx_queued(priv))
        printk(KERN_DEBUG DRV_NAME
            ": Tx Packet Len:%d\n", priv->tx_skb->len);

    if (netif_msg_pktdata(priv))
        dump_packet(__func__,
                priv->tx_skb->len, priv->tx_skb->data);
    enc28j60_packet_write(priv, priv->tx_skb->len, priv->tx_skb->data);

#ifdef CONFIG_ENC28J60_WRITEVERIFY
    /* readback and verify written data */
    if (netif_msg_drv(priv)) {
        int test_len, k;
        u8 test_buf[64]; /* limit the test to the first 64 bytes */
        int okflag;

        test_len = priv->tx_skb->len;
        if (test_len > sizeof(test_buf))
            test_len = sizeof(test_buf);

        /* + 1 to skip control byte */
        enc28j60_mem_read(priv, TXSTART_INIT + 1, test_len, test_buf);
        okflag = 1;
        for (k = 0; k < test_len; k++) {
            if (priv->tx_skb->data[k] != test_buf[k]) {
                printk(KERN_DEBUG DRV_NAME
                     ": Error, %d location differ: "
                     "0x%02x-0x%02x\n", k,
                     priv->tx_skb->data[k], test_buf[k]);
                okflag = 0;
            }
        }
        if (!okflag)
            printk(KERN_DEBUG DRV_NAME ": Tx write buffer, "
                "verify ERROR!\n");
    }
#endif
    /* set TX request flag */
    locked_reg_bfset(priv, ECON1, ECON1_TXRTS);
}

在这个函数中与linux网络架构相关的就是
enc28j60_packet_write(priv, priv->tx_skb->len, priv->tx_skb->data);
从该句话中可以获知,sk_buffer中的len保存发送长度,data保存数据的起始位置。该函数中其他代码是跟该网卡驱动相关的硬件操作,不做太详细的分析。

到这里网卡已经可以将该数据包发出去,但是有一点需要考虑的就是,协议栈发给网卡驱动的sk_buffer应该由谁来释放?不释放就会造成内存泄漏。如果由协议栈释放,那么可能会出现驱动正在用sk_buffer里面的数据,结果协议栈就给释放了,要挨打的。那么只能由驱动来释放了。一般网卡都会有一个数据发送完毕的事件。在此时可以对sk_buffer来释放。在enc28j60中存在一个发送完成的中断,可以使用此中断进行sk_buffer的回收操作。
还有就是之前调用了netif_stop_queue(dev);来禁止上层再发包,此时也应该使能发包。

        /* TX complete handler */
        if (((intflags & EIR_TXIF) != 0) &&
            ((intflags & EIR_TXERIF) == 0)) {
            bool err = false;
            loop++;
            if (netif_msg_intr(priv))
                printk(KERN_DEBUG DRV_NAME
                    ": intTX(%d)\n", loop);
            priv->tx_retry_count = 0;
            if (locked_regb_read(priv, ESTAT) & ESTAT_TXABRT) {
                if (netif_msg_tx_err(priv))
                    dev_err(&ndev->dev,
                        "Tx Error (aborted)\n");
                err = true;
            }
            if (netif_msg_tx_done(priv)) {
                u8 tsv[TSV_SIZE];
                enc28j60_read_tsv(priv, tsv);
                enc28j60_dump_tsv(priv, "Tx Done", tsv);
            }
            enc28j60_tx_clear(ndev, err);
            locked_reg_bfclr(priv, EIR, EIR_TXIF);

上面这段代码为中断处理下半部的一部分代码,专门处理发送完成中断。这里最重要的是enc28j60_tx_clear函数。

static void enc28j60_tx_clear(struct net_device *ndev, bool err)
{
    struct enc28j60_net *priv = netdev_priv(ndev);

    if (err)
        ndev->stats.tx_errors++;
    else
        ndev->stats.tx_packets++;

    if (priv->tx_skb) {
        if (!err)
            ndev->stats.tx_bytes += priv->tx_skb->len;
        dev_kfree_skb(priv->tx_skb);
        priv->tx_skb = NULL;
    }
    locked_reg_bfclr(priv, ECON1, ECON1_TXRTS);
    netif_wake_queue(ndev);
}

enc28j60_tx_clear函数中,对发送成功和发送失败进行了统计。调用dev_kfree_skb释放之前的sk_buffer内存空间。
调用netif_wake_queue来使能上层发包。

至此,发包流程结束。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值