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

1、为了更好低学习linux的网络驱动架构,本文选择分析linux kernel下的有线网卡驱动enc28j60来学习网络驱动架构。

enc28j60是一个10/100Mb的有线网卡,适用于嵌入式设备上进行网络通信,使用SPI接口与CPU进行通信。该驱动已经集成在linux kernel源码中,路径是driver/net/ethernet/microchip/enc28j60.c

2、驱动入口
既然是使用SPI接口与CPU进行通信,那么想当然地一定用了SPI Driver的架构,入口和出口如下:

static const struct of_device_id enc28j60_dt_ids[] = {
    { .compatible = "microchip,enc28j60" },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, enc28j60_dt_ids);

static struct spi_driver enc28j60_driver = {
    .driver = {
        .name = DRV_NAME,
        .of_match_table = enc28j60_dt_ids,
     },
    .probe = enc28j60_probe,
    .remove = enc28j60_remove,
};

static int __init enc28j60_init(void)
{
    msec20_to_jiffies = msecs_to_jiffies(20);

    return spi_register_driver(&enc28j60_driver);
}

module_init(enc28j60_init);

static void __exit enc28j60_exit(void)
{
    spi_unregister_driver(&enc28j60_driver);
}

使用spi_register_driver和spi_unregister_driver分别向内核注册和注销驱动的作用。
使用MODULE_DEVICE_TABLE对外输出device_id,以供其他模块依赖加载。
在driver和device匹配(match)后,会调用spi_driver中的probe函数,我们在probe函数中进一步进行初始化。

2、probe初始化
probe函数详情如下:

static int enc28j60_probe(struct spi_device *spi)
{
    struct net_device *dev;
    struct enc28j60_net *priv;
    const void *macaddr;
    int ret = 0;

    if (netif_msg_drv(&debug))
        dev_info(&spi->dev, DRV_NAME " Ethernet driver %s loaded\n",
            DRV_VERSION);

    dev = alloc_etherdev(sizeof(struct enc28j60_net));
    if (!dev) {
        ret = -ENOMEM;
        goto error_alloc;
    }
    priv = netdev_priv(dev);

    priv->netdev = dev; /* priv to netdev reference */
    priv->spi = spi;    /* priv to spi reference */
    priv->msg_enable = netif_msg_init(debug.msg_enable,
                        ENC28J60_MSG_DEFAULT);
    mutex_init(&priv->lock);
    INIT_WORK(&priv->tx_work, enc28j60_tx_work_handler);
    INIT_WORK(&priv->setrx_work, enc28j60_setrx_work_handler);
    INIT_WORK(&priv->irq_work, enc28j60_irq_work_handler);
    INIT_WORK(&priv->restart_work, enc28j60_restart_work_handler);
    spi_set_drvdata(spi, priv); /* spi to priv reference */
    SET_NETDEV_DEV(dev, &spi->dev);

    if (!enc28j60_chipset_init(dev)) {
        if (netif_msg_probe(priv))
            dev_info(&spi->dev, DRV_NAME " chip not found\n");
        ret = -EIO;
        goto error_irq;
    }

    macaddr = of_get_mac_address(spi->dev.of_node);
    if (macaddr)
        ether_addr_copy(dev->dev_addr, macaddr);
    else
        eth_hw_addr_random(dev);
    enc28j60_set_hw_macaddr(dev);

    /* Board setup must set the relevant edge trigger type;
     * level triggers won't currently work.
     */
    ret = request_irq(spi->irq, enc28j60_irq, 0, DRV_NAME, priv);
    if (ret < 0) {
        if (netif_msg_probe(priv))
            dev_err(&spi->dev, DRV_NAME ": request irq %d failed "
                "(ret = %d)\n", spi->irq, ret);
        goto error_irq;
    }

    dev->if_port = IF_PORT_10BASET;
    dev->irq = spi->irq;
    dev->netdev_ops = &enc28j60_netdev_ops;
    dev->watchdog_timeo = TX_TIMEOUT;
    dev->ethtool_ops = &enc28j60_ethtool_ops;

    enc28j60_lowpower(priv, true);

    ret = register_netdev(dev);
    if (ret) {
        if (netif_msg_probe(priv))
            dev_err(&spi->dev, "register netdev " DRV_NAME
                " failed (ret = %d)\n", ret);
        goto error_register;
    }
    dev_info(&dev->dev, DRV_NAME " driver registered\n");

    return 0;

error_register:
    free_irq(spi->irq, priv);
error_irq:
    free_netdev(dev);
error_alloc:
    return ret;
}

