Follow Techotopia on Twitter

On-line Guides
All Guides
eBook Store
iOS / Android
Linux for Beginners
Office Productivity
Linux Installation
Linux Security
Linux Utilities
Linux Virtualization
Linux Kernel
System/Network Admin
Programming
Scripting Languages
Development Tools
Web Development
GUI Toolkits/Desktop
Databases
Mail Systems
openSolaris
Eclipse Documentation
Techotopia.com
Virtuatopia.com

How To Guides
Virtualization
General System Admin
Linux Security
Linux Filesystems
Web Servers
Graphics & Desktop
PC Hardware
Windows
Problem Solutions

  




 

 

Determining the Correct Module From Scratch

Sometimes you do not have the option of getting a distribution kernel working on a machine in order to determine what kernel modules are needed to drive the hardware. Or you have added new hardware to your system, and you need to figure out what kernel configuration option needs to be enabled to get it to work properly. This section will help you determine how to find that configuration option to get the hardware up and running.

The easiest way to figure out which driver controls a new device is to build all of the different drivers of that type in the kernel source tree as modules, and let the udev startup process match the driver to the device. Once this happens, you should be able to work backwards using the steps just discussed to determine the proper driver needed, and then go back and enable just that driver in the kernel configuration.

But if you do not want to build all drivers, or this does not work for some reason, it will require a bit more work to determine the proper driver that is needed. The following steps are complex and require digging in the kernel source code at times. Do not be afraid of this, it will only help you understand your hardware and the kernel source better.

The steps involved in matching the driver to the device differ depending on the type of device that you are working with. We will discuss the two most common forms of devices in this chapter: PCI and USB devices. The methods described here will also work with other types of devices.

Also, it is very important for the kernel to be able to find all of the filesystems in the system, the most important one being the root filesystem. We will go into how to do this toward the end of the chapter in the section called “Root Filesystem”.

PCI devices

PCI devices are distinguished by vendor ID and device ID; each combination of vendor and device ID could require a unique driver. This is the basis for the research this section shows you how to do.

For this example, let us use a PCI network card that is not working with the currently running kernel version. This example will be different from your situation, with different PCI device and bus ID values, but the steps involved should be relevant to any type of PCI device you wish to find a working driver for.

First, find the PCI device in the system that is not working. To get a list of all PCI devices, use the lspci program. Because we care only about Ethernet PCI devices, we will narrow our search of the PCI devices by searching only for strings containing the term Ethernet (case-insensitive):

$ 
/usr/sbin/lspci | grep -i ethernet 

06:04.0 Ethernet controller: Realtek Semiconductor Co., Ltd.  RTL-8139/8139C/8139C+ (rev 10)

This is the device we wish to get working. [10]

Note

Almost all distributions place the lspci program in the /usr/sbin/ directory, but some place it in other locations. To find out where it is located, enter:

$ 
which lspci

/usr/sbin/lspci

If you are using a distribution that puts it somewhere else, please use that path whenever we discuss using lspci

The first few bits of the lspci output show the PCI bus id for this device, 06:04.0. That is the value we will use when looking through sysfs in order to find out more information about this device.

Go into sysfs where all of the different PCI devices are listed, and look at their names:

$ 
cd /sys/bus/pci/devices/

$ 
ls

0000:00:00.0  0000:00:1d.0  0000:00:1e.0  0000:00:1f.3  0000:06:03.3
0000:00:02.0  0000:00:1d.1  0000:00:1f.0  0000:06:03.0  0000:06:03.4
0000:00:02.1  0000:00:1d.2  0000:00:1f.1  0000:06:03.1  0000:06:04.0
0000:00:1b.0  0000:00:1d.7  0000:00:1f.2  0000:06:03.2  0000:06:05.0

The kernel numbers PCI devices with a leading 0000: that does not show up in the output of the lspci program. [11] So add the leading 0000: onto the number that you found using lspci and go into that directory:

$ 
cd 0000:06:04.0

In this directory, you want to know the values of the vendor and device filenames:

$ 
cat vendor

0x10ec
$ 
cat device

0x8139

These are the vendor and device IDs for this PCI device. The kernel uses these values to match a driver to a device properly. PCI drivers tell the kernel which vendor and device IDs they will support so that the kernel knows how to bind the driver to the proper device. Write them down somewhere, as we will refer to them later.

Now that we know the vendor and product ID for this PCI device, we need to find the proper kernel driver that advertises that it supports this device. Go back to the kernel source directory:

$ 
cd ~/linux/linux-2.6.17.8/

