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);
	//	...
	//}
	...
}

當網卡設備註冊成功之後,接下來對網卡設備的操作就會按照文件系統的模式來運行。

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