打印
[工具和软件]

Freescale i.MX6 Linux Ethernet Driver驱动源码分析(二)

[复制链接]
1867|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
Vitality1|  楼主 | 2015-1-28 20:23 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
上一篇**分析了Freescale i.MX6 Linux Ethernet Driver的device的添加和driver的加载过程,接下来分析fec_enet_init()函数:首先提一点这个函数的声明是static int fec_enet_init(struct net_device *ndev),即传递参数为net_device,那么通过netdev_priv(ndev)即可以获取到之前alloc_etherdev()函数分配的指向私有数据的地址:
ndev = alloc_etherdev(sizeof(struct fec_enet_private));
struct fec_enet_private *fep = netdev_priv(ndev);
这种通过结构体内部指针传递私有数据的方式在driver中非常常见。函数开头即为Ethernet Controller的DMA 控制器分配相应的buffer描述符:
/* Allocate memory for buffer descriptors. */
cbd_base = dma_alloc_noncacheable(NULL, BUFDES_SIZE, &fep->bd_dma,
                GFP_KERNEL);
if (!cbd_base) {
        printk("FEC: allocate descriptor memory failed?\n");
        return -ENOMEM;
}
这里分配的缓冲区大小是(tx buffer个数+rx buffer个数)×buffer描述符大小:
#define BUFDES_SIZE ((RX_RING_SIZE + TX_RING_SIZE) * sizeof(struct bufdesc))
由于buffer描述符会被CPU以及DMA控制器访问,因此会存在Cache一致性问题,这里采用了dma_alloc_noncacheable()函数,即DMA一致性映射。这里采用一致性映射是因为CPU或者DMA控制器会以不可预知的方式去访问这段内存区,在Linux Kernel中解决Cache一致性问题有两种方案:DMA流式映射和DMA一致性映射,关于这两者的区别在《Understanding Linux Kernel》以及《LDD3》中均有介绍,我个人也总结了一篇博文初步讲述了这两者的区别:http://blog.163.com/thinki_cao/blog/static/83944875201362142939337
这里分析一下DMA控制器,i.MX6的DMA控制器采用了环形buffer描述符,这里buffer分为两种,Legacy buffer descriptor是为了保持对前代Freescale器件的兼容性,而Enhanced buffer descriptor则提供了更多的功能,引用i.MX6Q的reference manual中的图:
Legacy buffer descriptor一共有8个字节,注意这里是采用大端存储模式的。


而Enhanced buffer descriptor一个有64字节,也是采用大端存储模式的,个人觉得这个Ethernet IP有点像是从PowerPC那边扣过来的。




可以从fec.h文件中找到对这两个描述符的定义:
struct bufdesc {
        unsigned short cbd_datlen;        /* Data length */
        unsigned short cbd_sc;        /* Control and status info */
        unsigned long cbd_bufaddr;        /* Buffer address */
#ifdef CONFIG_ENHANCED_BD
        unsigned long cbd_esc;
        unsigned long cbd_prot;
        unsigned long cbd_bdu;
        unsigned long ts;
        unsigned short res0[4];
#endif
如果定义了CONFIG_ENHANCED_BD宏,则开启Enhanced buffer descriptor的支持。不过纵观整个driver程序,3.0.35的内核并没有使用enhanced buffer descriptor使用的一些功能,比如Enhanced transmit buffer descriptor中的offset+8位置的PINS和IINS位,提供了采用MAC提供的IP accelerator进行硬件校验,提供对协议的校验和IP头的校验。而在yocto 3.10.17内核上,这些已经支持了!这也是为什么3.0.35上的Ethernet driver的性能不如3.10.17上的原因之一吧。下面继续分析代码:
spin_lock_init(&fep->hw_lock); /* 初始化自旋锁 */

fep->netdev = ndev; /*把net_device的地址传给netdev*/

/* Get the Ethernet address */fec_get_mac(ndev);
fec_get_mac会从多个地方获取mac地址:
static void __inline__ fec_get_mac(struct net_device *ndev)
{
        struct fec_enet_private *fep = netdev_priv(ndev);
        struct fec_platform_data *pdata = fep->pdev->dev.platform_data;
        unsigned char *iap, tmpaddr[ETH_ALEN];

        /*
         * try to get mac address in following order:
         *
         * 1) module parameter via kernel command line in form
         *    fec.macaddr=0x00,0x04,0x9f,0x01,0x30,0xe0
         */
        iap = macaddr;

        /*
         * 2) from flash or fuse (via platform data)
         */
        if (!is_valid_ether_addr(iap)) {
                if (pdata)
                        memcpy(iap, pdata->mac, ETH_ALEN);
        }

        /*
         * 3) FEC mac registers set by bootloader
         */
        if (!is_valid_ether_addr(iap)) {
                *((unsigned long *) &tmpaddr[0]) =
                        be32_to_cpu(readl(fep->hwp + FEC_ADDR_LOW));
                *((unsigned short *) &tmpaddr[4]) =
                        be16_to_cpu(readl(fep->hwp + FEC_ADDR_HIGH) >> 16);
                iap = &tmpaddr[0];
        }

        memcpy(ndev->dev_addr, iap, ETH_ALEN);

        /* Adjust MAC if using macaddr */
        if (iap == macaddr)
                 ndev->dev_addr[ETH_ALEN-1] = macaddr[ETH_ALEN-1] + fep->pdev->id;
}
1)首先是从全局变量macaddr获取ip地址,macaddr定义相关的代码如下:
static unsigned char macaddr[ETH_ALEN];
module_param_array(macaddr, byte, NULL, 0);
MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address");

__setup("fec_mac=", fec_mac_addr_setup);
这里的__setup是用来从uboot传给内核的启动参数中捕获fec_mac(即mac地址)参数,并将该参数传递给fec_mac_addr_setup(char *mac_addr)函数进行解析的。如果uboot中没有传递mac参数,那么macaddr数组中的成员全是0。
2)检测1)中获取的mac地址是否合法,如果不合法,则从设备的私有数据结构(如果pdata指针不为空)struct fec_platform_data中获取mac数组的值。
3)检测2)中获取的mac地址是否合法,如果不合法,则读取Ethernet控制器的mac地址寄存器来获取mac地址。
最后把mac地址传递给内核中net_device结构体中的dev_addr字段。
接着继续分析代码:
/* Set receive and transmit descriptor base. */
fep->rx_bd_base = cbd_base;
fep->tx_bd_base = cbd_base + RX_RING_SIZE;


设置tx_bd_base和rx_bd_base,即tx buffer descriptor base 和rx buffer descriptor base,示意图如下:

接着就是net_device已经fec_enet_private等结构体的设置:
/* The FEC Ethernet specific entries in the device structure */
ndev->watchdog_timeo = TX_TIMEOUT; /* watchdong定时器唤醒间隔 */
ndev->netdev_ops = &fec_netdev_ops;
ndev->ethtool_ops = &fec_enet_ethtool_ops;

fep->use_napi = FEC_NAPI_ENABLE;fep->napi_weight = FEC_NAPI_WEIGHT;if (fep->use_napi) {        fec_rx_int_is_enabled(ndev, false);        netif_napi_add(ndev, &fep->napi, fec_rx_poll, fep->napi_weight);}


相关帖子

发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

81

主题

421

帖子

9

粉丝