這裏,將會以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);
// ...
//}
...
}
當網卡設備註冊成功之後,接下來對網卡設備的操作就會按照文件系統的模式來運行。