分享

linux网络设备应用与驱动编程学习4——模板与实例(A)

 WUCANADA 2012-12-14

linux网络设备应用与驱动编程学习4——模板与实例(A)

分类: 驱动笔记 498人阅读 评论(0) 收藏 举报
 

源码来自lpc32xx_mii.c

1.       模块初始化卸载

static int __init lpc32xx_net_init(void)

{

return platform_driver_register(&lpc32xx_net_driver);

}

 

static void __exit lpc32xx_net_cleanup(void)

{

platform_driver_unregister(&lpc32xx_net_driver);

}

2.       平台驱动相关方法

static struct platform_driver lpc32xx_net_driver = {

.probe            = lpc32xx_net_drv_probe,

.remove          = __devexit_p(lpc32xx_net_drv_remove),

.suspend  = lpc32xx_net_drv_suspend,

.resume          = lpc32xx_net_drv_resume,

.driver            = {

        .name      = MODNAME,

},

};

3.       probe方法分析

static int lpc32xx_net_drv_probe(struct platform_device *pdev)

{

struct resource *res;

struct net_device *ndev;

struct netdata_local *pldat;

struct phy_device *phydev;

dma_addr_t dma_handle;

int irq, ret;

 

第一步:从平台上获取资源信息

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

irq = platform_get_irq(pdev, 0);

/*static struct resource net_resources[] = {

[0] = {

        .start       = ETHERNET_BASE,

        .end = ETHERNET_BASE + SZ_4K - 1,

        .flags      = IORESOURCE_MEM,

},

 

[1] = {

        .start       = IRQ_ETHERNET,

        .end = IRQ_ETHERNET,

        .flags      = IORESOURCE_IRQ,

},

};*/

if ((!res) || (irq < 0) || (irq >= NR_IRQS))

{

        dev_err(&pdev->dev, "error getting resources.\n");

        ret = -ENXIO;

        goto err_exit;

}

 

第二步:分配和初始化net_device结构,这一步也可放在模块初始化中完成

ndev = alloc_etherdev(sizeof(struct netdata_local));

/*alloc_etherdev函数最终调用alloc_netdev_mq(sizeof_priv, "eth%d", ether_setup, queue_count);函数*/

if (!ndev) {

        dev_err(&pdev->dev, "could not allocate device.\n");

        ret = -ENOMEM;

        goto err_exit;

}

 

SET_NETDEV_DEV(ndev, &pdev->dev);

/*将ndev的父设备指向平台设备。即将ndev设备挂到平台设备表里*/

 

pldat = netdev_priv(ndev);

//获得ndev的私有指针,指针内的成员由驱动作者自己定义。

pldat->pdev = pdev;

pldat->ndev = ndev;

 

spin_lock_init(&pldat->lock);

/* Save resources */

pldat->net_region_start = res->start;

pldat->net_region_size = res->end - res->start + 1;

ndev->irq = irq;

 

//第三步:申请硬件资源

/* Get clock for the device */

pldat->clk = clk_get(&ndev->dev, "net_ck");

if (IS_ERR(pldat->clk)) {

        ret = PTR_ERR(pldat->clk);

        goto err_out_free_dev;

}

/*以上为初始化私有结构体*/

 

/* Enable network clock */

__lpc32xx_net_clock_enable(pldat, 1);

//使能时钟

/* Map IO space */

pldat->net_base = ioremap(pldat->net_region_start, pldat->net_region_size);

if (!pldat->net_base)

{

        dev_err(&pdev->dev, "failed to map registers, aborting.\n");

        ret = -ENOMEM;

        goto err_out_disable_clocks;

}

//将网卡物理空间动态映射到内核空间。

ret = request_irq(ndev->irq, __lpc32xx_eth_interrupt, 0,

                 ndev->name, ndev);

if (ret) {

        printk(KERN_ERR

               "%s: Unable to request IRQ %d (error %d)\n",

               ndev->name, ndev->irq, ret);

        goto err_out_iounmap;

}

//申请中断

 

 

//第四步:设备操作接口初始化

/* Fill in the fields of the device structure with ethernet values. */

ether_setup(ndev);

/*ndev申请完之后并没有初始化,ether_setup()函数就是完成ndev有关于以太网确定成员的初始化*/

/*probe的以上部分完成了网络设备驱动的“网络设备接口层”的工作,以下对设备操作函数的具体实现便是“设备驱动功能层”的事情,即网络设备驱动的主体工作*/

/* Setup driver functions */

ndev->open = lpc32xx_net_open;

ndev->stop = lpc32xx_net_close;

//设备打开与关闭时调用

ndev->hard_start_xmit = lpc32xx_net_hard_start_xmit;

//设备数据发送时调用

ndev->tx_timeout = lpc32xx_net_timeout;

//发送超时调用

ndev->watchdog_timeo = msecs_to_jiffies(watchdog);

//设定超时时间,单位jiffies

ndev->set_multicast_list = lpc32xx_net_set_multicast_list;

/*当设备的组播列表改变或设备标志改变时调用*/

ndev->ethtool_ops = &lpc32xx_net_ethtool_ops;

//结构体中的成员用于更改或报告网络设备的设置

ndev->do_ioctl = &lpc32xx_net_ioctl;

//设备特定的I/O控制

#ifdef CONFIG_NET_POLL_CONTROLLER

ndev->poll_controller = lpc32xx_net_poll_controller;

//支持纯粹的netconsole(用于kgdb调试),它以轮询方式接收数据包

#endif

ndev->base_addr = pldat->net_region_start;

// 继续初始化ndev:虚拟基地址

 

//第五步:其它及DMA初始化

/* Save board specific configuration */

pldat->ncfg = (struct lpc32xx_net_cfg *) pdev->dev.platform_data;

/* .platform_data     = &lpc32xx_netdata,

 

 struct lpc32xx_net_cfg lpc32xx_netdata =

{

.get_mac_addr       = &return_mac_address,

.phy_irq  = -1,

.phy_mask      = 0xFFFFFFF0,

 

};

*/

if (pldat->ncfg == NULL)

{

        printk(KERN_INFO "%s : WARNING: No board MAC address provided\n",

               ndev->name);

        pldat->ncfg = &__lpc32xx_local_net_config;

}

 

/* Get size of DMA buffers/descriptors region */

pldat->dma_buff_size = (ENET_TX_DESC + ENET_RX_DESC) * (ENET_MAXF_SIZE +

        sizeof(struct txrx_desc_t) + sizeof(struct rx_status_t));

/*计算DMA缓冲区所需要的空间,DMA空间包括帧片断(描述符)数组大小,状态大小,帧片断数量*/

#if defined(CONFIG_MACH_LPC32XX_IRAM_FOR_NET)

pldat->dma_buff_base_v = (u32) io_p2v(IRAM_BASE);

dma_handle = (dma_addr_t) IRAM_BASE;

//初始化DMA缓冲区的基地址

#else

pldat->dma_buff_size += 4096; /* Allows room for alignment */

 

/* Align on the next highest page entry size */

pldat->dma_buff_size &= 0Xfffff000;

pldat->dma_buff_size += 0X00001000;

//如果DMA缓冲区不在内部RAM中,则进行页对齐

/* Allocate a chunk of memory for the DMA ethernet buffers and descriptors */

pldat->dma_buff_base_v = (u32) dma_alloc_coherent(&pldat->pdev->dev, pldat->dma_buff_size,

        &dma_handle, GFP_KERNEL);

#endif

//申请一致性缓冲区,初始化DMA缓冲区的基地址

if (pldat->dma_buff_base_v == (u32) NULL)

{

        dev_err(&pdev->dev, "error getting DMA region.\n");

        ret = -ENOMEM;

        goto err_out_free_irq;

}

pldat->dma_buff_base_p = (u32) dma_handle;

 

#ifdef NET_DEBUG

printk(KERN_INFO "Ethernet net MAC resources\n");

printk(KERN_INFO "IO address start     :0x%08x\n", (u32) pldat->net_region_start);

printk(KERN_INFO "IO address size      :%d\n", (u32) pldat->net_region_size);

printk(KERN_INFO "IO address (mapped)  :0x%08x\n", (u32) pldat->net_base);

printk(KERN_INFO "IRQ number           :%d\n", ndev->irq);

printk(KERN_INFO "DMA buffer size      :%d\n", pldat->dma_buff_size);

printk(KERN_INFO "DMA buffer P address :0x%08x\n", pldat->dma_buff_base_p);

printk(KERN_INFO "DMA buffer V address :0x%08x\n", pldat->dma_buff_base_v);

#endif

 

/* Get the board MAC address */

if (pldat->ncfg->get_mac_addr != NULL)

{

        ret = pldat->ncfg->get_mac_addr(ndev->dev_addr);

//在探测阶段先随便指定一个mac完成初始化

        if (ret)

        {

               /* Mac address load error */

               goto err_out_dma_unmap;

        }

}

 

if (!is_valid_ether_addr(ndev->dev_addr))

{

        printk(KERN_INFO "%s: Invalid ethernet MAC address.  Please "

               "set using ifconfig\n", ndev->name);

}

 

第六步:以太网控制器相关

/* Reset the ethernet controller */

__lpc32xx_eth_reset(pldat);

//__lpc32xx_eth_reset()函数是一些读写寄存器构成

 

/* then shut everything down to save power */

__lpc32xx_net_shutdown(pldat);

 

/* Set default parameters */

pldat->msg_enable = NETIF_MSG_LINK;

 

/* Force an MII interface reset and clock setup */

__lpc32xx_mii_mngt_reset(pldat);

 

/* Force default PHY interface setup in chip, this will probably be

   changed by the PHY driver */

pldat->link = 0;

pldat->speed = 100;

pldat->duplex = DUPLEX_FULL;

__lpc32xx_params_setup(pldat);

//__lpc32xx_params_setup()函数就是根据pldat的speed,duplex设置完成相应寄存器设置。

 

ret = register_netdev(ndev);

//以上代码主要就是完成了ndev及其私有指针pldat指向结构的部分初始化

if (ret) {

        dev_err(&pdev->dev, "Cannot register net device, aborting.\n");

        goto err_out_dma_unmap;

}

platform_set_drvdata(pdev, ndev);

//将ndev作为pdev->drvdata,方便pdev与ndev之间结构信息共享

 

if (lpc32xx_mii_init(pldat) != 0) {

        goto err_out_unregister_netdev;

}

// lpc32xx_mii_init()完成mii接口的初始化见“lpc32xx_mii_init()函数分析”

 

 

printk(KERN_INFO "%s: LPC32XX mac at 0x%08lx irq %d\n",

       ndev->name, ndev->base_addr, ndev->irq);

 

//最后初始化了一个总线设备。下面的是一些错误处理。

phydev = pldat->phy_dev;

printk(KERN_INFO "%s: attached PHY driver [%s] "

        "(mii_bus:phy_addr=%s, irq=%d)\n",

        ndev->name, phydev->drv->name, phydev->dev.bus_id, phydev->irq);

 

return 0;

 

err_out_unregister_netdev:

platform_set_drvdata(pdev, NULL);

unregister_netdev(ndev);

err_out_dma_unmap:

dma_free_coherent(&pldat->pdev->dev, pldat->dma_buff_size,

        (void *) pldat->dma_buff_base_v, (dma_addr_t) pldat->dma_buff_base_p);

err_out_free_irq:

free_irq(ndev->irq, ndev);

err_out_iounmap:

iounmap(pldat->net_base);

err_out_disable_clocks:

clk_disable(pldat->clk);

clk_put(pldat->clk);

err_out_free_dev:

free_netdev(ndev);

err_exit:

printk("%s: not found (%d).\n", MODNAME, ret);

return ret;

}

 

