Device Discovery and Dynamic Network Interface Driver Initialization - Linux

We don’t attempt to explain every type of hardware supported by the Linux operating system. However, it is useful to see how a network interface driver is automatically configured and how a device is matched by PCI ID to the correct driver. We use a PCI device as an example because most common network interface devices including Ethernet chips are implemented as PCI devices. This section explains what happens for one method of discovering a PCI device. We examine how a PCI device is matched and the driver’s initialization function is called. In general, a device is detected on the PCI bus by matching the vendor and device IDs in the PCI configuration space, on the PCI card with an internally stored value in the network device driver. The main match is on the PCI device ID and vendor codes, both of which are 16-bit numbers, but the PCI vendor and class can be matched, too. For an example, we will use the IDs for one of the Intel Ethernet chips handled by the e100 driver. These values are shown in Table . We will describe the things that happen when a device is matched. Figure shows a typical initialization sequence for a network interface driver module for a PCI hardware device.

PCI device driver initialization sequence.

PCI device driver initialization sequence.

PCI Device ID Structure for an Ethernet Chip

PCI Device ID Structure for an Ethernet Chip

Let’s begin with the module initialization before the hardware is discovered. Each network driver, implemented as a module, must contain a driver-specific module initialization function, which is called when the kernel loads the network driver module either at boot time or later. For example, we will be using the e100 Ethernet driver. We will look at the module initialization function e100_init_module in file linux/drivers/net/e100/e100_main.c. To begin the initialization process, if the e100 driver is statically linked into the kernel, e100_init_module is called by the kernel. However, if the driver is a module, this function is called when the insmod(8) or modprob(8) utilities are run to install the module. In our example, the driver name is initialized to "e100", id_table is initialized to e100_id_table, and probe to the function e100_found1. E100_found1 is the function that actually does the PCI device match.

The first thing we do is call the generic function, pci_module_init, which does most of the actual work of registering the PCI device.

Pci_module_init is defined in linux/include/linux/pci.h. It places a pointer to this particular PCI driver on a linked list of PCI drivers, and this is how the device driver registers both the PCI device ID and the vendor code with the Linux kernel. Later, the ID and vendor are matched when a PCI device is discovered or plugged in (if it is hot-pluggable).

inline int pci_module_init(struct pci_driver *drv);

The only thing this function does is call pci_register_driver, defined in the file linux/drivers/pci.pci_driver.c to register the driver, initialize the common fields in the pci_driver structure, and add the driver to the list of registered drivers. Pci_register_driver returns the number of PCI devices that are claimed by the driver during registration, and even if its function returns a zero, the driver remains registered.

We initialize the common fields in the pci_driver structure including the probe field, which is called when a matching device is discovered on the PCI bus. Notice that the field probe does not point directly to e100_found1. Instead, it points to a generic pci probe function, pci_device_probe, which in turn calls e100_found1 through the probe field of drv. when the device is matched.

This is where we actually register the driver with the kernel.

One of the fields in the pci_driver structure, probe, points to a function containing the driver’s initialization entry point, which is called after a match is detected between the PCI device’s configuration space and the driver’s device ID structure. One of the PCI ID matches for the e100 driver is shown in Table 4.3. The sequence of events for this initialization is shown in Figure 4.2. Another way to show how this sequence works is to look at the code from one of the Ethernet drivers in Linux. For our example, we will look at the e100 driver. Once this sequence of steps is complete, the driver is matched to its device and the driver initialization can proceed. A module for a PCI driver statically defines an instance of the pci_driver structure.

struct pci_driver {

The first field, node, is the head of the list of all the PCI drivers.

struct list_head node;

Name is the driver name string. Id_table is the pointer to the PCI configuration space information and must not be NULL for the probe function to be called. One example of the id_table entry for one of the Ethernet chips supported by our e100 driver is shown in Figure 4.2. Probe points to the function that is called when a matching device is discovered or, if the device is hot-pluggable, when it is inserted. Remove must not be NULL for hot-plug devices.

The next function, remove, is called when a hot-swap capable device is removed from the PCI bus. Suspend is called when a device is suspended, and resume is called when a device is woken up.

The next field, enable_wake, points to the function that enables or disables the wake_up event.

The pci_driver structure is initialized as a static structure. Here we show that the e100 module initializes the fields in the pci_driver structure. This is done in the file linux/drivers/net/e100/e100_main.c.

We can see that the probe field is initialized to e100_found1, which is called when the device is discovered or plugged in (if it is hot-pluggable).


All rights reserved © 2018 Wisdom IT Services India Pvt. Ltd DMCA.com Protection Status

Linux Topics