/**
* 这些code 在 kernel中是怎么添加的、怎么匹配的、probe remove pm 是怎么被调用的,大家都十分熟悉了,
* 这里就不在赘述了. 详情参考 :
* https://blog.csdn.net/leesagacious/article/details/48306889
* https://blog.csdn.net/leesagacious/article/details/50246789
*/
static struct platform_driver macb_driver = {
.probe = macb_probe,
.remove = macb_remove,
.driver = {
.name = "macb",
.of_match_table = of_match_ptr(macb_dt_ids),
.pm = &macb_pm_ops,
},
};
module_platform_driver(macb_driver);
先来看一张简单的框图, 先欣赏linux kernel 实现,然后欣赏 verilog 实现.
phy_device 最里层当然是封装了struct device, 因为它要往设备驱动模型中放入这个设备.
/*
* #define PHY_MAX_ADDR 32
* 这个 32 意思很明确了, MDIO bus仅支持一个Mac作为主设备, 最多32个Phy做从设备.
*
* mii_bus 提供了read() 来读取 PHY Identifier Register的值
* 具体到 KSZ9031RNX 就是macb_mdio_read ()函数. 它在 macb_mii_init() 函数中被赋值.
* ixgbe网卡则是 ixgbe_x550em_a_mii_bus_read()函数
*/
for (i = 0; i < PHY_MAX_ADDR; i++) {
phydev = mdiobus_scan(bp->mii_bus, i);
if (IS_ERR(phydev) &&
PTR_ERR(phydev) != -ENODEV) {
ret = PTR_ERR(phydev);
break;
}
}
phy 状态’迁移变化
NAPI示意
在调用pool之前,需要一个条件
结构关系
/**
* 该函数在蔽中断环境下运行.
* 其目的是为了触发 Rx 软中断.
*/
static inline void ____napi_schedule(struct softnet_data *sd,
struct napi_struct *napi)
{
/**
* 把 napi_struct 挂到 softnet_data 持有的 poll_list 链表上.
*
* 在 net_rx_action() 中遍历该 poll_list,取出entry. 即 struct napi_struct。
* 执行该entry持有的 poll(). 即执行 macb_poll().
* macb_poll() 是在 netif_napi_add()中被初始化的. (KSZ9031RN)
* macb_init()
* {
* netif_napi_add();
* }
*/
list_add_tail(&napi->poll_list, &sd->poll_list);
/**
* 触发 Rx 软中断.
* net_rx_action()会被执行, net_rx_action() 在 net_dev_init()中注册.
* net_dev_init()
* {
* open_softirq(net_rx_action, NET_RX_SOFTIRQ);
* }
*/
__raise_softirq_irqoff(NET_RX_SOFTIRQ);
}
ok, 上面我们简单的介绍了一下PHY 和 napi,下面详细看它的实现. 以及I350网卡的实现.
对比两款NIC驱动实现的异同.
/**
* probe 函数好呀,资源的申请、释放可以胆大点了,
* 即 Use with resource management to register resources.
* 有设备驱动模型在罩着,你放心的去 request 吧. 因为他爸是李刚, 😄
*/
static int macb_probe(struct platform_device *pdev)
{
/*
* 这种操作很恶心,
* 作者就是想匹配不同的mac, 执行不同的init()函数、clk_init()来初始化mac
* 作者的思路很简单, 匹配不上, 你就用默认的.
*/
const struct macb_config *macb_config = &default_gem_config;
/*
* mac和clk初始化在这里直接使用默认的macb_init()、macb_clk_init().
* ok吧! cadence 这company设计的让人汗颜 . 尤其是 5 个 二级指针.
* 这code与Intel Corporation 差远了 .......
* 下面的变量还有很多, 这儿不一一说明, 用到在说吧.
*/
int (*clk_init)(struct platform_device *, struct clk **,
struct clk **, struct clk **, struct clk **,
struct clk **) = macb_config->clk_init;
int (*init)(struct platform_device *) = macb_config->init;
struct device_node *np = pdev->dev.of_node;
/*
* ok. 获取 mac 基地址. 常规操作.
* 当然, 根节点的子节点被看作总线节点,(of_platform_bus_create(...))
* 被转换为platform device. (chosen、memory 等除外)
* gem3: ethernet@ff0e0000 {
* compatible = "cdns,zynqmp-gem", "cdns,gem";
* status = "disabled";
* interrupt-parent = <&gic>;
* interrupts = <0 63 4>, <0 63 4>;
* reg = <0x0 0xff0e0000 0x0 0x1000>;
* clock-names = "pclk", "hclk", "tx_clk";
* .....
* };
*/
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
/*
* 申请使用该memory + 映射.
* 这个操作是非常耗时的, 它底层执行ioremap().会导致建立页表的操作
*/
mem = devm_ioremap_resource(&pdev->dev, regs);
if (IS_ERR(mem))
return PTR_ERR(mem);
if (np) {
}
}
/*
* 和上面的probe有明显的不同,参数是两个,很显然,PCIe来了.
*/
static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
}