总结一下lpc32xx_net_drv_probe()函数:首先根据平台设备的 resource结构获得空间和中断信息,并利用这些作息初始化申请的net_device结构体,再向内核申请这些资源。再次,填充ndev的设备操作 函数成员,让内核得到一些控制网络传输的方法。接着,根据芯片特点,申请了DMA缓冲区,初始化了mac。而后便是初始化以太网控制器及其与phy的数据 交互接口mii,最后是一些错误处理。可以说一个probe方法完成了整个网络设备驱动的构架工作。

 

 

lpc32xx_mii_init()函数分析

static int lpc32xx_mii_init(struct netdata_local *pldat)

{

       int err = -ENXIO, i;

 

       /* Setup MII mode */

#if defined (MAC_LPC32XX_MII_SUPPORT)

       __raw_writel(COMMAND_PASSRUNTFRAME, ENET_COMMAND(pldat->net_base));

#else

       __raw_writel((COMMAND_PASSRUNTFRAME | COMMAND_RMII),

              ENET_COMMAND(pldat->net_base));

       __raw_writel(SUPP_RESET_RMII, ENET_SUPP(pldat->net_base));

#endif

 

       pldat->mii_bus.name = "LPC32XX_mii_bus";

       pldat->mii_bus.read = &lpc32xx_mdio_read;

       pldat->mii_bus.write = &lpc32xx_mdio_write;

       pldat->mii_bus.reset = &lpc32xx_mdio_reset;

       snprintf(pldat->mii_bus.id, MII_BUS_ID_SIZE, "%x", pldat->pdev->id);

       pldat->mii_bus.priv = pldat;

       pldat->mii_bus.dev = &pldat->ndev->dev;

       pldat->mii_bus.phy_mask = 0xFFFFFFF0;

/*在plat的结构中,mii_bus是一种PHY设备挂接的总线.该总线介于mac于phy之间,以上是它的初始化。该总线提供了read,write,reset方法,有点像字符设备中fop提供的方法*/

 

       if (pldat->ncfg)

       {

              pldat->mii_bus.phy_mask = pldat->ncfg->phy_mask;

       }

 

       pldat->mii_bus.irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);

       if (!pldat->mii_bus.irq) {

              err = -ENOMEM;

              goto err_out;

       }

 

       for (i = 0; i < PHY_MAX_ADDR; i++)

       {

              pldat->mii_bus.irq[i] = PHY_POLL;

       }

/*申请了一片断区域,并初始化。PHY_MAX_ADDR代表总线能接受的设备数量

一个设备将来对就这里申请的一个中断位置*/

       platform_set_drvdata(pldat->ndev, &pldat->mii_bus);

 

//像这样的函数,个人以为,只为作指针引用方便

       if (mdiobus_register(&pldat->mii_bus))

       {

              goto err_out_free_mdio_irq;

       }

 

       if (lpc32xx_mii_probe(pldat->ndev) != 0)

       {

              goto err_out_unregister_bus;

       }

 

       return 0;

 

err_out_unregister_bus:

       mdiobus_unregister(&pldat->mii_bus);

err_out_free_mdio_irq:

       kfree(pldat->mii_bus.irq);

err_out:

       return err;

}

 

追踪(mdiobus_register(&pldat->mii_bus)

int mdiobus_register(struct mii_bus *bus)

{

       int i;

       int err = 0;

 

       if (NULL == bus || NULL == bus->name ||

                     NULL == bus->read ||

                     NULL == bus->write)

              return -EINVAL;

//检查总线是否被初始化

 

       mutex_init(&bus->mdio_lock);

 

       if (bus->reset)

              bus->reset(bus);

 

       for (i = 0; i < PHY_MAX_ADDR; i++) {

              struct phy_device *phydev;

 

              if (bus->phy_mask & (1 << i)) {

                     bus->phy_map[i] = NULL;

                     continue;

              }

 

              phydev = get_phy_device(bus, i);

 

              if (IS_ERR(phydev))

                     return PTR_ERR(phydev);

 

              /* There's a PHY at this address

               * We need to set:

               * 1) IRQ

               * 2) bus_id

               * 3) parent

               * 4) bus

               * 5) mii_bus

               * And, we need to register it */

              if (phydev) {

                     phydev->irq = bus->irq[i];

 

                     phydev->dev.parent = bus->dev;

/*pldat->mii_bus.dev = &pldat->ndev->dev; 这说明phydev的父设备是ndev,即物理层PHY设备的父设备为MAC设备*/

                     phydev->dev.bus = &mdio_bus_type;

                     snprintf(phydev->dev.bus_id, BUS_ID_SIZE, PHY_ID_FMT, bus->id, i);

 

                     phydev->bus = bus;

 

                     /* Run all of the fixups for this PHY */

                     phy_scan_fixups(phydev);

 

                     err = device_register(&phydev->dev);

 

                     if (err) {

                            printk(KERN_ERR "phy %d failed to register\n",

                                          i);

                            phy_device_free(phydev);

                            phydev = NULL;

                     }

              }

 

              bus->phy_map[i] = phydev;

       }

 

       pr_info("%s: probed\n", bus->name);

 

       return err;

}

EXPORT_SYMBOL(mdiobus_register);

/*经过以上源代码分析,可以看出mdiobus_register()函数为总线上所有设备进行了设置,并注册进了设备模型。从名子上看是总线注册,实际是总线上的设备注册。*/

 

追踪lpc32xx_mii_probe()

static int lpc32xx_mii_probe(struct net_device *ndev)

{

       struct netdata_local *pldat = netdev_priv(ndev);

       struct phy_device *phydev = NULL;

       int phy_addr;

 

       /* find the first phy */

       for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++)

       {

              if (pldat->mii_bus.phy_map[phy_addr])

              {

                     phydev = pldat->mii_bus.phy_map[phy_addr];

                     break;

              }

       }

 

       if (!phydev)

       {

              printk (KERN_ERR "%s: no PHY found\n", ndev->name);

              return -1;

       }

 

       /* Attach to the PHY */

#if defined (MAC_LPC32XX_MII_SUPPORT)

       phydev = phy_connect(ndev, phydev->dev.bus_id,

              &lpc32xx_handle_link_change, 0, PHY_INTERFACE_MODE_MII);

#else

       phydev = phy_connect(ndev, phydev->dev.bus_id,

              &lpc32xx_handle_link_change, 0, PHY_INTERFACE_MODE_RMII);

#endif

 

       if (IS_ERR(phydev))

       {

              printk(KERN_ERR "%s: Could not attach to PHY\n", ndev->name);

              return PTR_ERR(phydev);

       }

 

       /* mask with MAC supported features */

       phydev->supported &= PHY_BASIC_FEATURES;

 

       phydev->advertising = phydev->supported;

 

       pldat->link = 0;

       pldat->speed = 0;

       pldat->duplex = -1;

       pldat->phy_dev = phydev;

 

       return 0;

}