dev = alloc_etherdev(sizeof(struct enc28j60_net));
在这里动态地申请net_device结构体,同时申请了大小为sizeof(struct enc28j60_net)的私有空间,其内存分布如下:

内存低地址 内存高地址
net_device enc28j60_net
priv = netdev_priv(dev);

通过netdev_priv获取到私有空间的地址,即struct enc28j60_net这个结构体的地址。

priv->netdev = dev; /* priv to netdev reference */
priv->spi = spi;    /* priv to spi reference */
priv->msg_enable = netif_msg_init(debug.msg_enable,
                        ENC28J60_MSG_DEFAULT);
mutex_init(&priv->lock);
INIT_WORK(&priv->tx_work, enc28j60_tx_work_handler);
INIT_WORK(&priv->setrx_work, enc28j60_setrx_work_handler);
INIT_WORK(&priv->irq_work, enc28j60_irq_work_handler);
INIT_WORK(&priv->restart_work, enc28j60_restart_work_handler);

在这里对申请的enc28j60_net结构体成员进行初始化。

spi_set_drvdata(spi, priv); /* spi to priv reference */
SET_NETDEV_DEV(dev, &spi->dev);

spi_set_drvdatastruct enc28j60_net这个结构体与spi_device关联在一起

if (!enc28j60_chipset_init(dev)) {
    if (netif_msg_probe(priv))
        dev_info(&spi->dev, DRV_NAME " chip not found\n");
    ret = -EIO;
    goto error_irq;
}

这里对硬件进行初始化,设置寄存器

    macaddr = of_get_mac_address(spi->dev.of_node);
    if (macaddr)
        ether_addr_copy(dev->dev_addr, macaddr);
    else
        eth_hw_addr_random(dev);
    enc28j60_set_hw_macaddr(dev);

上述代码进行mac设置,如果设备树里面指定了mac地址,则使用设备树里的,否则随机生成。

    ret = request_irq(spi->irq, enc28j60_irq, 0, DRV_NAME, priv);
    if (ret < 0) {
        if (netif_msg_probe(priv))
            dev_err(&spi->dev, DRV_NAME ": request irq %d failed "
                "(ret = %d)\n", spi->irq, ret);
        goto error_irq;
    }

在这里申请中断,并指定中断处理函数。

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,
};

static const struct ethtool_ops enc28j60_ethtool_ops = {
    .get_drvinfo    = enc28j60_get_drvinfo,
    .get_msglevel   = enc28j60_get_msglevel,
    .set_msglevel   = enc28j60_set_msglevel,
    .get_link_ksettings = enc28j60_get_link_ksettings,
    .set_link_ksettings = enc28j60_set_link_ksettings,
};

    dev->if_port = IF_PORT_10BASET;
    dev->irq = spi->irq;
    dev->netdev_ops = &enc28j60_netdev_ops;
    dev->watchdog_timeo = TX_TIMEOUT;
    dev->ethtool_ops = &enc28j60_ethtool_ops;
    enc28j60_lowpower(priv, true);
    ret = register_netdev(dev);

在这里填充net_device结构体,设置该网络设备的netdev_opsethtool_ops。其中netdev_ops负责该设备的打开、关闭、发送等有关功能性的函数接口;ethtool_ops负责一些负责性的函数接口,比如获取连接速度、MAC地址等。

至此网卡初始化完毕,上层函数发包使用网络设备对应的ndo_start_xmit进行发包,收包由中断处理函数唤醒收包队列进行收包。

中断处理如下

static irqreturn_t enc28j60_irq(int irq, void *dev_id)
{
    struct enc28j60_net *priv = dev_id;

    schedule_work(&priv->irq_work);
    return IRQ_HANDLED;
}


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__);
}

中断处理函数enc28j60_irq_work_handler很简单,就是直接调度一个work,而该work在probe初始化的时候,已经与enc28j60_irq_work_handler关联。

INIT_WORK(&priv->irq_work, enc28j60_irq_work_handler);

你可能感兴趣的:(Linux,Kernel)