Linux_rtl8169

这里,将会以r8169_main.c文件为例,来说明Realtek_816x系列网卡的驱动注册与运行。在内核当中,对Realtek_816x系列网卡进行了抽象的定义,即struct pci_driver rtl8169_pci_driver结构体。同时,内核调用module_pci_driver()宏定义来进阶的执行pci_driver_register()函数来注册驱动模块。

static struct pci_driver rtl8169_pci_driver = {
	.name = MODULENAME,
	.id_table = rtl8169_pci_tbl,	//该变量类型为struct pci_device_id,主要说明该驱动程序可以匹配的设备
	.probe = rtl_init_one,	//设备初始化
	.remove = rtl_remove_one,	//移除设备
	.shutdown = rtl_shutdown,	//设备关机
	.driver.pm = RTL8169_PM_OPS,	//设备的电源管理
};
module_pci_driver(rtl8169_pci_driver);

在注册驱动程序的过程中,内核会遍历所有的PCI设备,并将其与上述结构体中的id_table中的成员变量进行匹配,如果发现设备名称与驱动名称相同,则将该驱动程序与该设备进行关联。

static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
	struct rtl8169_private *tp;
	struct net_device *dev;
	...
	dev = devm_alloc_etherdev(&pdev->dev, sizeof(*tp));
	//申请互联网设备,该函数实际类型为宏定义,其执行的内容为devm_alloc_etherdev_mqs()函数。在该函数中,首先执行devres_alloc()。
	//这里,介绍结构体struct devres_node和struct devres;
	
	//	struct devres_node  {
	//		struct list_head entry;
	//		dr_release_t release;
	//	#ifdef CONFIG_DEBUG_DERVRES
	//		const char *name;
	//		size_t size;
	//	#endif
	//	};
	
	//	struct devres {
	//		struct devres_node node;
	//		u8 __aligned(ARCH_KMALLOC_MINALIGN) data[];
	//	};
	
	//首先调用devres_alloc()函数,申请struct devres结构体对象,对其进行初始化,并将该结构体中的data成员返回。将该成员赋值给类型
	//struct net_deviced **的dr变量。
	//随后调用alloc_etherdev_mqs()函数,该函数申请net_device结构体对象,并对其进行初始化,比如:为该对象分配并初始化消息收发队列等。
	//将该对象赋值给类型为struct net_device *的netdev变量。
	//接着,将pdev->dev与devres->node利用链表进行关联。
	//最后,将netdev返回给dev变量。
	if (!dev)
		return -ENOMEM;
	
	SET_NETDEV_DEV(dev, &pdev->dev);
	//该宏定义执行的内容为:dev->dev.parent = pdev->dev。
	dev->netdev_ops = &rtl_netdev_ops;
	//设置该网络设备的操作,即rtl_netdev_ops结构体中所定义的相关操作。
	
	tp = netdev_priv(dev);
	//该函数返回的指针地址紧挨着dev结构体的结束地址处,即end_address_of_net_device + 1。
	//接下来对tp对象进行初始化赋值。
	tp->dev = dev;
	...
	
	rtl8169_get_mac_version(tp);
	if (tp->mac_version == RTL_GIGA_MAC_NONE)
		return -ENODEV;
	...
	rtl_hw_initalize(tp);
	//网卡硬件初始化。
	//static void rtl_hwinitialize(struct rtl8169_private *tp)
	//{
	//	switch (tp->macversion) {
	//		case RTL_GIGA_MAC_VER_49 ... RTL_GIGA_MAC_VER_52:	rtl8168ep_stop_cmac(tp);
	//		case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_48:	rtl_hw_init_8168g(tp); break;
	//		case RTL_GIGA_MAC_VER_60 ... RTL_GIGA_MAC_VER_61:	rtl_hw_init_8125(tp); break;
	//		default: break;
	//}
	rtl_hw_reset(tp);
	pci_set_master(pdev);
	//设置为pci主设备。
	rc = rtl_alloc_irq(tp);
	//申请中断号。
	if (rc < 0) {
		dev_err(&pdev->dev, "Can't allocate interrupt\n");
		return rc;
	}
	...
	rtl_init_mac_address(tp);
	//初始化网卡MAC地址。
	...
	//当执行完上边的初始化赋值操作之后,接下来,需要进行相关的注册操作。
	rc = r8169_mdio_register(tp);
	//mdio总线是网卡中的一种总线形式,主要用于网卡中mac芯片与phy芯片之间的通信。该函数首先申请struct mii_bus结构体,对其进行初始化,进行注册。这里会将该总线对象与传入的tp对象进行关联。
	...
	rc = register_netdev(dev);
	//int register_netdevice(struct net_device *dev)
	//{
	//	...
	//	struct net *net = dev_net(dev);
	//	...
	//	ret = netdev_register_kobject(dev);
	//	...
	//	linkwatch_init_dev(dev);
	//	dev_init_scheduler(dev);
	//	dev_hold(dev);
	//	list_netdevice(dev);
	//	add_device_randomness(dev->dev_addr, dev->addr_len);
	//	...
	//}
	...
}

当网卡设备注册成功之后,接下来对网卡设备的操作就会按照文件系统的模式来运行。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章