lpc32xx_mii_probe()完成了这样一个事情:找到第一个phy设备,然后根据内核配置,选用MII接口或RMII接口,之后再进行简单的配置。这个探测方法完成的是phy设备的探测。

总结一下:

lpc32xx_mii_init()函数完成的是mii接口的初始化,包括注册注册mii_bus总线上的设备,然后根据找到总线上第一个phy设备进行一些初始化设置。


linux网络设备应用与驱动编程学习4——模板与实例(B)——打开和释放方法

分类: 驱动笔记 142人阅读 评论(0) 收藏 举报
 

l         open方法

static int lpc32xx_net_open(struct net_device *ndev)

{

       struct netdata_local *pldat = netdev_priv(ndev);

 

       /* if the phy is not yet registered, retry later*/

       if (!pldat->phy_dev)

       {

              return -EAGAIN;

       }

 

       if (netif_msg_ifup(pldat))

       {

              dev_dbg(&pldat->pdev->dev, "enabling %s\n", ndev->name);

       }

 

       if (!is_valid_ether_addr(ndev->dev_addr))

       {

              return -EADDRNOTAVAIL;

       }

 

       __lpc32xx_net_clock_enable(pldat, 1);

 

       /* Reset and initialize */

       __lpc32xx_eth_reset(pldat);

       __lpc32xx_eth_init(pldat);

 

       /* schedule a link state check */

       phy_start(pldat->phy_dev);

// 更换PHY状态,启动PHY

       netif_start_queue(ndev);

//激活发送队列

       return 0;

}

总结:open中最重要的两个函数是__lpc32xx_eth_init()和netif_start()一个做数据处理前的准备工作,另一个激活发送队列

 

追踪__lpc32xx_eth_init

static void __lpc32xx_eth_init(struct netdata_local *pldat)

{

       u32 tmp;

 

       /* Disable controller and reset */

       tmp = __raw_readl(ENET_COMMAND(pldat->net_base));

       tmp &= ~COMMAND_RXENABLE | COMMAND_TXENABLE;

       __raw_writel(tmp, ENET_COMMAND(pldat->net_base));

       tmp = __raw_readl(ENET_MAC1(pldat->net_base));

       tmp &= ~MAC1_RECV_ENABLE;

       __raw_writel(tmp, ENET_MAC1(pldat->net_base));

/*在命令寄存器中禁能接收和发送,然后又在MAC1中禁止接收帧*/

 

 

       /* Initial MAC setup */

       __raw_writel(MAC1_PASS_ALL_RX_FRAMES, ENET_MAC1(pldat->net_base));

       __raw_writel((MAC2_PAD_CRC_ENABLE | MAC2_CRC_ENABLE),

              ENET_MAC2(pldat->net_base));

       __raw_writel(ENET_MAXF_SIZE, ENET_MAXF(pldat->net_base));

/*在MAC初始化配置:传递所有帧,每帧上添加CRC,MAC填充所有短帧,最大帧长度为1536个字节*/

 

       /* Collision window, gap */

       __raw_writel((CLRT_LOAD_RETRY_MAX(0xF) |

              CLRT_LOAD_COLLISION_WINDOW(0x37)), ENET_CLRT(pldat->net_base));

       __raw_writel(IPGR_LOAD_PART2(0x12), ENET_IPGR(pldat->net_base));

/*冲突窗口/重试寄存器,冲突之后重复发送次数15次,冲突窗口(导言区和SFD)之后有56个字节窗口;非背对背的内部包间隔寄存器:包间隔为18*/

 

#if defined (MAC_LPC32XX_MII_SUPPORT)

       __raw_writel(COMMAND_PASSRUNTFRAME, ENET_COMMAND(pldat->net_base));

//MII模式,小于64字节的短帧传递到寄存器

#else

       __raw_writel((COMMAND_PASSRUNTFRAME | COMMAND_RMII),

              ENET_COMMAND(pldat->net_base));

       __raw_writel(SUPP_RESET_RMII, ENET_SUPP(pldat->net_base));

#endif

/*RMII模式,100Mbps模式*/

       __lpc32xx_params_setup(pldat);

 

       /* Setup TX and RX descriptors */

       __lpc32xx_txrx_desc_setup(pldat);

//建立描述符,这是接收和发送过程的第一步

 

       /* Setup packet filtering */

       __raw_writel((RXFLTRW_ACCEPTUBROADCAST | RXFLTRW_ACCEPTPERFECT),

              ENET_RXFILTER_CTRL(pldat->net_base));

 

       /* Clear and enable interrupts */

       __raw_writel(0xFFFF, ENET_INTCLEAR(pldat->net_base));

       __raw_writel((MACINT_RXDONEINTEN | MACINT_TXDONEINTEN),

              ENET_INTENABLE(pldat->net_base));

//设置过滤寄存器和使能中断

 

       /* Get the next TX buffer output index */

       pldat->num_used_tx_buffs = 0;

       pldat->last_tx_idx =

              __raw_readl(ENET_TXCONSUMEINDEX(pldat->net_base));

 

       /* Enable controller */

       tmp = __raw_readl(ENET_COMMAND(pldat->net_base));

       tmp |= COMMAND_RXENABLE | COMMAND_TXENABLE;

       __raw_writel(tmp, ENET_COMMAND(pldat->net_base));

       tmp = __raw_readl(ENET_MAC1(pldat->net_base));

       tmp |= MAC1_RECV_ENABLE;

       __raw_writel(tmp, ENET_MAC1(pldat->net_base));

}

/*使能发送和接收通道。接收时还要使能MAC。这是接收和发送过程的第二步*/

总结:__lpc32xx_eth_init()函数完成了以太网模块工作方式的初始化:工作模式,速度,帧格式,建立描述符,使能接收和发送通道。这些都是接收数据前的准备工作。

 

追踪__lpc32xx_params_setup(pldat);

static void __lpc32xx_params_setup(struct netdata_local *pldat)

{

       u32 tmp;

 

       if (pldat->duplex == DUPLEX_FULL)

       {

              tmp = __raw_readl(ENET_MAC2(pldat->net_base));

              tmp |= MAC2_FULL_DUPLEX;

              __raw_writel(tmp, ENET_MAC2(pldat->net_base));

//配置为全双工模式

              tmp = __raw_readl(ENET_COMMAND(pldat->net_base));

              tmp |= COMMAND_FULLDUPLEX;

              __raw_writel(tmp, ENET_COMMAND(pldat->net_base));

//全双工模式下操作

              __raw_writel(IPGT_LOAD(0x15), ENET_IPGT(pldat->net_base));

//包结尾与包开始的时间间隔

       }

       else

       {

              tmp = __raw_readl(ENET_MAC2(pldat->net_base));

              tmp &= ~MAC2_FULL_DUPLEX;

              __raw_writel(tmp, ENET_MAC2(pldat->net_base));

              tmp = __raw_readl(ENET_COMMAND(pldat->net_base));

              tmp &= ~COMMAND_FULLDUPLEX;

              __raw_writel(tmp, ENET_COMMAND(pldat->net_base));

              __raw_writel(IPGT_LOAD(0x12), ENET_IPGT(pldat->net_base));

       }

//或者设置成半双工模式

 

       if (pldat->speed == SPEED_100)

       {

              __raw_writel(SUPP_SPEED, ENET_SUPP(pldat->net_base));

       }

       else

       {

              __raw_writel(0, ENET_SUPP(pldat->net_base));

       }

}

