ZYNQ 平台 AD9361实现网络通信的一种方案+网卡驱动分析及实现

声明:文中若有不合理的地方,欢迎讨论学习及指正,本文仅仅涉及软件部分的代码,不阐述逻辑代码的实现。

功能:通过AD9361芯片实现无线组网,能实现视频、文件、音频等传输(当然承载量不能太大,由于逻辑实现采用时分的方法收发包,故只能实现最大约7Mbit/s的传输速率,此方式的弊端在于参与组网的设备越多,则传输的速率越慢,实际该方式有很多可优化的地方),其基本原理是在Linux内核层添加一个网卡设备,进行网络包的传输,其过程和真实的网卡一致,通过MAC层从物理设备收发上次协议栈的数据。

整体框架:

程序分析:

  • 分配接收缓冲区

缓冲区用于接收数据,防止数据突发量太大无法处理,分配函数实现如下:

ringBuffer = ringbuffer_malloc();

 

  • 注册一个网络设备

上边初始化了网口的MAC地址以及操作函数,结构体定义如下:


接着看一下ad9361_net_dev.net_dev->netdev_ops = &ad9361_netdev_ops;里的各部分操作接口(比较简单,没有实现太多接口),如下:

实际上在上述网卡注册的过程中还应该包含一项初始化,可以看一下内核里其他网卡设备的初始化过程,如下:

其中ethtool_ops这一项在这里并没有设置,ethtool_ops的成员函数如下:

包括对一些信息的获取以及其他参数的设置,这里没有用到,所以程序里没有进行初始化,建议对该结构体进行初始化。回到上面说到的netdev_ops,程序中对该部分实现的比较简单,仅有几个必要的函数,其中的open和close也不一定需要,如果要做的更加规范,建议把通用的接口全部实现,open函数里一般是包含缓冲区的初始化、中断申请、添加NAPI等操作,close则是释放这些资源,看一下上边的传输函数,如下:

static netdev_tx_t ad9361net_start_xmit(struct sk_buff *skb, struct net_device *dev)

{

    /* stop queue */

    netif_stop_queue(dev);         

    send_eth_frame(&ad9361_net_dev,(u8 *)skb->data,skb->len,cnt,vct);

    dev_kfree_skb(skb); 

    netif_wake_queue(dev);

    dev->stats.tx_packets++;

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

    return NETDEV_TX_OK;

}

上边实现得比较简洁。下边看一下MAC地址设置函数,如下:

网络设备相关的函数就到此结束。

  • Mac list初始化

该部分由于特殊需要,实现维护一张类ARP的表,功能与ARP类似,只是成员不同,不过多阐述。

  • Netlink初始化

通过netlink实现应用程序对FPGA寄存器的配置,此部分比较简单,不做描述。

  • DMA初始化

DMA初始化部分参见本人DMA相关文章,在此不做描述。

  • NAPI初始化

NAPI是网络部分使用的一种新的机制,以前的接口要么使用轮询要么中断,在处理函数中调用netif_receive_skb等接口向上层协议栈上报,这样就存在一些问题,要么CPU一直不断轮询无论数据有没有到来都耗费CPU的时间,要么中断触发后在中断中处理,这样会导致CPU频繁被中断同样也会耗费CPU时间,且中断触发较为频繁时,效率不如轮询,那么NAPI就是两者的结合,使用中断的同时也使用轮询,数据量低时采用中断,反之采用轮询。下面看一下程序实现,如下:

首先是初始化:

netif_napi_add(ad9361_net_dev.net_dev, &ad9361_net_dev.napi, ad9361_rx_poll, 16);

napi_enable(&ad9361_net_dev.napi);

netif_napi_add函数有四个参数,第一个是net_dev结构体指针,第二个是napi的结构体指针,第三个是poll时使用的函数,第四个每次poll能处理的包个数。

接着看一下ad9361_rx_poll函数,由于函数里内容比较多,这里精简列出:

static int ad9361_rx_poll(struct napi_struct *napi, int budget)
{
    int rx = 0;
    while (rx < budget) {
        rx++;
        /*数据读取*/
        ……………………
        /*上报数据*/
        napi_gro_receive(napi, skb);
    }
    if (rx < budget) {
        napi_gro_flush(napi, false);
        napi_complete_done(napi, rx);
        /*enable interrupt*/
        *(ad9361_net_dev.axi_intc + IER_OFFSET) = 0xff;
    }
    return rx;
}

         为了代码的整洁方便分析,这里进行了删减,但程序以及经过验证,保证可靠。

         中断处理函数如下:

static irqreturn_t ad9361_mac_irq_handler(int irq, void *data)
{
    struct ad9361_net *dev = (struct ad9361_net *)data;
    if(!dev){
          dev = &ad9361_net_dev;
    }
    /*napi*/
    if (napi_schedule_prep(&ad9361_net_dev.napi)){
          /*disable interrupt*/
          *(ad9361_net_dev.axi_intc + IER_OFFSET) = 0x0;
          __napi_schedule(&ad9361_net_dev.napi);
    }
    return IRQ_HANDLED;
}

在中断被触发之后,进入中断处理函数,如何调度?首先执行napi_schedule_prep判断能否进行调度,参见函数描述,如下:

如果能进行调度,则执行__napi_schedule函数,函数如下:

首先函数会保存当前中断的状态,然后调用____napi_schedule函数,最后恢复中断的状态,____napi_schedule函数如下:

首先将我们初始化的napi结构体加入当前CPU的poll_lost链表用于轮询,随后设置NET_RX_SOFTIRQ触发软中断。

上述就是NAPI使用的详细代码,可以根据自己的需求进行更改。

  • 映射寄存器

这部分代码对FPGA的相关寄存器建立映射,方便用于程序配置相关参数,不做描述。

  • Axi interrupt初始化

初始化代码如下,比较简单,不做描述,参见xilinx官方手册的描述

  • 中断申请

err = request_irq(ad9361_net_dev.irq, ad9361_mac_irq_handler,  IRQF_TRIGGER_HIGH, "ad9361_net", &ad9361_net_dev);

  • 配置FPGA参数

略。

文中若有地方存在不妥的,欢迎交流,软件实现很简单,分享学习。

最终实现了最大13.4Mb/s即1.5MB/s左右的速率

  • 4
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值