The monst common location for PCI IDs in the kernel source tree is include/linux/pci_ids.h. Search that file for our vendor product number:

$ 
grep -i 0x10ec include/linux/pci_ids.h

#define PCI_VENDOR_ID_REALTEK           0x10ec

The defined value here, PCI_VENDOR_ID_REALTEK is what will probably be used in any kernel driver that purports to support devices from this manufacturer.

To be safe, also look in this file for our device ID, as it is also sometimes described there:

$ 
grep -i 0x8139 include/linux/pci_ids.h

#define PCI_DEVICE_ID_REALTEK_8139      0x8139

That definition will be useful later.

Now look for driver source files referring to this vendor definition:

$ 
grep -Rl PCI_VENDOR_ID_REALTEK *

include/linux/pci_ids.h
drivers/net/r8169.c
drivers/net/8139too.c
drivers/net/8139cp.c

The first file listed here, pci_ids.h does not need to be looked at, as that is where we found the original definition. But the files r8139.c, 8139too.c, and 8169cp.c in the drivers/net/ subdirectory should be examined more closely.

Open one of these files in an editor and search for PCI_VENDOR_ID_REALTEK. In the file drivers/net/r8169.c, it shows up in this section of code:

static struct pci_device_id rtl8169_pci_tbl[] = {
        { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8169), },
        { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8129), },
        { PCI_DEVICE(PCI_VENDOR_ID_DLINK,   0x4300), },
        { PCI_DEVICE(0x16ec,                0x0116), },
        { PCI_VENDOR_ID_LINKSYS,            0x1032, PCI_ANY_ID, 0x0024, },
        {0,},
};

All PCI drivers contain a list of the different devices that they support. That list is contained in a structure of struct pci_device_id values, just like this one. That is what we need to look at in order to determine whether our device is supported by this driver. The vendor value matches here, but the second value after the vendor is the device value. Our device has the value 0x8139, while this driver supports the device values of 0x8169 and 0x8129 for devices with the vendor ID of PCI_VENDOR_ID_REALTEK. So this driver will not support our device.

Moving on to the next file drivers/net/8139too.c, we find the string PCI_VENDOR_ID_REALTEK in the following bit of code:

if (pdev->vendor == PCI_VENDOR_ID_REALTEK &&
    pdev->device == PCI_DEVICE_ID_REALTEK_8139 && pci_rev >= 0x20) {
	dev_info(&pdev->dev,
		   "This (id %04x:%04x rev %02x) is an enhanced 8139C+ chip\n",
		   pdev->vendor, pdev->device, pci_rev);
	dev_info(&pdev->dev,
		   "Use the \"8139cp\" driver for improved performance and stability.\n");
}

The use of the PCI_VENDOR_ID_REALTEK value here also corresponds with the code that checks whether the PCI device id matches the PCI_DEVICE_ID_REALTEK_8139 value. If it does, the driver is to print out a message that says: “ Use the 8139cp driver for improved performance and stability. ” Perhaps we should look at that driver next. Even if we did not have such a visible clue, the 8139too.c driver does not have the vendor and device ID pair that we are looking for in a struct pci_device_id variable, so that gives us the clue that it will not support our device.

Finally, look at the drivers/net/8139cp.c file. It uses the PCI_VENDOR_ID_REALTEK definition in the following code segment:

static struct pci_device_id cp_pci_tbl[] = {
        { PCI_VENDOR_ID_REALTEK, PCI_DEVICE_ID_REALTEK_8139,
          PCI_ANY_ID, PCI_ANY_ID, 0, 0, },
        { PCI_VENDOR_ID_TTTECH, PCI_DEVICE_ID_TTTECH_MC322,
          PCI_ANY_ID, PCI_ANY_ID, 0, 0, },
        { },
};
MODULE_DEVICE_TABLE(pci, cp_pci_tbl);

Here is a use of both our vendor and device ID values in a struct pci_device_id variable. This driver should support our device.

Now that we have the driver name, we can work backward, as shown in the first section in this chapter to find the proper kernel configuration value that should be enabled to build this driver.

In summary, here are the steps needed in order to find which PCI driver can control a specific PCI device:

  1. Find the PCI bus ID of the device for which you want to find the driver, using lspci .

  2. Go into the /sys/bus/pci/devices/0000: bus_id directory, where bus_id is the PCI bus ID found in the previous step.

  3. Read the values of the vendor and device files in the PCI device directory.

  4. Move back to the kernel source tree and look in include/linux/pci_ids.h for the PCI vendor and device IDs found in the previous step.

  5. Search the kernel source tree for references to those values in drivers. Both the vendor and device ID should be in a struct pci_device_id definition.

  6. Search the kernel Makefiles for the CONFIG_ rule that builds this driver by using find and grep :

    $ 
    find -type f -name Makefile | xargs grep DRIVER_NAME
    
    

  7. Search in the kernel configuration system for that configuration value and go to the location in the menu that it specifies to enable that driver to be built.