//RMII的附加设置,设置速度为10Mbps或100Mbps

 

追踪__lpc32xx_txrx_desc_setup(pldat);

static void __lpc32xx_txrx_desc_setup(struct netdata_local *pldat)

{

       u32 tbuff, *ptxstat;

       int i;

       struct txrx_desc_t *ptxrxdesc;

       struct rx_status_t *prxstat;

// txrx_desc_t结构用来描述帧片断描述符的,rx_status_t结构用来描述描述符态的

 

       tbuff = __ptr_align(pldat->dma_buff_base_v);

//16字节对齐tbuff

 

       /* Setup TX descriptors, status, and buffers */

       for (i = 0; i < ENET_TX_DESC; i++)

       {

              pldat->tx_desc_v [i] = tbuff;

              tbuff += sizeof(struct txrx_desc_t);

       }

       for (i = 0; i < ENET_TX_DESC; i++)

       {

              pldat->tx_stat_v [i] = tbuff;

              tbuff += sizeof(u32);

       }

       tbuff = __ptr_align(tbuff);

       for (i = 0; i < ENET_TX_DESC; i++)

       {

              pldat->tx_buff_v [i] = tbuff;

              tbuff += ENET_MAXF_SIZE;

       }

 

       /* Setup RX descriptors, status, and buffers */

       for (i = 0; i < ENET_RX_DESC; i++)

       {

              pldat->rx_desc_v [i] = tbuff;

              tbuff += sizeof(struct txrx_desc_t);

       }

       tbuff = __ptr_align(tbuff);

       for (i = 0; i < ENET_RX_DESC; i++)

       {

              pldat->rx_stat_v [i] = tbuff;

              tbuff += sizeof(struct rx_status_t);

       }

       tbuff = __ptr_align(tbuff);

       for (i = 0; i < ENET_RX_DESC; i++)

       {

              pldat->rx_buff_v [i] = tbuff;

              tbuff += ENET_MAXF_SIZE;

       }

/*以上内容分别给发送和接收描述符,状态,相关数据区做了空间分配*/

 

 

       /* Map the TX descriptors to the TX buffers in hardware */

       for (i = 0; i < ENET_TX_DESC; i++)

       {

              ptxstat = (u32 *) pldat->tx_stat_v [i];

              ptxrxdesc = (struct txrx_desc_t *) pldat->tx_desc_v [i];

 

              ptxrxdesc->packet = __va_to_pa(pldat->tx_buff_v [i], pldat);

              ptxrxdesc->control = 0;

              *ptxstat = 0;

       }

 

       /* Map the RX descriptors to the RX buffers in hardware */

       for (i = 0; i < ENET_RX_DESC; i++)

       {

              prxstat = (struct rx_status_t *) pldat->rx_stat_v [i];

              ptxrxdesc = (struct txrx_desc_t *) pldat->rx_desc_v [i];

 

              ptxrxdesc->packet = __va_to_pa(pldat->rx_buff_v [i], pldat);

              ptxrxdesc->control = 0x80000000 | (ENET_MAXF_SIZE - 1);

              prxstat->statusinfo = 0;

              prxstat->statushashcrc = 0;

       }

       /* Setup base addresses in hardware to point to buffers and descriptors */

__raw_writel((ENET_TX_DESC - 1), ENET_TXDESCRIPTORNUMBER(pldat->net_base));

       __raw_writel(__va_to_pa(pldat->tx_desc_v [0], pldat), ENET_TXDESCRIPTOR(pldat->net_base));

       __raw_writel(__va_to_pa(pldat->tx_stat_v [0], pldat), ENET_TXSTATUS(pldat->net_base));

       __raw_writel((ENET_RX_DESC - 1), ENET_RXDESCRIPTORNUMBER(pldat->net_base));

       __raw_writel(__va_to_pa(pldat->rx_desc_v [0], pldat), ENET_RXDESCRIPTOR(pldat->net_base));

       __raw_writel(__va_to_pa(pldat->rx_stat_v [0], pldat), ENET_RXSTATUS(pldat->net_base));

}

/*初始化了描述符和状态寄存器的数值,然后写入相应寄存器,packet指向的是一个物理地址*/

总结:正如函数的名字__lpc32xx_txrx_desc_setup,它的作用就是建立描述符,和相关状态初始化。

 

static int lpc32xx_net_close(struct net_device *ndev)

{

       unsigned long flags;

       struct netdata_local *pldat = netdev_priv(ndev);

 

       if (netif_msg_ifdown(pldat))

       {

              dev_dbg(&pldat->pdev->dev, "shutting down %s\n", ndev->name);

       }

 

       netif_stop_queue(ndev);

 

       if (pldat->phy_dev)

       {

              phy_stop(pldat->phy_dev);

       }

 

       spin_lock_irqsave(&pldat->lock, flags);

       __lpc32xx_eth_reset(pldat);

       netif_carrier_off(ndev);

       __raw_writel(0, ENET_MAC1(pldat->net_base));

       __raw_writel(0, ENET_MAC2(pldat->net_base));

       spin_unlock_irqrestore(&pldat->lock, flags);

 

       __lpc32xx_net_clock_enable(pldat, 0);

 

       return 0;

}

和open方法相反,调用netif_stop_queue()停止传输包,重启以太网模块,改变设备连接状态,停止时钟。

linux网络设备应用与驱动编程学习4——模板与实例(C)——其它方法

分类: 驱动笔记 188人阅读 评论(0) 收藏 举报
 

为了便于分析,贴出对应的方法

ndev->open = lpc32xx_net_open;

       ndev->stop = lpc32xx_net_close;

       ndev->hard_start_xmit = lpc32xx_net_hard_start_xmit;

       ndev->tx_timeout = lpc32xx_net_timeout;

       ndev->watchdog_timeo = msecs_to_jiffies(watchdog);

       ndev->set_multicast_list = lpc32xx_net_set_multicast_list;

       ndev->ethtool_ops = &lpc32xx_net_ethtool_ops;

       ndev->do_ioctl = &lpc32xx_net_ioctl;

#ifdef CONFIG_NET_POLL_CONTROLLER

       ndev->poll_controller = lpc32xx_net_poll_controller;

 

n         hard_start_xmit方法(发送数据时调用)

static int lpc32xx_net_hard_start_xmit(struct sk_buff *skb, struct net_device *ndev)

{

       struct netdata_local *pldat = netdev_priv(ndev);

       unsigned int len, txidx;

       u32 *ptxstat;

       struct txrx_desc_t *ptxrxdesc;

 

       len = skb->len;

//skb的大小

 

       spin_lock_irq(&pldat->lock);

 

       if (pldat->num_used_tx_buffs >= (ENET_TX_DESC - 1))

       {

              /* This function should never be called when there are no

                 buffers, log the error */

              netif_stop_queue(ndev);

              spin_unlock_irq(&pldat->lock);

              dev_err(&pldat->pdev->dev,

                     "BUG! TX request when no free TX buffers!\n");

              return 1;

       }

 

       /* Get the next TX descriptor index */

       txidx = __raw_readl(ENET_TXPRODUCEINDEX(pldat->net_base));

//获得发送描述符的生产者索引

 

       /* Setup control for the transfer */

       ptxstat = (u32 *) pldat->tx_stat_v [txidx];

       *ptxstat = 0;

//将描述符状态写为0  

ptxrxdesc = (struct txrx_desc_t *) pldat->tx_desc_v [txidx];

       ptxrxdesc->control = (len - 1) | 0xC0000000;

//描述符的控制字,写入skb的长度,最后希望发送完成后,产生中断。

 

       /* Copy data to the DMA buffer */

       memcpy((void *) pldat->tx_buff_v [txidx], skb->data, len);

//将skb拷贝到DMA的数据缓冲区中。

       /* Save the buffer and increment the buffer counter */

       pldat->skb[txidx] = skb;

       pldat->num_used_tx_buffs++;

 

       /* Start transmit */

       txidx++;

       if (txidx >= ENET_TX_DESC)

       {

              txidx = 0;

       }

//生产者索引到最大值时应回0

       __raw_writel((u32) txidx, ENET_TXPRODUCEINDEX(pldat->net_base));

//更新发送描述符的生产者索引应由软件完成。

 

       /* Stop queue if no more TX buffers */

       if (pldat->num_used_tx_buffs >= (ENET_TX_DESC - 1))

       {

              netif_stop_queue(ndev);

       }

 

       spin_unlock_irq(&pldat->lock);

       ndev->trans_start = jiffies;

//记录发送的时间戳

 

       return 0;

}

