这里,将会以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);
// ...
//}
...
}
当网卡设备注册成功之后,接下来对网卡设备的操作就会按照文件系统的模式来运行。