本文共 3070 字,大约阅读时间需要 10 分钟。
这里,将会以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); // ... //} ...}
当网卡设备注册成功之后,接下来对网卡设备的操作就会按照文件系统的模式来运行。
转载地址:http://wpxii.baihongyu.com/