这个函数只发送了一个描述符,然后更新了一次发送生产者索引,一个描述符对应一个skb

n         tx_timeout(发送超时时调用)

static void lpc32xx_net_timeout(struct net_device *ndev)

{

       struct netdata_local *pldat = netdev_priv(ndev);

 

       /* This should never happen and indicates a problem */

       dev_err(&pldat->pdev->dev, "BUG! TX timeout occurred!\n");

}

这个函数从实现的结果看,它只是调试方便才实现的。

n         watchdog_timeo(设置超时时间)

n         set_multicast_list(组播列表改变或设备标志改变时)

static void lpc32xx_net_set_multicast_list(struct net_device *ndev)

{

       struct netdata_local *pldat = netdev_priv(ndev);

       struct dev_mc_list *mcptr = ndev->mc_list;

//组播mac地址

       int i, mc_cnt = ndev->mc_count;

//组播(地址)数

       u32 tmp32, hash_val, hashlo, hashhi;

       unsigned long flags;

 

       spin_lock_irqsave(&pldat->lock, flags);

 

       /* Set station address */

       __lpc32xx_set_mac(pldat, ndev->dev_addr);

//将dev_addr中的MAC地址存入站寄存器

 

       tmp32 =  RXFLTRW_ACCEPTUBROADCAST | RXFLTRW_ACCEPTPERFECT;

 

       if (ndev->flags & IFF_PROMISC)

       {

              tmp32 |= RXFLTRW_ACCEPTUNICAST | RXFLTRW_ACCEPTUNICASTHASH |

                     RXFLTRW_ACCEPTUMULTICASTHASH;

       }

       if (ndev->flags & IFF_ALLMULTI)

       {

              tmp32 |= RXFLTRW_ACCEPTUMULTICAST;

       }

       __raw_writel(tmp32, ENET_RXFILTER_CTRL(pldat->net_base));

//设置滤波存器

 

 

       /* Set initial hash table */

       hashlo = 0x0;

       hashhi = 0x80000000;

 

       /* 64 bits : multicast address in hash table */

       for (i = 0; i < mc_cnt; i++, mcptr = mcptr->next)

       {

              hash_val = ether_crc_le(6, mcptr->dmi_addr) & 0x3f;

 

              if (hash_val >= 32)

              {

                     hashhi |= 1 << (32 - hash_val);

              }

              else

              {

                     hashlo |= 1 << hash_val;

              }

       }

 

       __raw_writel(hashlo, ENET_HASHFILTERL(pldat->net_base));

       __raw_writel(hashhi, ENET_HASHFILTERH(pldat->net_base));

//填充Hash滤波器表

 

       spin_unlock_irqrestore(&pldat->lock, flags);

}

总结:这个函数重填了MAC地址,根据标志重设了滤波寄存器,重填了 hash滤波表。

n         ethtool_ops(更改或报告网络设备的设置)

static const struct ethtool_ops lpc32xx_net_ethtool_ops = {

       .get_drvinfo    = lpc32xx_net_ethtool_getdrvinfo,

       .get_settings   = lpc32xx_net_ethtool_getsettings,

       .set_settings    = lpc32xx_net_ethtool_setsettings,

       .get_msglevel  = lpc32xx_net_ethtool_getmsglevel,

       .set_msglevel  = lpc32xx_net_ethtool_setmsglevel,

       .get_link  = ethtool_op_get_link,

};

lpc32xx_net_ethtool_getdrvinfo,用于获得驱动的信息,如驱动版本,总线等

lpc32xx_net_ethtool_getsettings,lpc32xx_net_ethtool_setsettings分别用于获得和设置一些信息。视命令而定

ethtool_op_get_link,获知网络连接状态的

 

 

除了以上方法,还有中断

static irqreturn_t __lpc32xx_eth_interrupt(int irq, void *dev_id)

{

       struct net_device *ndev = dev_id;

       struct netdata_local *pldat = netdev_priv(ndev);

       u32 tmp;

 

       spin_lock(&pldat->lock);

 

       /* Get the interrupt status */

       tmp = __raw_readl(ENET_INTSTATUS(pldat->net_base));

/*每当发生中断时,执行此函数,此函数先查询中断状态寄存器*/

       while (tmp)

       {

              /* Clear interrupts */

              __raw_writel(tmp, ENET_INTCLEAR(pldat->net_base));

 

              /* Transmit complete? */

              if (tmp & (MACINT_TXUNDERRUNINTEN | MACINT_TXERRORINTEN |

                     MACINT_TXFINISHEDINTEN | MACINT_TXDONEINTEN))

/*如果发生了重大的溢出错误,发送出现错误,单个或所有描述符发送完成都会调用以下函数*/

              {

                     __lpc32xx_handle_xmit(ndev);

              }

 

              /* Receive buffer available */

              if (tmp & (MACINT_RXOVERRUNINTEN | MACINT_RXERRORONINT |

                     MACINT_RXFINISHEDINTEN | MACINT_RXDONEINTEN))

              {

                     __lpc32xx_handle_recv(ndev);

              }

 

              /* Recheck the interrupt status */

              tmp = __raw_readl(ENET_INTSTATUS(pldat->net_base));

       }

 

       spin_unlock(&pldat->lock);

 

       return IRQ_HANDLED;

}

函数处理了发送与接收中断。

 

 

追踪__lpc32xx_handle_xmit

static void __lpc32xx_handle_xmit(struct net_device *ndev)

{

       struct netdata_local *pldat = netdev_priv(ndev);

       struct sk_buff *skb;

       unsigned int txcidx, *ptxstat, txstat;

 

       txcidx = __raw_readl(ENET_TXCONSUMEINDEX(pldat->net_base));

//获得发送消费者索引(要发送的描述符)

       while (pldat->last_tx_idx != txcidx)

       {

              skb = (struct sk_buff *) pldat->skb[pldat->last_tx_idx];

//获得最近已发送描述符对应的skb

              /* A buffer is available, get buffer status */

              ptxstat = (unsigned int *) pldat->tx_stat_v[pldat->last_tx_idx];

              txstat = *ptxstat;

//将上一次发送的描述符状态赋给txstat.

              /* Next buffer and decrement used buffer counter */

              pldat->num_used_tx_buffs--;

              pldat->last_tx_idx++;

              if (pldat->last_tx_idx >= ENET_TX_DESC)

              {

                     pldat->last_tx_idx = 0;

              }

 

              /* Update collision counter */

              ndev->stats.collisions += ((txstat >> 21) & 0xF);

//更新总的冲突次数

 

              /* Any errors occurred? */

              if (txstat & 0x80000000)

              {

                     if (txstat & 0x20000000)

                     {

                            /* FIFO underrun */

                            ndev->stats.tx_fifo_errors++;

                            ndev->stats.tx_errors++;

                     }

                     if (txstat & 0x10000000)

                     {

                            /* Late collision */

                            ndev->stats.tx_aborted_errors++;

                            ndev->stats.tx_errors++;

                     }

                     if (txstat & 0x08000000)

                     {

                            /* Excessive collision */

                            ndev->stats.tx_aborted_errors++;

                            ndev->stats.tx_errors++;

                     }

                     if (txstat & 0x04000000)

                     {

                            /* Defer limit */

                            ndev->stats.tx_aborted_errors++;

                            ndev->stats.tx_errors++;

                     }

/*更新发送描述符过程中的各种错误统计*/

 

                     /* Buffer transmit failed, requeue it */

                     lpc32xx_net_hard_start_xmit(skb, ndev);

//统计完这些信息后,重发。

              }

              else

              {

                     /* Update stats */

                     ndev->stats.tx_packets++;

                     ndev->stats.tx_bytes += skb->len;

 

                     /* Free buffer */

                     dev_kfree_skb_irq(skb);

//如果没有错误就更新数据缓冲区指针,释放刚使用的skb构。

              }

             

              txcidx = __raw_readl(ENET_TXCONSUMEINDEX(pldat->net_base));

       }

 

       if (netif_queue_stopped(ndev))

       {

              netif_wake_queue(ndev);

//重新启动发送队列

       }

}

