zoukankan      html  css  js  c++  java
  • RK3288 GMAC整理

    一、源文件

    源码路径:drivers etethernet ockchipgmac

    源码阅读顺序:

         

    二、重要探针函数stmmac_dvr_probe

    1. alloc_etherdev

    申请网卡设备和私有数据。

    struct net_device *ndev = NULL;

    struct stmmac_priv *priv;

    ndev = alloc_etherdev(sizeof(struct stmmac_priv));

    priv = netdev_priv(ndev);

    网卡设备和私有数据紧紧的挨在一起:网卡设备+私用数据结构,通过netdev_priv获取私有数据。网卡设备是通用数据结构,私有数据则为各个MAC控制器的数据结构。

    2. stmmac_hw_init

    初始化MAC设备。检测要添加的设备(GMAC/MAC10-100),检查HW能力并设置驱动程序的特性。

    3. netif_napi_add

    初始化一个NAPI上下文,轮询接收数据包接口。

    netif_napi_add(ndev, &priv->napi, stmmac_poll, 64);

    stmmac_poll是轮询接口

    该接口完成两个事情:

    1)当数据发送完成时产生中断,进入该函数进行资源回收。

    2)收到数据产生中断,进入该函数进行数据接收和处理。

    复制代码
    static int stmmac_poll(struct napi_struct *napi, int budget)
    {
      ......
      stmmac_tx_clean(priv);  //传输完成后回收资源
      work_done = stmmac_rx(priv, budget); //数据包接收处理
      stmmac_enable_dma_irq(priv); //使能dma终端
      ......
    }
    复制代码

    stmmac_rx函数解析

    作用:再次填充并使用skb预先分配的缓存,由NAPI轮询方法调用,可以获得环内所有帧。

    函数:static int stmmac_rx(struct stmmac_priv *priv, int limit)

    函数内定义:

    1)priv->hw->desc->get_rx_owner(p)

    判断当前描述子的归属:描述子数据结构中OWN位,

      0: 当前描述子应该由CPU操作,

      1: 前描述子应该由GMAC操作。

    对于接收,初始化dma描述子队列时设置为1。

    GMAC根据寄存器配置,获取可用接收描述子,然后从RxFIFO中读取从PHY接收的Ethernet报文,如果报文符合接收条件,将该报文写入接收描述子指向的数据缓冲区,并回写接收描述子。这个回写就会将OWN位设置为0。

    2)next_entry = (++priv->cur_rx) % rxsize;

    获取下一帧描述符,

    p_next =priv->dma_rx + next_entry;

    priv->cur_rx:已经传递给协议层的index。

    3)status =(priv->hw->desc->rx_status(&priv->dev->stats, &priv->xstats,p));

    获取收到帧状态,如果是丢弃帧,则什么都不处理;否则上传到上层网络。

    4)frame_len = priv->hw->desc->get_rx_frame_len(p, coe); 获取帧长度

    5)skb = priv->rx_skbuff[entry];

    priv->rx_skbuff[entry] = NULL;

    注意:skb将有上层网络处理完后进行释放。

    6)skb_put(skb, frame_len);

    dma_unmap_single(priv->device,priv->rx_skbuff_dma[entry],priv->dma_buf_sz, DMA_FROM_DEVICE);  设置skb数据长度和解除流式DMA映射

    7)获取skb的协议类型

    skb->protocol = eth_type_trans(skb,priv->dev);

    skb->dev = priv->dev;

    8)napi_gro_receive(&priv->napi,skb);

    将skb通过NAPI接口上传上层网络协议处理。

    9)stmmac_rx_refill(priv); 重新填充接收队列

    三、打开网卡设备接口stmmac_open

    网卡刚起来时是关闭的,要用命令去打开,ifconfig eth0 up 时调用net_device_ops的.ndo_open,这里为stmmac_open。

    1. netdev_priv 获得网络设备私有数据
    2. stmmac_check_ether_addr 检测MAC地址是否有效,若无效,随机产生一个
    3. stmmac_mdio_register注册MII总线

    mdiobus_register()

    a.注册总线设备device_register(&bus->dev);

    b.复位总线bus->reset(bus);

    c.扫描总线上的PHY设备,最大支持32个

    4. stmmac_init_phy初始化PHY设备,并将PHY和MAC绑定

    5. request_irq 申请中断

    ret = request_irq(dev->irq, stmmac_interrupt,

      IRQF_SHARED, dev->name, dev);

    注册中断请求线IRQ,中断处理函数stmmac_interrupt用于接收DMA数据,配合NAPI处理。

    复制代码
    static irqreturn_t stmmac_interrupt(int irq, void *dev_id)
    {
      ......
      /* To handle DMA interrupts */
      stmmac_dma_interrupt(priv);  
    }
    
    static void stmmac_dma_interrupt(struct stmmac_priv *priv)
    {
      ......
      status = priv->hw->dma->dma_interrupt(priv->ioaddr, &priv->xstats);
      if (likely((status & handle_rx)) || (status & handle_tx)) {
        if (likely(napi_schedule_prep(&priv->napi))) {
          stmmac_disable_dma_irq(priv);
          __napi_schedule(&priv->napi); //加入poll流程
        }
      }
      ......
    }
    复制代码

     

    四、发送数据接口stmmac_xmit

    该接口实现了Scatter/Gather I/O功能,通过skb_shinfo宏来判断数据包是由一个数据片段组成,还是由大量数据片段组成。

    1、entry= priv->cur_tx % txsize;

    desc= priv->dma_tx + entry; 获取可用发送描述子

    first= desc; 保存第一个数据片段

    2、priv->tx_skbuff[entry]= skb;

    priv->tx_page[entry]= NULL; 将skb放到发送队列

    3、unsignedint nopaged_len = skb_headlen(skb);

    desc->des2=dma_map_single(priv->device,skb->data,nopaged_len, DMA_TO_DEVICE);

    priv->hw->desc->prepare_tx_desc(desc,1, nopaged_len, csum_insertion);

    发送单个或第一个数据包,当只有一个数据片段时,skb->data将发送所有数据;当有多个数据片段时,skb->data则指向第一个数据片段,数据长度skb->len –skb->data_len,其他数据存放在共享数据结构frags数组中。(skb->len:数据包中全部数据的长度,skb->data_len分隔存储数据片段长度)

    4、

    复制代码
    for (i = 0; i < nfrags; i++) {
      const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
      int len = skb_frag_size(frag);
      entry = (++priv->cur_tx) % txsize;
      if (priv->extend_desc)
        desc = (struct dma_desc *)(priv->dma_etx + entry);
      else
        desc = priv->dma_tx + entry;
      TX_DBG("	[entry %d] segment len: %d
    ", entry, len);
      desc->des2 = skb_frag_dma_map(priv->device, frag, 0, len, DMA_TO_DEVICE);
      priv->tx_skbuff_dma[entry] = desc->des2;
      priv->tx_skbuff[entry] = NULL;
      priv->hw->desc->prepare_tx_desc(desc, 0, len, csum_insertion,
      priv->mode);
      wmb();
      priv->hw->desc->set_tx_owner(desc);
      wmb();
    }
    复制代码

    发送剩余数据片段。

    对于多个数据片段时,还要进行数据片段的发送,采用页处理。直接处理页结构,而不是内核虚拟地址。

    5、priv->hw->desc->set_tx_owner(first);

    priv->cur_tx ++;

    将第一个数据片段描述子交给GMAC,记录当前发送index

    6、priv->hw->dma->enable_dma_transmission(priv->dma_ioaddr,

                              priv->dma_channel);

    写任何值唤醒处于挂起的RxDMA

  • 相关阅读:
    Permission denied (publickey). fatal: Could not read from remote repository.
    jQuery OCUpload一键上传文件
    org.apache.subversion.javahl.ClientException: Working copy is not up-to-date
    测开之路一百三十五:实现登录身份验证功能
    测开之路一百三十四:实现指定查找功能
    测开之路一百三十三:实现sql函数封装
    测开之路一百三十二:实现修改功能
    测开之路一百三十一:实现删除功能
    测开之路一百三十:实现前端到数据库交互(增和查)
    测开之路一百二十九:jinja2模板语法
  • 原文地址:https://www.cnblogs.com/try-again/p/9179046.html
Copyright © 2011-2022 走看看