linux网卡驱动

1. 总线、设备和驱动

    1.1 简单介绍

     Linux设备模型中三个很重要的概念就是总线、设备和驱动,即bus,device和driver。它们分别对应的数据结构分别为struct bus_type,struct device和struct device_driver。

     总线是处理器与一个或多个设备之间的通道,在设备模型中,所有的设备都通过总线相连。在最底层,Linux系统中的每一个设备都用device结构的一个实例来表示。而驱动则是使总线上的设备能够完成它应该完成的功能。

     在系统中有多种总线,如PCI总线、SCSI总线等。系统中的多个设备和驱动是通过总线让它们联系起来的。在bus_type中两个很重要的成员就是struct kset drivers和struct kset devices。它分别代表了连接在这个总线上的两个链,一个是设备链表,另一个则是设备驱动链表。也就是说,通过一个总线描述符,就可以找到挂载到这条总线上的设备,以及支持该总线的不同的设备驱动程序。

    1.2 总线、设备与驱动的绑定

     在系统启动时,它会对每种类型的总线创建一个描述符,并将使用该总线的设备链接到该总线描述符的devices链上来。也即是说在系统初始化时,它会扫描连接了哪些设备,并且为每个设备建立一个struce device变量,然后将该变量链接到这个设备所连接的总线的描述符上去。另一方面,每当加载了一个设备驱动,则系统也会准备一个struct device_driver结构的变量,然后再将这个变量也链接到它所在总线的描述符的drivers链上去。

     对于设备来说,在结构体struct device中有两个重要的成员,一个是struct bus_type *bus,另一个是struct device_driver *driver。bus成员就表示该设备是链接到哪一个总线上的,而driver成员就表示当前设备是由哪个驱动程序所驱动的。对于驱动程序来说,在结构体struct device_driver中也有两个成员,struct bus_type *bus和struct list_head devices,这里的bus成员也是指向这个驱动是链接到哪个总线上的,而devices这个链表则是表示当前这个驱动程序可以去进行驱动的那些设备。一个驱动程序可以支持一个或多个设备,而一个设备则只会绑定给一个驱动程序。

     对于device与device_driver之间建立联系的方式,主要有两种方式。第一种,在计算机启动的时候,总线开始扫描连接在其上的设备,为每个设备建立一个struct device变量并链接到该总线的devices链上,然后开始初始化不同的驱动程序,驱动程序到它所在的总线的devices链上去遍历每一个还没有被绑定给某个驱动的设备,然后再查看是否能够支持这种设备,如果它能够支持这种设备,则将这个设备与这个驱动联系起来。即,将这个设备的device变量加到驱动的devices链上,同时让struct device中的device_driver指向当前这个驱动。第二种则是热插拔。也即是在系统运行时插入了设备,此时内核会去查找在该bus链上注册了的device_driver,然后再将设备与驱动联系起来。设备与驱动根据什么规则联系起来,它们是如何被联系起来的代码我们将在后面的章节进行详细的描述。

    1.3 PCI总线

     PCI是一种在CPU与I/O设备之间进行高速数据传输的一种总线。有很多设备都是使用PCI总线的,网卡就是其中之一。我们在前面讲了那些总线、设备与驱动方面的知识,原因就在于网卡是连接到PCI总线上,所以PCI总线、网卡设备以及网卡驱动就成了我们研究网卡的一个很重要的线索,尤其是在网络的链路层部分。下图显示了在一个系统中PCI设备的一个框图:

    

    图1. PCI结构图

     PCI子系统声明了一个bus_type结构,为pci_bus_type。它就是PCI总线的描述符。在这个变量上,链接了PCI设备以及支持PCI设备的驱动程序。

    1.4 PCI设备与驱动

     PCI设备通常由一组参数唯一地标识,它们被vendorID,deviceID和class nodes所标识,即设备厂商,型号等,这些参数保存在pci_device_id结构中。每个PCI设备都会被分配一个pci_dev变量,内核就用这个数据结构来表示一个PCI设备。

     所有的PCI驱动程序都必须定义一个pci_driver结构变量,在该变量中包含了这个PCI驱动程序所提供的不同功能的函数,同时,在这个结构中也包含了一个device_driver结构,这个结构定义了PCI子系统与PCI设备之间的接口。在注册PCI驱动程序时,这个结构将被初始化,同时这个pci_driver变量会被链接到pci_bus_type中的驱动链上去。

     在pci_driver中有一个成员struct pci_device_id *id_table,它列出了这个设备驱动程序所能够处理的所有PCI设备的ID值。

    1.5 PCI设备与驱动的绑定过程

     下面描述一下对于PCI设备与驱动绑定的过程。首先在系统启动的时候,PCI总线会去扫描连接到这个总线上的设备,同时为每一个设备建立一个pci_dev结构,在这个结构中有一个device成员,并将这些pci_dev结构链接到PCI总线描述符上的devices链。如下图所示:

    

    图2. 将设备链接到总线描述符上

     第二步是当PCI驱动被加载时,pci_driver结构体将被初始化,这一过程在函数pci_register_driver中:

     drv->driver.bus = &pci_bus_type;

     drv->driver.probe = pci_device_probe;

     最后会调用driver_register(&drv->driver)将这个PCI驱动挂载到总线描述符的驱动链上。同时在注册的过程中,会根据pci_driver中的id_table中的ID值去查看该驱动支持哪些设备,将这些设备挂载到pci_driver中的devices链中来。如下图所示:

    

    图3. 加载设备驱动

     对于不同的设备,可能驱动程序也不一样,因此,对于上图中的Dev3,可能就需要另外一个驱动程序来对其进行驱动。所以当加载了Dev3的驱动程序时,其示意图如下图所示:

    

    图4. 不同的设备驱动

     上面这三个示意图就描述了总线、设备以及驱动在系统中是如何进行相互联系的。前面对于驱动注册这些函数的描述较为简单,因为网卡是一个PCI设备,因此在后面具体地讲到网卡注册时再来详细地讲解和PCI相关的注册等函数。

    1.6 小结

     本部分主要讲解了总线、设备以及驱动方面的一些知识,由于网卡是一个PCI设备,因此具体地讲到了一点PCI总线、PCI设备及相应的PCI驱动方面的知识,但是由于PCI本身就是很大的一个子系统,因此这里不可能对其进行详细地讲解,在后面对网卡的分析中,将对网卡中涉及到的和PCI相关的部分进行讲解。这一节还是起一个引子的作用。


 2. 网卡在PCI层的注册

    2.1 数据结构

     前面第一章讲了总线、设备以及驱动方面的关系,也讲到了大多数网卡设备实际上是一个PCI设备。因此,本章就讲解网卡设备在注册时是如何注册到PCI总线上去的。在这里,以Intel的E100网卡驱动进行讲解。

     前面讲到每个PCI设备都由一组参数唯一地标识,这些参数保存在结构体pci_device_id中,如下所示:

    struct pci_device_id {

     __u32 vendor, device; /* Vendor and device ID or PCI_ANY_ID*/

     __u32 subvendor, subdevice; /* Subsystem ID's or PCI_ANY_ID */

     __u32 class, class_mask; /* (class,subclass,prog-if) triplet */

     kernel_ulong_t driver_data; /* Data private to the driver */

    };

     每个PCI设备驱动都有一个pci_driver变量,它描述了一个PCI驱动的信息,如下所示:

    struct pci_driver {

     struct list_head node;

     char *name;

     const struct pci_device_id *id_table; /* must be non-NULL for probe to be called */

     int (*probe) (struct pci_dev *dev, const struct pci_device_id *id); /* New device inserted */

     void (*remove) (struct pci_dev *dev); /* Device removed (NULL if not a hot-plug capable driver) */

     int (*suspend) (struct pci_dev *dev, pm_message_t state); /* Device suspended */

     int (*suspend_late) (struct pci_dev *dev, pm_message_t state);

     int (*resume_early) (struct pci_dev *dev);

     int (*resume) (struct pci_dev *dev); /* Device woken up */

     int (*enable_wake) (struct pci_dev *dev, pci_power_t state, int enable); /* Enable wake event */

     void (*shutdown) (struct pci_dev *dev);

    

     struct pci_error_handlers *err_handler;

     struct device_driver driver;

     struct pci_dynids dynids;

    

     int multithread_probe;

    };

     每个PCI驱动中都有一个id_table成员变量,记录了当前这个驱动所能够进行驱动的那些设备的ID值。

     对于E100网卡驱动来说,它的pci_driver变量定义为:

    static struct pci_driver e100_driver = {

     .name = DRV_NAME,

     .id_table = e100_id_table,

     .probe = e100_probe,

     .remove = __devexit_p(e100_remove),

    #ifdef CONFIG_PM

     /* Power Management hooks */

     .suspend = e100_suspend,

     .resume = e100_resume,

    #endif

     .shutdown = e100_shutdown,

     .err_handler = &e100_err_handler,

    };

     里面e100_id_table就表示该E100驱动所能够支持的PCI设备的ID号,其定义为:

    #define INTEL_8255X_ETHERNET_DEVICE(device_id, ich) {\

     PCI_VENDOR_ID_INTEL, device_id, PCI_ANY_ID, PCI_ANY_ID, \

     PCI_CLASS_NETWORK_ETHERNET

    static struct pci_device_id e100_id_table[] = {

     INTEL_8255X_ETHERNET_DEVICE(0x1029, 0),

     INTEL_8255X_ETHERNET_DEVICE(0x1030, 0),

     …

     { 0, }

    };

     当PCI层检测到一个PCI设备能够被某PCI驱动所支持时(这是通过函数pci_match_one_device来进行检测的),就会调用这个PCI驱动上的probe函数,在该函数中会对该特定的PCI设备进行一些具体的初始化等操作。比如对于E100设备驱动来说,其probe函数为e100_probe。在这个函数中,会对网卡设备进行初始化。

     e100_probe主要就涉及到网卡设备net_device的初始化,我们现在先来关注一下从网卡注册一直到调用e100_probe这一个过程的整个流程。

    2.2 E100初始化

     E100驱动程序的初始化是在函数e100_init_module()中的,如下:

    static int __init e100_init_module(void)

    {

     if(((1

     printk(KERN_INFO PFX "%s, %s\n", DRV_DESCRIPTION, DRV_VERSION);

     printk(KERN_INFO PFX "%s\n", DRV_COPYRIGHT);

     }

     return pci_register_driver(&e100_driver);

    }

     在这个函数中,调用了pci_register_driver()函数,对e100_driver这个驱动进行注册。

    2.3 PCI注册

     在前面我们已经看到,PCI的注册就是将PCI驱动程序挂载到其所在的总线的drivers链,同时扫描PCI设备,将它能够进行驱动的设备挂载到driver上的devices链表上来,这里,我们将详细地查看这整个流程的函数调用关系。

     pci_register_driver()->__pci_register_driver()

    /**

     * __pci_register_driver - register a new pci driver

     * @drv: the driver structure to register

     * @owner: owner module of drv

     * @mod_name: module name string

     *

     * Adds the driver structure to the list of registered drivers.

     * Returns a negative value on error, otherwise 0.

     * If no error occurred, the driver remains registered even if

     * no device was claimed during registration.

     */

    int __pci_register_driver(struct pci_driver *drv, struct module *owner, const char *mod_name);

     在函数中有几个初始化语句:

     drv->driver.name = drv->name;

     drv->driver.bus = &pci_bus_type;

     drv->driver.owner = owner;

     drv->driver.mod_name = mod_name;

     即是将PCI设备中的driver变量的总线指向pci_bus_type这个总线描述符,同时设置驱动的名字等。

     pci_bus_type定义如下:

    struct bus_type pci_bus_type = {

     .name = "pci",

     .match = pci_bus_match,

     .uevent = pci_uevent,

     .probe = pci_device_probe,

     .remove = pci_device_remove,

     .suspend = pci_device_suspend,

     .suspend_late = pci_device_suspend_late,

     .resume_early = pci_device_resume_early,

     .resume = pci_device_resume,

     .shutdown = pci_device_shutdown,

     .dev_attrs = pci_dev_attrs,

    };

     然后再调用函数driver_register(&drv->driver);通过这个函数将这个PCI驱动中的struct device_driver driver成员变量注册到系统中去。

     pci_register_driver()->__pci_register_driver()->driver_register()

     driver_register()代码如下:

    /**

     * driver_register - register driver with bus

     * @drv: driver to register

     *

     * We pass off most of the work to the bus_add_driver() call,

     * since most of the things we have to do deal with the bus

     * structures.

     *

     * The one interesting aspect is that we setup @drv->unloaded

     * as a completion that gets complete when the driver reference

     * count reaches 0.

     */

    int driver_register(struct device_driver * drv)

    {

     if ((drv->bus->probe && drv->probe) ||

     (drv->bus->remove && drv->remove) ||

     (drv->bus->shutdown && drv->shutdown)) {

     printk(KERN_WARNING "Driver '%s' needs updating - please use bus_type methods\n", drv->name);

     }

     klist_init(&drv->klist_devices, NULL, NULL);

     init_completion(&drv->unloaded);

     return bus_add_driver(drv);

    }

     klist_init()是为设备驱动的klist_devices成员进行初始化,这个klist_devices是一个对链表进行操作的包裹结构,它会链接这个驱动能够支持的那些设备。

     最后就调用bus_add_driver()函数。这个函数的功能就是将这个驱动加到其所在的总线的驱动链上。

     pci_register_driver()->__pci_register_driver()->driver_register()->bus_add_driver()->driver_attach()

     在bus_add_driver()函数中,最重要的是调用driver_attach()函数,其定义如下:

    /**

     * driver_attach - try to bind driver to devices.

     * @drv: driver.

     *

     * Walk the list of devices that the bus has on it and try to

     * match the driver with each one. If driver_probe_device()

     * returns 0 and the @dev->driver is set, we've found a

     * compatible pair.

     */

    int driver_attach(struct device_driver * drv)

    {

     return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);

    }

     该函数遍历这个驱动所在的总线上的所有设备,然后将这些设备与当前驱动进行匹配,以检测这个驱动是否能够支持某个设备,也即是将设备与驱动联系起来。

     bus_for_each_dev函数是扫描在drv->bus这个总线上的所有设备,然后将每个设备以及当前驱动这两个指针传递给__driver_attach函数。

     pci_register_driver()->__pci_register_driver()->driver_register()->bus_add_driver()->driver_attach()->__driver_attach()

     __driver_attach()函数是将驱动与设备联系起来的函数。

    static int __driver_attach(struct device * dev, void * data)

    {

     struct device_driver * drv = data;

    

     /*

     * Lock device and try to bind to it. We drop the error

     * here and always return 0, because we need to keep trying

     * to bind to devices and some drivers will return an error

     * simply if it didn't support the device.

     *

     * driver_probe_device() will spit a warning if there

     * is an error.

     */

    

     if (dev->parent) /* Needed for USB */

     down(&dev->parent->sem);

     down(&dev->sem);

     if (!dev->driver)

     driver_probe_device(drv, dev);

     up(&dev->sem);

     if (dev->parent)

     up(&dev->parent->sem);

    

     return 0;

    }

     在函数中有两条语句:

     if (!dev->driver)

     driver_probe_device(drv, dev);

     也即是判断当前设备是否已经注册了一个驱动,如果没有注册驱动,则调用driver_probe_device()函数。

     pci_register_driver()->__pci_register_driver()->driver_register()->bus_add_driver()->driver_attach()->__driver_attach()->driver_probe_device()

     如下:

    

    /**

     * driver_probe_device - attempt to bind device & driver together

     * @drv: driver to bind a device to

     * @dev: device to try to bind to the driver

     *

     * First, we call the bus's match function, if one present, which should

     * compare the device IDs the driver supports with the device IDs of the

     * device. Note we don't do this ourselves because we don't know the

     * format of the ID structures, nor what is to be considered a match and

     * what is not.

     *

     * This function returns 1 if a match is found, an error if one occurs

     * (that is not -ENODEV or -ENXIO), and 0 otherwise.

     *

     * This function must be called with @dev->sem held. When called for a

     * USB interface, @dev->parent->sem must be held as well.

     */

    int driver_probe_device(struct device_driver * drv, struct device * dev)

    {

     struct stupid_thread_structure *data;

     struct task_struct *probe_task;

     int ret = 0;

    

     if (!device_is_registered(dev))

     return -ENODEV;

     if (drv->bus->match && !drv->bus->match(dev, drv))

     goto done;

    

     pr_debug("%s: Matched Device %s with Driver %s\n",

     drv->bus->name, dev->bus_id, drv->name);

    

     data = kmalloc(sizeof(*data), GFP_KERNEL);

     if (!data)

     return -ENOMEM;

     data->drv = drv;

     data->dev = dev;

    

     if (drv->multithread_probe) {

     probe_task = kthread_run(really_probe, data,

     "probe-%s", dev->bus_id);

     if (IS_ERR(probe_task))

     ret = really_probe(data);

     } else

     ret = really_probe(data);

    

    done:

     return ret;

    }

     该函数首先会调用总线上的match函数,以判断当前的PCI驱动能否支持该PCI设备,如果可以,则继续往后面执行。

     drv->bus->match函数也即是pci_bus_type中的match成员变量,它为pci_bus_match函数。

     pci_register_driver()->__pci_register_driver()->driver_register()->bus_add_driver()->driver_attach()->__driver_attach()->driver_probe_device()->pci_bus_match()

    /**

     * pci_bus_match - Tell if a PCI device structure has a matching PCI device id structure

     * @dev: the PCI device structure to match against

     * @drv: the device driver to search for matching PCI device id structures

     *

     * Used by a driver to check whether a PCI device present in the

     * system is in its list of supported devices. Returns the matching

     * pci_device_id structure or %NULL if there is no match.

     */

    static int pci_bus_match(struct device *dev, struct device_driver *drv)

    {

     struct pci_dev *pci_dev = to_pci_dev(dev);

     struct pci_driver *pci_drv = to_pci_driver(drv);

     const struct pci_device_id *found_id;

    

     found_id = pci_match_device(pci_drv, pci_dev);

     if (found_id)

     return 1;

    

     return 0;

    }

     pci_bus_match函数的作用就是将PCI设备与PCI驱动进行比较以检查该驱动是否能够支持这个设备。在函数的最前面是两个宏to_pci_dev和to_pci_driver。因为在函数执行的过程中,虽然最开始传进来的是pci_driver结构与pci_dev结构,但是在执行的时候却取了这两个结构体中的device_driver和device成员变量,所以现在就要通过这两个成员变量找到之前对应的pci_driver和pci_dev结构的地址。

    #define to_pci_dev(n) container_of(n, struct pci_dev, dev)

    #define to_pci_driver(drv) container_of(drv,struct pci_driver, driver)

     这两个宏在 3rd书上有相应的讲解,这里也就是找到E100的pci_driver:e100_driver以及该网卡设备的pci_dev结构。现在就要对它们进行比较以看它们之间是否能够联系起来。这是通过函数pci_match_device实现的。

     pci_register_driver()->__pci_register_driver()->driver_register()->bus_add_driver()->driver_attach()->__driver_attach()->driver_probe_device()->pci_bus_match()->pci_match_device()

    

    /**

     * pci_match_device - Tell if a PCI device structure has a matching PCI device id structure

     * @drv: the PCI driver to match against

     * @dev: the PCI device structure to match against

     *

     * Used by a driver to check whether a PCI device present in the

     * system is in its list of supported devices. Returns the matching

     * pci_device_id structure or %NULL if there is no match.

     */

    const struct pci_device_id *pci_match_device(struct pci_driver *drv,

     struct pci_dev *dev)

    {

     struct pci_dynid *dynid;

    

     /* Look at the dynamic ids first, before the static ones */

     spin_lock(&drv->dynids.lock);

     list_for_each_entry(dynid, &drv->dynids.list, node) {

     if (pci_match_one_device(&dynid->id, dev)) {

     spin_unlock(&drv->dynids.lock);

     return &dynid->id;

     }

     }

     spin_unlock(&drv->dynids.lock);

    

     return pci_match_id(drv->id_table, dev);

    }

     pci_match_one_driver函数的作用是将一个PCI设备与PCI驱动进行比较,以查看它们是否相匹配。如果相匹配,则返回匹配的pci_device_id结构体指针。

     此时,如果该PCI驱动已经找到了一个可以想符的PCI设备,则返回,然后再退回到之前的driver_probe_device函数中。在该函数最后将调用really_probe函数。将device_driver与device结构体指针作为参数传递到这个函数中。下面几行是调用驱动或者总线的probe函数来扫描设备。

     pci_register_driver()->__pci_register_driver()->driver_register()->bus_add_driver()->driver_attach()->__driver_attach()->driver_probe_device()->really_probe()

     在函数really_probe()中:

     if (dev->bus->probe) {

     ret = dev->bus->probe(dev);

     if (ret)

     goto probe_failed;

     } else if (drv->probe) {

     ret = drv->probe(dev);

     if (ret)

     goto probe_failed;

     }

     此时的dev->bus为pci_bus_type,其probe函数则对应为:pci_device_probe。

     pci_register_driver()->__pci_register_driver()->driver_register()->bus_add_driver()->driver_attach()->__driver_attach()->driver_probe_device()->really_probe()->pci_device_probe()

     同样,在该函数中会获得当前的PCI设备的pci_dev结构体指针以及PCI驱动程序的pci_driver结构体指针。分别使用宏to_pci_dev和to_pci_driver。最后则调用函数__pci_device_probe。在该函数中还会调用函数pci_call_probe,这是最后的函数

     pci_register_driver()->__pci_register_driver()->driver_register()->bus_add_driver()->driver_attach()->__driver_attach()->driver_probe_device()->really_probe()->pci_device_probe()->__pci_device_probe()->pci_call_probe()

     在函数pci_call_probe里有一条语句:

    static int pci_call_probe(struct pci_driver *drv, struct pci_dev *dev,

     const struct pci_device_id *id)

    {

     int error;

    /* 省略 */

     error = drv->probe(dev, id);

    

     在此处就调用了pci_driver的probe函数,对于这里的E100驱动来说,它的probe函数是最开始注册的e100_probe函数,在该函数中会完成对网卡设备net_device的初始化等操作。

     pci_register_driver()->__pci_register_driver()->driver_register()->bus_add_driver()->driver_attach()->__driver_attach()->driver_probe_device()->really_probe()->pci_device_probe()->__pci_device_probe()->pci_call_probe()->e100_probe()

    

     到这里,我们对网卡驱动的PCI层的初始化分析就告一个段落了,剩下的部分就是网卡驱动对网卡设备本身的初始化等操作。

    2.4 函数调用流程图

     在这里,为网卡在PCI层的注册画了一个函数调用的流程图,能够更直观地展现网卡从注册到调用其自身的网卡初始化的这一个函数调用过程。

    

    

    











常情况下,在 Linux 操作系统中,网卡的驱动程序都是通过加载模块来实现的,我们可以在提示符下,键入 lsmod 命令来检查当前使用的网卡对应的模块名称,例如:

Module Size Used by Tainted: P 
nls_iso8859-1 3516 1 (autoclean)
nls_cp437 5148 1 (autoclean)
vfat 13004 1 (autoclean)
fat 38872 0 (autoclean) [vfat]
parport_pc 19076 1 (autoclean)
lp 9028 0 (autoclean)
parport 37088 1 (autoclean) [parport_pc lp]
autofs 13364 0 (autoclean) (unused)
basp 60416 1 
bcm5700 105512 1 
e100 56164 1 
ipt_REJECT 4632 1 (autoclean)
ipt_state 1080 1 (autoclean)
ip_conntrack 27304 1 (autoclean) [ipt_state]
iptable_filter 2412 1 (autoclean)
ip_tables 15776 3 [ipt_REJECT ipt_state iptable_filter]
floppy 58160 2 (autoclean)
microcode 4724 0 (autoclean)
keybdev 2976 0 (unused)
mousedev 5524 0 (unused)
hid 22212 0 (unused)
input 5888 0 [keybdev mousedev hid]
ehci-hcd 20104 0 (unused)
usb-uhci 26412 0 (unused)
usbcore 79392 1 [hid ehci-hcd usb-uhci]
ext3 91592 2 
jbd 52336 2 [ext3]
aic7xxx 163440 3 
sd_mod 13744 6 
scsi_mod 108840 2 [aic7xxx sd_mod]


其中兰色的两行是 broadcom 网卡和 intel 网卡的驱动程序,但是,我们可以看到,lsmod 并不能列出对应驱动程序的版本信息,而当我们使用一此应用程序时,例如 Broadcom  BASP,这些程序的某些功能对网卡的驱动程序版本有一定的要求,这时,我们就需要查看当前系统中使用的网卡驱动程序的版本信息。这时,我们需要用到一个linux 下用来查询和更改以太网卡设备设置的小工具 ---- ethtool
首先,键入 # rpm -qa | grep ethtool 或者 # rpm -q ethtool 来检查系统中是否安装了对应的 rpm 包,如果系统中已经安装了ethtool 的包,会列出 ethtool-x.x-x 的信息,以 Red Hat Enterprise Linux 3 为例,会列出 ethtool-1.8-2 的信息,而如果系统中没有安装 ethtool 包,可以插入 Red Hat Enterprise Linux 3 的第二张安装光盘,先键入 # mount /dev/cdrom  mount光驱设备,再用命令 # rpm -ivh /mnt/cdrom/RedHat/RPMS/ethtool-1.8-2.i386.rpm 或者 # rpm -ivh /mnt/cdrom/RedHat/RPMS/ethtool* 来安装。
如果系统中已经安装了 ethtool,只需键入下面的命令:
# ethtool –i eth0 
或者 
# ethtool -i eth1
就会列出网卡 eth0 的驱动程序版本信息。
例如:
driver: bcm5700
version: 7.1.9
firmware-version: 
bus-info: 02:03.0

或者:
driver: e100
version: 2.3.13-k1-1
firmware-version: N/A
bus-info: 02:00.0




 现有的linux已经自带了许多网卡驱动,但还是会有一些不常见的网卡,这时就需要自己来找网卡驱动安装。

   linux下网卡驱动安装及问题:

1、首先需要确认网卡是否安装。

   PCI详细信息查看 #lspci               //如果有Ethernet 相关信息说明网卡硬件存在

   网卡详细信息查看 #kudzu --probe --class=network //可以看到网卡的具体型号信息

   注:有些集成在主板的网卡,需要在BIOS设置中把网卡激活;

   BIOS主菜单->Integrated Peripherals->onbroad lan  设置Enabled

2、查看linux自带网卡驱动。

   linux会有很多自带驱动 #cd /lib/modules//kernel/drivers/net/

   如有相应的网卡驱动,一般会在安装系统时自动安装;也可以手动自己安装;

   在上面的目录下 #insmod 网卡驱动程序(2.4内核是.o文件、2.6内核是.ko文件)

   lsmod 查看驱动安装

3、安装网卡驱动

   一些不常见的网卡就需要自己动手安装了

  首先确定您的Linux系统当中已经下列软件的存在:kernel-source、kernel、gcc、make

  下载网卡驱动 到src目录

  #make install

  编译成功产生.o或 .ko文件

  #insmod .o或 .ko

  #lsmod 确认驱动是否安装

4、IP配置

  ifconfig 进行配置或者配置/etc/sysconfig/network-scripts/ifcfg-eth0  文件

 

注:有时重启系统网卡驱动不自动加载,检查/etc/modprobe.conf 文件中是否有alias eth0 XXX

没有手动添加一条 (XXX为网卡驱动名字)





Enabling e1000 Gigabit device emulation in Citrix XenServer

The following howto describes modification to critical system software. If you choose to follow this guide, you do so at your own risk.

The problem

The commercial version of the Citrix XenServer does not allow you to choose the type of ethernet adapter to emulate within your VM. The standard device that is emulated is a Realtek 8139 (RTL8139), which is a 100Mbit/sec Fast Ethernet card.

Citrix themselves do not view this as a major issue, as they expect you to install paravirtualised drivers within your guest operating system. This is usually a very good idea and just fine if you're using Windows, or a major supported OS such as Red Hat, CentOS or Ubuntu. Under these Linux operating systems, your entire kernel must be replaced by a Citrix supplied kernel. The paravirtualised drivers will outperform any emulated device.

However, if you're running a system with a customised non-standard kernel that doesn't support Citrix Xen paravirtualisation, you'll be stuck with a 100Mbit/sec bottleneck in your network. Sure, you can go and rebuild your kernel with the right paravirtualised drivers, but that's not always an option.

Those familiar with the open source version of Xen will know that the underlying QEMU device emulation that Xen uses can emulate an Intel 1Gbit/sec adapter, called "e1000". Apart of the additional speed, this device also supports jumbo ethernet frames. This emulation mode is available under Citrix XenServer, but is a hidden feature, due to hard-coding of the Realtek driver option.

Enabling e1000 emulation

You'll need to ssh into your Citrix server and become root. Then do the following:

First rename /usr/lib/xen/bin/qemu-dm to /usr/lib/xen/bin/qemu-dm.orig

# mv /usr/lib/xen/bin/qemu-dm /usr/lib/xen/bin/qemu-dm.orig

Then make a replacement /usr/lib/xen/bin/qemu-dm file like this

#!/bin/bash
oldstring=$@
newstring=${oldstring//rtl8139/e1000}
exec /usr/lib/xen/bin/qemu-dm.orig $newstring

Then chmod (to make it executable) and chattr it (to stop it being overwritten):

# chmod 755 /usr/lib/xen/bin/qemu-dm
# chattr +i /usr/lib/xen/bin/qemu-dm

If you now shutdown and re-start your Citrix virtual machines, they will have an emulated e1000 device.

Warning

The "chattr" line above makes the replacement file "immutable". This means that the file cannot be overwritten, prevents the loss of this modification in the event of a system update.

However, this may cause updates provided by Citrix to fail at the point of installation. An alternative approach would be leave the file unprotected, and re-applying this modification after Citrix-supplied updates have been applied.

The remove the protection from the file, do the following:

 

# chattr -i /usr/lib/xen/bin/qemu-dm



今天装了个RHCL5,不能加载网卡就更别说驱动了,用以下命令查看下网卡信息:

一、找硬件

[root@localhost ~]# kudzu --probe --class=network

class: NETWORK
bus: PCI
detached: 0
device: eth0
desc: "Realtek Semiconductor Co., Ltd. RTL-8110SC/8169SC Gigabit Ethernet"
vendorId: 10ec
deviceId: 8167
subVendorId: 147b
subDeviceId: 1c2a
pciType: 1
pcidom:    0
pcibus: 1
pcidev: 8
pcifn: 0

[root@localhost ~]# lspci | grep Ethernet

01:08.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL-8110SC/8169SC Gigabit Ethernet (rev 10)

我的测试机器的网卡型号是:RTL-8110SC/8169SC

二、下载驱动

有了硬件去网上下载相应的驱动,去http://www.realtek.com.tw,我的网卡的驱动下载地址如下:http://www.realtek.com.tw/downloads/downloadsView.aspx?Langid=3&PNid=4&PFid=4&Level=5&Conn=4&DownTypeID=3&GetDown=false&Downloads=true#RTL8110S-32/RTL8169S-32/RTL8110SB(L)/RTL8169SB(L)<br>RTL8110SC(L)/RTL8169SC(L)

LINUX driver for kernel 2.6.x and 2.4.X (support x86 and x64)
6.011.00 2009/7/30 29k GO GO GO

驱动程序为:r8169-6.011.00.tar.bz2

三、装驱动

第一步:添加新硬件,要查看下kernel是不是装的完整,以免后面报和kernel相关错误:

[root@localhost kernel]# rpm -qa | grep kernel

如果装全了,会显示如下几个包:

       kernel-xen-devel-2.6.18-8.el5

       kernel-devel-2.6.18-8.el5

       kernel-2.6.18-8.el5

        kernel-xen-2.6.18-8.el5

       kernel-headers-2.6.18-8.el5

第二步:解压R8169驱动包

[root@localhost ~]# tar jxvf r8169-6.011.00.tar.bz2

进入解压到驱动程序目录,命令:cd r8169-6.003.00

        查看目前是否有网卡驱动模块,命令:lsmod|grep r8169,通常都没有

        执行清除网卡驱动命令:make clean

        执行安装网卡模块命令:make modules

        接着安装:make install

        接着挂接:depmod –a

        挂驱动:insmod ./src/r8168.ko

        查看是否成功:lsmod | grep r8169,如显示有信息则成功

重新启动系统,进去后,就可以看到未被配置的网卡信息了。呵呵,easy.


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值