总结:__lpc32xx_handle_xmit,出现 underrun,latecollision,excessivecollision,,excessivedefer错误时,统计错误信息,重新发送 目前的skb。或是其它的中断如txfinishedint和txdoneint则更新发送状态。(似乎对于NoDescriptor这样的中断是按正常 中断处理的)

 

追踪__lpc32xx_handle_recv

static void __lpc32xx_handle_recv(struct net_device *ndev)

{

       struct netdata_local *pldat = netdev_priv(ndev);

       struct sk_buff *skb;

       int rxconsidx, len, ethst;

       struct rx_status_t *prxstat;

       u8 *prdbuf;

 

       /* Get the current RX buffer indexes */

       rxconsidx = (int) __raw_readl(ENET_RXCONSUMEINDEX(pldat->net_base));

//获得接收消费者索引

       while (rxconsidx != (int) __raw_readl(ENET_RXPRODUCEINDEX(pldat->net_base)))

//当描述符数组不为空时

       {

              /* Get pointer to receive status */

              prxstat = (struct rx_status_t *) pldat->rx_stat_v [rxconsidx];

              len = (prxstat->statusinfo & 0x7FF) + 1;

//获得要处理的描述符的状态和数据缓冲区的字节数

 

              /* Status error? */

              ethst = prxstat->statusinfo;

              if ((ethst & 0xBF800000) == 0x84000000)

              {

                     /* Range error, can be ignored */

                     ethst &= ~0x80000000;

              }

忽略“接收的包超出包最大限制”的错误。

 

              if (ethst & 0x80000000)

              {

                     /* Check statuses */

                     if (prxstat->statusinfo & (1 << 28))

                     {

                            /* Overrun error */

                            ndev->stats.rx_fifo_errors++;

                     }

                     else if (prxstat->statusinfo & (1 << 23))

                     {

                            /* CRC error */

                            ndev->stats.rx_crc_errors++;

                     }

                     else if (prxstat->statusinfo & (1 << 25))

                     {

                            /* Length error */

                            ndev->stats.rx_length_errors++;

                     }

                     else if (prxstat->statusinfo & 0x80000000)

                     {

                            /* Other error */

                            ndev->stats.rx_length_errors++;

                     }

                     ndev->stats.rx_errors++;

              }

//如果状态字出现错误,则统计错误信息。

              else

              {

                     /* Packet is good */

                     skb = dev_alloc_skb(len + 8);

//申请一个skb结构体,大小为接收的skb的大小再加8

                     if (!skb)

                     {

                            ndev->stats.rx_dropped++;

                     }

                     else

                     {

                            skb_reserve(skb, 8);

//将skb后移8个字节。

                            prdbuf = skb_put(skb, (len - 0));

//skb尾部增加len大小。

                            /* Copy packer from buffer */

                            memcpy(prdbuf, (void *) pldat->rx_buff_v [rxconsidx], len);

//将接收描述符拷贝入skb结构中。

                            /* Pass to upper layer */

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

                            netif_rx(skb);

                            ndev->last_rx = jiffies;

                            ndev->stats.rx_packets++;

                            ndev->stats.rx_bytes += len;

                     }

              }

 

              /* Increment consume index */

              rxconsidx = rxconsidx + 1;

              if (rxconsidx >= ENET_RX_DESC)

              {

                     rxconsidx = 0;

              }

              __raw_writel((u32) rxconsidx, ENET_RXCONSUMEINDEX(pldat->net_base));

       }

}

//最后更新接收描述符的消费索引

总结:依照对__lpc32xx_handle_recv的分析,它只能对错误信息进行统计,然后会继续接收新的描述符。所谓接收处理即将接收描述符的数据指针指向的数据定入申请的skb,然后发送给上一层协议。

 

为了分析的完整性,还有平台的resume,remove,suspend方法。

static struct platform_driver lpc32xx_net_driver = {

       .probe            = lpc32xx_net_drv_probe,

       .remove          = __devexit_p(lpc32xx_net_drv_remove),

       .suspend  = lpc32xx_net_drv_suspend,

       .resume          = lpc32xx_net_drv_resume,

       .driver            = {

              .name      = MODNAME,

       },

};

 

static int lpc32xx_net_drv_remove(struct platform_device *pdev)

{

       struct net_device *ndev = platform_get_drvdata(pdev);

       struct netdata_local *pldat = netdev_priv(ndev);

 

       unregister_netdev(ndev);//注销ndev

       platform_set_drvdata(pdev, NULL);//释放pdev的drvdata

       dma_free_coherent(&pldat->pdev->dev, pldat->dma_buff_size,

              (void *) pldat->dma_buff_base_v, (dma_addr_t) pldat->dma_buff_base_p);

//释放dma一致性缓冲区。

       free_irq(ndev->irq, ndev);//释放中断

       iounmap(pldat->net_base);//去除寄存器映射

       clk_disable(pldat->clk);

       clk_put(pldat->clk);//释放时钟

       free_netdev(ndev);//释放ndev

 

       return 0;

}

 

static int lpc32xx_net_drv_suspend(struct platform_device *pdev, pm_message_t state)

{

       struct net_device *ndev = platform_get_drvdata(pdev);

       struct netdata_local *pldat = netdev_priv(ndev);

 

       if (ndev)

       {

              if (netif_running(ndev))

//推测设备是否停下来。

              {

                     netif_device_detach(ndev);

//断开网络设备

                     __lpc32xx_net_shutdown(pldat);

//重启以太网块,复位MAC配置寄存器

                     clk_disable(pldat->clk);

              }

       }

 

       return 0;

}

 

static int lpc32xx_net_drv_resume(struct platform_device *pdev)

{

       struct net_device *ndev = platform_get_drvdata(pdev);

       struct netdata_local *pldat;

 

       if (ndev)

       {

              if (netif_running(ndev))

              {

                     pldat = netdev_priv(ndev);

 

                     /* Enable interface clock */

                     clk_enable(pldat->clk);

//获得时钟

 

                     /* Reset and initialize */

                     __lpc32xx_eth_reset(pldat);

                     __lpc32xx_eth_init(pldat);

 

                     netif_device_attach(ndev);

//连接网络设备

              }

       }

 

       return 0;

}

 

总结:网络设备驱动也是有自己的框架,要做的就是填充ndev的成员和操作函数,然后处理好中断接收。还有一个工作就是错误处理和信息统计。

 

在分析lpc32xx_mii.c时,也遇到一些问题:比如mdiobus_register()看似是注册总线,实际上注册的是设备。而且注册的 设备,像字符设备一样,拥有自己一系列的操作方法。内核中,给出了一种名为mdio_bus的总线,通过mdiobus_register注册的设备在设 备模型上隶属于mdio_bus,但是实际却并没有相应的驱动。这样的设备本身拥有mii_bus总线,问题是这个总线却没有向内核注册。该设备上的方法 就是mii_bus结构中的成员。问题多多,以后再说

linux网络设备应用与驱动编程学习4——模板与实例(C)——其它方法

分类: 驱动笔记 188人阅读 评论(0) 收藏 举报
 

为了便于分析,贴出对应的方法

ndev->open = lpc32xx_net_open;

       ndev->stop = lpc32xx_net_close;

       ndev->hard_start_xmit = lpc32xx_net_hard_start_xmit;

       ndev->tx_timeout = lpc32xx_net_timeout;

       ndev->watchdog_timeo = msecs_to_jiffies(watchdog);

       ndev->set_multicast_list = lpc32xx_net_set_multicast_list;

       ndev->ethtool_ops = &lpc32xx_net_ethtool_ops;

       ndev->do_ioctl = &lpc32xx_net_ioctl;

#ifdef CONFIG_NET_POLL_CONTROLLER

       ndev->poll_controller = lpc32xx_net_poll_controller;

 

n         hard_start_xmit方法(发送数据时调用)

static int lpc32xx_net_hard_start_xmit(struct sk_buff *skb, struct net_device *ndev)