USB devices

Finding the specific driver for a USB device is much like finding the driver for a PCI device as described in the previous section, with only minor differences in finding the bus ID values.

In this example, let us find the driver that is needed for a USB wireless device. As with the PCI device example, the details in this example will be different from your situation, but the steps involved should be relevant to any type of USB device for which you wish to find a working driver.

As with the PCI device, the bus ID must be found for the USB device you wish to find the driver for. To do this, you can use the lsusb program that comes in the usbutils package.

The lsusb program shows all USB devices attached to the system. As willou do not know what the specific device your are looking for is called, start by looking at all devices:

$ 
/usr/sbin/lsusb

Bus 002 Device 003: ID 045e:0023 Microsoft Corp. Trackball Optical
Bus 002 Device 001: ID 0000:0000  
Bus 005 Device 003: ID 0409:0058 NEC Corp. HighSpeed Hub
Bus 005 Device 001: ID 0000:0000  
Bus 004 Device 003: ID 157e:300d  
Bus 004 Device 002: ID 045e:001c Microsoft Corp. 
Bus 004 Device 001: ID 0000:0000  
Bus 003 Device 001: ID 0000:0000  
Bus 001 Device 001: ID 0000:0000  

The devices with an ID of 0000:0000 can be ignored, as they are USB host controllers that drive the bus itself. Filtering them away leaves us with four devices:

$ 
/usr/sbin/lsusb | grep -v 0000:0000

Bus 002 Device 003: ID 045e:0023 Microsoft Corp. Trackball Optical
Bus 005 Device 003: ID 0409:0058 NEC Corp. HighSpeed Hub
Bus 004 Device 003: ID 157e:300d  
Bus 004 Device 002: ID 045e:001c Microsoft Corp.

Because USB devices are easy to remove, unplug the device you want to find the driver for and run lsusb again:

$ 
/usr/sbin/lsusb | grep -v 0000:0000

Bus 002 Device 003: ID 045e:0023 Microsoft Corp. Trackball Optical
Bus 005 Device 003: ID 0409:0058 NEC Corp. HighSpeed Hub
Bus 004 Device 002: ID 045e:001c Microsoft Corp.

The third device is now missing, which means the device shown as:

Bus 004 Device 003: ID 157e:300d

is the device you want to find the driver for.

If you replace the device and look at the output of lsusb again, the device number will have changed:

$ 
/usr/sbin/lsusb | grep 157e

Bus 004 Device 004: ID 157e:300d

This is because the USB device numbers are not unique, but change every time they are plugged in. What is stable is the vendor and product ID, shown here by lsusb as two four-digit values with a : between them. For this device, the vendor ID is 157e and the product ID is 300d. Write down the values you find, as you will use them in future steps.

As with the PCI device, we will search the kernel source code for the USB vendor and product IDs in order to find the proper driver to control this device. Unfortunately, no single file contains all of the USB vendor IDs, as PCI has. So a search of the whole kernel source tree is necessary:

$ 
grep -i -R  -l 157e drivers/*

drivers/atm/pca200e.data
drivers/atm/pca200e_ecd.data
drivers/atm/sba200e_ecd.data
drivers/net/wireless/zd1211rw/zd_usb.c
drivers/scsi/ql1040_fw.h
drivers/scsi/ql1280_fw.h
drivers/scsi/qlogicpti_asm.c

We know this is a USB wireless device, and not an ATM or SCSI device, so we can safely ignore the files found in the atm and scsi directories. That leaves the drivers/net/wireless/zd1211rw/zd_usb.c filename to investigate.

zd_usb.c shows the string 157e in the following chunk of code:

static struct usb_device_id usb_ids[] = {
        /* ZD1211 */
        { USB_DEVICE(0x0ace, 0x1211), .driver_info = DEVICE_ZD1211 },
        { USB_DEVICE(0x07b8, 0x6001), .driver_info = DEVICE_ZD1211 },
        { USB_DEVICE(0x126f, 0xa006), .driver_info = DEVICE_ZD1211 },
        { USB_DEVICE(0x6891, 0xa727), .driver_info = DEVICE_ZD1211 },
        { USB_DEVICE(0x0df6, 0x9071), .driver_info = DEVICE_ZD1211 },
        { USB_DEVICE(0x157e, 0x300b), .driver_info = DEVICE_ZD1211 },
        /* ZD1211B */
        { USB_DEVICE(0x0ace, 0x1215), .driver_info = DEVICE_ZD1211B },
        { USB_DEVICE(0x157e, 0x300d), .driver_info = DEVICE_ZD1211B },
        {}
};

