dwmac是众多网卡驱动中的一个通用驱动系列,里面包含了大部分能用到的多种系列网卡
- stm32
- sunxi
- ipq806x
- lpc18xx
- sti
- fpga
- …
其实本身这套驱动系列架构较为成熟,已将各种操作抽象成类,但是对于一个普通的项目来说并不需要支持如此多的网卡,故需要针对不同的硬件进行优化。
准确地来说stmmac层封装了一些纯底层接口并方便上层调用,但是一个简单的项目并不需要如此多的配置,也并不需要单独开辟一个结构体为其赋值等。
这套驱动的入口就是各种dwmac-xxxxxx的网卡类型入口,需要传入一些平台数据(共有数据),并在platform层写入一些具体硬件类型的系列数据(半共有数据),之后将上述数据向下发送至stmmac层进行转化,上述数据被填充至网卡驱动的私有结构体中(私有数据),随后,stmmac层的各类函数对私有结构体中的数据进行读写,这些操作最后都会在底层寄存器中实现。
一个项目很少能用到多种类型的网卡,故需要删减。
stmmac层以上的数据最后都会在网卡私有结构体中体现,所以最先砍掉的就应该是平台和入口的一些赋值操作。
正常情况下,赋值操作都会写一些固定的值进去,所以可以根据硬件类型写一些共有数据进去,至于半共有数据,可以根据用户配置进行删减。
例如:流量控制、巨型帧等,大部分应用可能并不会用到,这时添加上未免有些臃肿。
结构体platfromData就是共有数据,并且其实priv中的ioaddr同样也是共有数据,准确地来说它是一种用来联系用户侧和底层的媒介。大部分的底层的传参都需要它。
因此ioaddr可以从mac_device_info的pcsr中拿出来,直接弄成全局变量也是可以的。
像上述图片中的调用mac_device_info参数的API,只用到了pcsr,这种传参未免有些复杂。
dma与core部分是纯底层中的核心,里面包含了大部分的常用功能,所以要尽可能的保留。
还有就是版本号。
版本号其实是为了不同硬件中的兼容而设计的,如果自己的项目已知版本号,那么下面的版本号判断分支可以完全撤掉,如果并不知道,也可以使用分支预测,减小系统开销。
static int stmmac_change_mtu(struct netdev *dev, int new_mtu)
{
struct stmmac_priv *priv = netdev_priv(dev);
int max_mtu;
if (netif_running(dev)) {
pr_err("%s: must be stopped to change its MTU\n", dev->dev_name);
return -EBUSY;
}
if ((priv->plat->enh_desc) || (priv->synopsys_id >= DWMAC_CORE_4_00))
max_mtu = JUMBO_LEN;
..........
}
那么什么才算是常用功能呢?
mac100 | mac1000 | mac4 | mac4.0 | mac4.1 | mac5.1 | xgmac2.0 | xgmac2.1 | |
---|---|---|---|---|---|---|---|---|
gmac | Y | |||||||
gmac4 | Y | Y | Y | Y | ||||
xgmac | Y | Y | ||||||
min_version | 4.0 | 4.1 | 5.1 | 2.0 | 2.1 | |||
desc_ops | 4 | 4 | 4 | 4 | 210 | 210 | ||
dma_ops | 100 | 1000 | 4 | 4 | 410 | 410 | 210 | 210 |
mac_ops | 100 | 1000 | 4 | 4 | 410 | 510 | 210 | 2 |
mode_ops | 4ring | 4ring | 4ring | |||||
tc | 510 | 510 | 510 | 510 | 510 | 510 | ||
mmc | Y | Y | Y | Y | Y | Y | 210 | 210 |
setup | 100 | 1000 | 4 | 4 | 4 | 4 | 2 | 2 |
quirks | 1 | 1 | 4 | 2 | ||||
ptp_off | 3 | 3 | 4 | 4 | 4 | 4 | 2 | 2 |
mmc_off | 3 | 3 | 4 | 4 | 4 | 4 | 2 | 2 |
上表是目前LINUX 5.9.1内核中的dw功能位图,代码实现是在(hwif.c)这个文件中。
上述图片中,有人可能会问描述符是个什么鬼东西?
在驱动工作模式或协议栈,数据的流传方式主要是通过SKB_BUFF进行流通,但是这种数据结构硬件并不认可,并且硬件不知道你的数据的具体信息,所以需要描述符这个东西来将SKB_BUFF中的数据能让硬件看懂。
初始化时,预先分配有关描述符连续的内存,并将首地址填入TX/RX DESC 地址寄存器中。
工作时SKB_BUFF将数据地址填入置描述符的数据区中,并将该数据区所在的描述符中的控制位区置位,硬件就了解到这个数据是要进行发送的,所以之后只要将发送使能寄存器置1,硬件就会自己拿这个描述符的地址中的数据进行发送。
描述符具体是什么样的其实代码的注释中已有说明,但是不同IP核版本还是略有差异:
上图是1000/100/10中的描述符划分:
下图是dwmac4中的描述符划分:
其实dw网卡里,DMA更贴近于数据,而MAC更贴近于功能,你会发现一些功能复杂操作都在 MAC_OPS 中进行实现(流控,混杂,过滤,计数),而对于数据的复杂操作基本都在 DMA_OPS 中实现(帧的快速发送),而描述符作为功能和数据的载体,承载了数据的绝大部分信息,但是描述符的4个区域还是略显不够,所以在1000/100/10中,会有两种描述符模式。
NORMAL 和 ENHANCE
Normal 仅包含了基本功能,像时间戳就没有,而 Enhance 有额外的 四个区域 :des4、des5、des6、des7
上图是1000/100/10中的ENHANCE描述符划分:
下图是dwmac4中的ENHANCE描述符划分:
到此为止,dw网卡算是有了一个大概的轮廓,具体还需要再深入的分析。