{

       struct netdata_local *pldat = netdev_priv(ndev);

       unsigned int len, txidx;

       u32 *ptxstat;

       struct txrx_desc_t *ptxrxdesc;

 

       len = skb->len;

//skb的大小

 

       spin_lock_irq(&pldat->lock);

 

       if (pldat->num_used_tx_buffs >= (ENET_TX_DESC - 1))

       {

              /* This function should never be called when there are no

                 buffers, log the error */

              netif_stop_queue(ndev);

              spin_unlock_irq(&pldat->lock);

              dev_err(&pldat->pdev->dev,

                     "BUG! TX request when no free TX buffers!\n");

              return 1;

       }

 

       /* Get the next TX descriptor index */

       txidx = __raw_readl(ENET_TXPRODUCEINDEX(pldat->net_base));

//获得发送描述符的生产者索引

 

       /* Setup control for the transfer */

       ptxstat = (u32 *) pldat->tx_stat_v [txidx];

       *ptxstat = 0;

//将描述符状态写为0  

ptxrxdesc = (struct txrx_desc_t *) pldat->tx_desc_v [txidx];

       ptxrxdesc->control = (len - 1) | 0xC0000000;

//描述符的控制字,写入skb的长度,最后希望发送完成后,产生中断。

 

       /* Copy data to the DMA buffer */

       memcpy((void *) pldat->tx_buff_v [txidx], skb->data, len);

//将skb拷贝到DMA的数据缓冲区中。

       /* Save the buffer and increment the buffer counter */

       pldat->skb[txidx] = skb;

       pldat->num_used_tx_buffs++;

 

       /* Start transmit */

       txidx++;

       if (txidx >= ENET_TX_DESC)

       {

              txidx = 0;

       }

//生产者索引到最大值时应回0

       __raw_writel((u32) txidx, ENET_TXPRODUCEINDEX(pldat->net_base));

//更新发送描述符的生产者索引应由软件完成。

 

       /* Stop queue if no more TX buffers */

       if (pldat->num_used_tx_buffs >= (ENET_TX_DESC - 1))

       {

              netif_stop_queue(ndev);

       }

 

       spin_unlock_irq(&pldat->lock);

       ndev->trans_start = jiffies;

//记录发送的时间戳

 

       return 0;

}

这个函数只发送了一个描述符,然后更新了一次发送生产者索引,一个描述符对应一个skb

n         tx_timeout(发送超时时调用)

static void lpc32xx_net_timeout(struct net_device *ndev)

{

       struct netdata_local *pldat = netdev_priv(ndev);

 

       /* This should never happen and indicates a problem */

       dev_err(&pldat->pdev->dev, "BUG! TX timeout occurred!\n");

}

这个函数从实现的结果看,它只是调试方便才实现的。

n         watchdog_timeo(设置超时时间)

n         set_multicast_list(组播列表改变或设备标志改变时)

static void lpc32xx_net_set_multicast_list(struct net_device *ndev)

{

       struct netdata_local *pldat = netdev_priv(ndev);

       struct dev_mc_list *mcptr = ndev->mc_list;

//组播mac地址

       int i, mc_cnt = ndev->mc_count;

//组播(地址)数

       u32 tmp32, hash_val, hashlo, hashhi;

       unsigned long flags;

 

       spin_lock_irqsave(&pldat->lock, flags);

 

       /* Set station address */

       __lpc32xx_set_mac(pldat, ndev->dev_addr);

//将dev_addr中的MAC地址存入站寄存器

 

       tmp32 =  RXFLTRW_ACCEPTUBROADCAST | RXFLTRW_ACCEPTPERFECT;

 

       if (ndev->flags & IFF_PROMISC)

       {

              tmp32 |= RXFLTRW_ACCEPTUNICAST | RXFLTRW_ACCEPTUNICASTHASH |

                     RXFLTRW_ACCEPTUMULTICASTHASH;

       }

       if (ndev->flags & IFF_ALLMULTI)

       {

              tmp32 |= RXFLTRW_ACCEPTUMULTICAST;

       }

       __raw_writel(tmp32, ENET_RXFILTER_CTRL(pldat->net_base));

//设置滤波存器

 

 

       /* Set initial hash table */

       hashlo = 0x0;

       hashhi = 0x80000000;

 

       /* 64 bits : multicast address in hash table */

       for (i = 0; i < mc_cnt; i++, mcptr = mcptr->next)

       {

              hash_val = ether_crc_le(6, mcptr->dmi_addr) & 0x3f;

 

              if (hash_val >= 32)

              {

                     hashhi |= 1 << (32 - hash_val);

              }

              else

              {

                     hashlo |= 1 << hash_val;

              }

       }

 

       __raw_writel(hashlo, ENET_HASHFILTERL(pldat->net_base));

       __raw_writel(hashhi, ENET_HASHFILTERH(pldat->net_base));

//填充Hash滤波器表

 

       spin_unlock_irqrestore(&pldat->lock, flags);

}

总结:这个函数重填了MAC地址,根据标志重设了滤波寄存器,重填了 hash滤波表。

n         ethtool_ops(更改或报告网络设备的设置)

static const struct ethtool_ops lpc32xx_net_ethtool_ops = {

       .get_drvinfo    = lpc32xx_net_ethtool_getdrvinfo,

       .get_settings   = lpc32xx_net_ethtool_getsettings,

       .set_settings    = lpc32xx_net_ethtool_setsettings,

       .get_msglevel  = lpc32xx_net_ethtool_getmsglevel,

       .set_msglevel  = lpc32xx_net_ethtool_setmsglevel,

       .get_link  = ethtool_op_get_link,

};

lpc32xx_net_ethtool_getdrvinfo,用于获得驱动的信息,如驱动版本,总线等

lpc32xx_net_ethtool_getsettings,lpc32xx_net_ethtool_setsettings分别用于获得和设置一些信息。视命令而定

ethtool_op_get_link,获知网络连接状态的

 

 

除了以上方法,还有中断

static irqreturn_t __lpc32xx_eth_interrupt(int irq, void *dev_id)

{

       struct net_device *ndev = dev_id;

       struct netdata_local *pldat = netdev_priv(ndev);

       u32 tmp;

 

       spin_lock(&pldat->lock);

 

       /* Get the interrupt status */

       tmp = __raw_readl(ENET_INTSTATUS(pldat->net_base));

/*每当发生中断时,执行此函数,此函数先查询中断状态寄存器*/

       while (tmp)

       {

              /* Clear interrupts */

              __raw_writel(tmp, ENET_INTCLEAR(pldat->net_base));

 

              /* Transmit complete? */

              if (tmp & (MACINT_TXUNDERRUNINTEN | MACINT_TXERRORINTEN |

                     MACINT_TXFINISHEDINTEN | MACINT_TXDONEINTEN))

/*如果发生了重大的溢出错误,发送出现错误,单个或所有描述符发送完成都会调用以下函数*/

              {

                     __lpc32xx_handle_xmit(ndev);

              }

 

              /* Receive buffer available */

              if (tmp & (MACINT_RXOVERRUNINTEN | MACINT_RXERRORONINT |

                     MACINT_RXFINISHEDINTEN | MACINT_RXDONEINTEN))

              {

                     __lpc32xx_handle_recv(ndev);

              }

 

              /* Recheck the interrupt status */

              tmp = __raw_readl(ENET_INTSTATUS(pldat->net_base));

       }

 

       spin_unlock(&pldat->lock);

 

       return IRQ_HANDLED;

}

函数处理了发送与接收中断。

 

 

追踪__lpc32xx_handle_xmit

static void __lpc32xx_handle_xmit(struct net_device *ndev)