Like PCI drivers, USB drivers tell the kernel what devices they support in order for the kernel to bind the driver to the device. This is done by using a struct usb_device_id variable, as shown here. This is a list of the different vendor and product IDs that are supported by this driver. The line:

        { USB_DEVICE(0x157e, 0x300b), .driver_info = DEVICE_ZD1211 },

shows that our vendor and product IDs are supported by this driver.

Once you have the driver name that is necessary to control this device, work backward through the kernel Makefiles, as described earlier in the chapter, to determine how to enable this driver to be built properly.

In summary, the steps needed in order to find which USB driver will control a specific USB device are:

  1. Find the USB vendor and product ID of device for which you want to find the driver, using lsusb after adding and then removing the device to see what changes in the list.

  2. Search the kernel source tree for the vendor and product ID of the USB device. Both the vendor and product ID should be in a struct usb_device_id definition.

  3. Search the kernel Makefiles for the CONFIG_ rule that builds this driver by using find and grep :

    $ 
    find -type f -name Makefile | xargs grep DRIVER_NAME
    
    

  4. Search in the kernel configuration system for that configuration value and go to the location in the menu that it specifies to enable that driver to be built.

Root Filesystem

The root filesystem is the filesystem that the kernel boots the main portion of the running system. It contains all of the initial programs that start up the distro, and also usually contains the entire system configuration for the machine. In short, it is very important, and must be able to be found by the kernel at boot time in order for things to work properly.

If your newly configured kernel dies at boot time with an error such as:

VFS: Cannot open root device hda2 (03:02)
Please append a correct "root=" boot option
Kernal panic: VFS: Unable to mount root fs on 03:02

then the root filesystem wasn't found. If you are not using a ramdisk image at boot time, it is usually recommended that you build both the filesystem that you use for your root partition, and the disk controller for that disk, into the kernel, instead of having it as a module. If you use a ramdisk at boot time, then you should be safe building these portions as modules. [12]

The following subsections show how to let the kernel find the root filesystem during boot.

Filesystem type

First, the type of filesystem that the root partition is using needs to be determined. To do that, look in the output of the mount command:

$ 
mount | grep " / "

/dev/sda2 on / type ext3 (rw,noatime)

We are interested in the type of the filesystem, which is shown after the word type. In this example, it is ext3. This is the type of filesystem that the root partition is using. Go into the kernel configuration system and make sure that this filesystem type is enabled as described in the section called “Filesystems”.

Disk controller

In the output of the mount command shown earlier, the first portion of the line shows which block device the root filesystem is mounted on. In this example, it's /dev/sda2. Now that the filesystem is configured properly in your kernel, you must also make sure that this block device will also work correctly. To find out which drivers are needed for this, you need to look at sysfs again.

All block devices show up in sysfs in either /sys/block/ or in /sys/class/block/, depending on the version of the kernel you are using. In either location, the block devices are a tree, with the different partitions being children of the main device:

$ 
tree -d /sys/block/ | egrep "hd|sd"

|-- hdc
|-- hdd
`-- sda
    |-- sda1
    |-- sda2
    |-- sda3

Given the information in the mount command, you need to ensure that the sda2 device is configured properly. Because this is a partition (disk partitions are numbered, while main block devices are not), the whole sda device must be configured. (Without the main block device, there is no way to access the individual partitions on that device.)

The sda block device is represented just like the network device we looked at earlier in this chapter. There is a symlink in the device's directory called device that points to the logical device that controls this block device:

$ 
ls -l /sys/block/sda

  ...
device -> ../../devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0
  ...

Now you need to start walking up the chain of devices in sysfs to find out which driver is controlling this device:

$ 
ls -l /sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0

  ...
driver -> ../../../../../../bus/scsi/drivers/sd
  ...

Here we see that the SCSI disk controller driver is responsible for making this device work. So we know we need to configure SCSI disk support into our kernel configuration.

Continuing up the directory chain in sysfs, try to find where the driver is that controls the hardware:

$ 
ls -l /sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0

  ...

There is no link called driver in this directory, so go back up one more level:

$ 
ls -l /sys/devices/pci0000:00/0000:00:1f.2/host0

  ...

Again, no driver here. Continuing on up one more level:

$ 
ls -l /sys/devices/pci0000:00/0000:00:1f.2

  ...
driver -> ../../../bus/pci/drivers/ata_piix
  ...

There! This is the disk controller we need to ensure is in our kernel configuration.

So for this root filesystem, we need to enable the ext3, sd, and ata_piix drivers in our kernel configuration so that we will be able to successfully boot our kernel on this hardware.

Helper Script

As mentioned near the beginning of this chapter, files and directories within sysfs change from one release of the kernel to another. Here is a script that is handy in determining the needed kernel driver and module module name for any device node in the system. It has been developed with the kernel developers responsible for sysfs and should successfully work on all future versions of the 2.6 kernel.

For instance, it makes short work of the previous example, when you had to get all of the proper drivers for the sda block device:

$ 
get-driver.sh sda

looking at sysfs device: /sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0
found driver: sd
found driver: ata_piix

I can also find all of the proper drivers needed for complex things such as USB-to-serial devices:

$ 
get-driver.sh ttyUSB0

looking at sysfs device: /sys/devices/pci0000:00/0000:00:1d.3/usb4/4-2/4-2.3/4-2.3:1.0/ttyUSB0
found driver: pl2303 from module: pl2303
found driver: pl2303 from module: pl2303
found driver: usb from module: usbcore
found driver: usb from module: usbcore
found driver: usb from module: usbcore
found driver: uhci_hcd from module: uhci_hcd

The script follows.

#!/bin/sh
#
# Find all modules and drivers for a given class device.
#

if [ $# != "1" ] ; then
	echo
	echo "Script to display the drivers and modules for a specified sysfs class device"
	echo "usage: $0 <CLASS_NAME>"
	echo
	echo "example usage:"
	echo "      $0 sda"
	echo "Will show all drivers and modules for the sda block device."
	echo
	exit 1
fi

DEV=$1

if test -e "$1"; then
	DEVPATH=$1
else
	# find sysfs device directory for device
	DEVPATH=$(find /sys/class -name "$1" | head -1)
	test -z "$DEVPATH" && DEVPATH=$(find /sys/block -name "$1" | head -1)
	test -z "$DEVPATH" && DEVPATH=$(find /sys/bus -name "$1" | head -1)
	if ! test -e "$DEVPATH"; then
		echo "no device found"
		exit 1
	fi
fi

echo "looking at sysfs device: $DEVPATH"

if test -L "$DEVPATH"; then
	# resolve class device link to device directory
	DEVPATH=$(readlink -f $DEVPATH)
	echo "resolve link to: $DEVPATH"
fi

if test -d "$DEVPATH"; then
	# resolve old-style "device" link to the parent device
	PARENT="$DEVPATH";
	while test "$PARENT" != "/"; do
		if test -L "$PARENT/device"; then
			DEVPATH=$(readlink -f $PARENT/device)
			echo "follow 'device' link to parent: $DEVPATH"
			break
		fi
		PARENT=$(dirname $PARENT)
	done
fi

while test "$DEVPATH" != "/"; do
	DRIVERPATH=
	DRIVER=
	MODULEPATH=
	MODULE=
	if test -e $DEVPATH/driver; then
		DRIVERPATH=$(readlink -f $DEVPATH/driver)
		DRIVER=$(basename $DRIVERPATH)
		echo -n "found driver: $DRIVER"
		if test -e $DRIVERPATH/module; then
			MODULEPATH=$(readlink -f $DRIVERPATH/module)
			MODULE=$(basename $MODULEPATH)
			echo -n " from module: $MODULE"
		fi
		echo
	fi

	DEVPATH=$(dirname $DEVPATH)
done


[10] Note that you can just try searching through the kernel configuration for a device that matches the string described here, a device from Realtek Semiconductor with a product name of RTL-8139/8139C/8139C+, but this does not always work. That is why we are taking the long way around in this chapter.

[11] Some 64bit processors will show the leading bus number for PCI devices in the output of lspci , but for the majority of the common Linux machines, it will not show up by default.

[12] How can you determine whether you are using a ramdisk at boot time? In Chapter 6, Installing and Booting From a Kernel we mention using the distribution installation script to install the kernel versus doing the installation on your own. If you are using the distribution installation script, you are probably using a ramdisk. If you are installing it on your own, then you are probably not.


 
 
  Published under the terms of the Creative Commons License Design by Interspire