{

       struct netdata_local *pldat = netdev_priv(ndev);

       struct sk_buff *skb;

       unsigned int txcidx, *ptxstat, txstat;

 

       txcidx = __raw_readl(ENET_TXCONSUMEINDEX(pldat->net_base));

//获得发送消费者索引(要发送的描述符)

       while (pldat->last_tx_idx != txcidx)

       {

              skb = (struct sk_buff *) pldat->skb[pldat->last_tx_idx];

//获得最近已发送描述符对应的skb

              /* A buffer is available, get buffer status */

              ptxstat = (unsigned int *) pldat->tx_stat_v[pldat->last_tx_idx];

              txstat = *ptxstat;

//将上一次发送的描述符状态赋给txstat.

              /* Next buffer and decrement used buffer counter */

              pldat->num_used_tx_buffs--;

              pldat->last_tx_idx++;

              if (pldat->last_tx_idx >= ENET_TX_DESC)

              {

                     pldat->last_tx_idx = 0;

              }

 

              /* Update collision counter */

              ndev->stats.collisions += ((txstat >> 21) & 0xF);

//更新总的冲突次数

 

              /* Any errors occurred? */

              if (txstat & 0x80000000)

              {

                     if (txstat & 0x20000000)

                     {

                            /* FIFO underrun */

                            ndev->stats.tx_fifo_errors++;

                            ndev->stats.tx_errors++;

                     }

                     if (txstat & 0x10000000)

                     {

                            /* Late collision */

                            ndev->stats.tx_aborted_errors++;

                            ndev->stats.tx_errors++;

                     }

                     if (txstat & 0x08000000)

                     {

                            /* Excessive collision */

                            ndev->stats.tx_aborted_errors++;

                            ndev->stats.tx_errors++;

                     }

                     if (txstat & 0x04000000)

                     {

                            /* Defer limit */

                            ndev->stats.tx_aborted_errors++;

                            ndev->stats.tx_errors++;

                     }

/*更新发送描述符过程中的各种错误统计*/

 

                     /* Buffer transmit failed, requeue it */

                     lpc32xx_net_hard_start_xmit(skb, ndev);

//统计完这些信息后,重发。

              }

              else

              {

                     /* Update stats */

                     ndev->stats.tx_packets++;

                     ndev->stats.tx_bytes += skb->len;

 

                     /* Free buffer */

                     dev_kfree_skb_irq(skb);

//如果没有错误就更新数据缓冲区指针,释放刚使用的skb构。

              }

             

              txcidx = __raw_readl(ENET_TXCONSUMEINDEX(pldat->net_base));

       }

 

       if (netif_queue_stopped(ndev))

       {

              netif_wake_queue(ndev);

//重新启动发送队列

       }

}

总结:__lpc32xx_handle_xmit,出现 underrun,latecollision,excessivecollision,,excessivedefer错误时,统计错误信息,重新发送 目前的skb。或是其它的中断如txfinishedint和txdoneint则更新发送状态。(似乎对于NoDescriptor这样的中断是按正常 中断处理的)

 

追踪__lpc32xx_handle_recv

static void __lpc32xx_handle_recv(struct net_device *ndev)

{

       struct netdata_local *pldat = netdev_priv(ndev);

       struct sk_buff *skb;

       int rxconsidx, len, ethst;

       struct rx_status_t *prxstat;

       u8 *prdbuf;

 

       /* Get the current RX buffer indexes */

       rxconsidx = (int) __raw_readl(ENET_RXCONSUMEINDEX(pldat->net_base));

//获得接收消费者索引

       while (rxconsidx != (int) __raw_readl(ENET_RXPRODUCEINDEX(pldat->net_base)))

//当描述符数组不为空时

       {

              /* Get pointer to receive status */

              prxstat = (struct rx_status_t *) pldat->rx_stat_v [rxconsidx];

              len = (prxstat->statusinfo & 0x7FF) + 1;

//获得要处理的描述符的状态和数据缓冲区的字节数

 

              /* Status error? */

              ethst = prxstat->statusinfo;

              if ((ethst & 0xBF800000) == 0x84000000)

              {

                     /* Range error, can be ignored */

                     ethst &= ~0x80000000;

              }

忽略“接收的包超出包最大限制”的错误。

 

              if (ethst & 0x80000000)

              {

                     /* Check statuses */

                     if (prxstat->statusinfo & (1 << 28))

                     {

                            /* Overrun error */

                            ndev->stats.rx_fifo_errors++;

                     }

                     else if (prxstat->statusinfo & (1 << 23))

                     {

                            /* CRC error */

                            ndev->stats.rx_crc_errors++;

                     }

                     else if (prxstat->statusinfo & (1 << 25))

                     {

                            /* Length error */

                            ndev->stats.rx_length_errors++;

                     }

                     else if (prxstat->statusinfo & 0x80000000)

                     {

                            /* Other error */

                            ndev->stats.rx_length_errors++;

                     }

                     ndev->stats.rx_errors++;

              }

//如果状态字出现错误,则统计错误信息。

              else

              {

                     /* Packet is good */

                     skb = dev_alloc_skb(len + 8);

//申请一个skb结构体,大小为接收的skb的大小再加8

                     if (!skb)

                     {

                            ndev->stats.rx_dropped++;

                     }

                     else

                     {

                            skb_reserve(skb, 8);

//将skb后移8个字节。

                            prdbuf = skb_put(skb, (len - 0));

//skb尾部增加len大小。

                            /* Copy packer from buffer */

                            memcpy(prdbuf, (void *) pldat->rx_buff_v [rxconsidx], len);

//将接收描述符拷贝入skb结构中。

                            /* Pass to upper layer */

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

                            netif_rx(skb);

                            ndev->last_rx = jiffies;

                            ndev->stats.rx_packets++;

                            ndev->stats.rx_bytes += len;

                     }

              }

 

              /* Increment consume index */

              rxconsidx = rxconsidx + 1;

              if (rxconsidx >= ENET_RX_DESC)

              {

                     rxconsidx = 0;

              }

              __raw_writel((u32) rxconsidx, ENET_RXCONSUMEINDEX(pldat->net_base));

       }

}

//最后更新接收描述符的消费索引

总结:依照对__lpc32xx_handle_recv的分析,它只能对错误信息进行统计,然后会继续接收新的描述符。所谓接收处理即将接收描述符的数据指针指向的数据定入申请的skb,然后发送给上一层协议。

 

为了分析的完整性,还有平台的resume,remove,suspend方法。

static struct platform_driver lpc32xx_net_driver = {

       .probe            = lpc32xx_net_drv_probe,

       .remove          = __devexit_p(lpc32xx_net_drv_remove),

       .suspend  = lpc32xx_net_drv_suspend,

       .resume          = lpc32xx_net_drv_resume,

       .driver            = {

              .name      = MODNAME,

       },

};

 

static int lpc32xx_net_drv_remove(struct platform_device *pdev)

{

       struct net_device *ndev = platform_get_drvdata(pdev);

       struct netdata_local *pldat = netdev_priv(ndev);

 

       unregister_netdev(ndev);//注销ndev

       platform_set_drvdata(pdev, NULL);//释放pdev的drvdata

       dma_free_coherent(&pldat->pdev->dev, pldat->dma_buff_size,

              (void *) pldat->dma_buff_base_v, (dma_addr_t) pldat->dma_buff_base_p);

//释放dma一致性缓冲区。

       free_irq(ndev->irq, ndev);//释放中断

       iounmap(pldat->net_base);//去除寄存器映射

       clk_disable(pldat->clk);

       clk_put(pldat->clk);//释放时钟

       free_netdev(ndev);//释放ndev

 

       return 0;

}

 

static int lpc32xx_net_drv_suspend(struct platform_device *pdev, pm_message_t state)

{

       struct net_device *ndev = platform_get_drvdata(pdev);

       struct netdata_local *pldat = netdev_priv(ndev);

 

       if (ndev)

       {

              if (netif_running(ndev))

//推测设备是否停下来。

              {

                     netif_device_detach(ndev);

//断开网络设备

                     __lpc32xx_net_shutdown(pldat);

//重启以太网块,复位MAC配置寄存器

                     clk_disable(pldat->clk);

              }

       }

 

       return 0;

}

 

static int lpc32xx_net_drv_resume(struct platform_device *pdev)

{

       struct net_device *ndev = platform_get_drvdata(pdev);

       struct netdata_local *pldat;

 

       if (ndev)

       {

              if (netif_running(ndev))

              {

                     pldat = netdev_priv(ndev);

 

                     /* Enable interface clock */

                     clk_enable(pldat->clk);

//获得时钟

 

                     /* Reset and initialize */

                     __lpc32xx_eth_reset(pldat);

                     __lpc32xx_eth_init(pldat);

 

                     netif_device_attach(ndev);

//连接网络设备

              }

       }

 

       return 0;

}

 

总结:网络设备驱动也是有自己的框架,要做的就是填充ndev的成员和操作函数,然后处理好中断接收。还有一个工作就是错误处理和信息统计。

 

在分析lpc32xx_mii.c时,也遇到一些问题:比如mdiobus_register()看似是注册总线,实际上注册的是设备。而且注册的 设备,像字符设备一样,拥有自己一系列的操作方法。内核中,给出了一种名为mdio_bus的总线,通过mdiobus_register注册的设备在设 备模型上隶属于mdio_bus,但是实际却并没有相应的驱动。这样的设备本身拥有mii_bus总线,问题是这个总线却没有向内核注册。该设备上的方法 就是mii_bus结构中的成员。问题多多,以后再说

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多