In general, any operating system needs a piece of software specific to the device. This piece of software understands the device functionality and is a middle layer between OS and Hardware. Device driver is the term used for this piece of software. In this article, we are going to discuss about how Linux handles the devices and device drivers. In simple words, we will explore the device driver framework of Linux.
Description
Typically, on any board or platform, multiple devices are present and these devices are connected with each other by some physical lines or protocols. These connection protocols are known as buses. There are several bus protocols available. Few examples are I2C, SPI, AHB, APB, PCI, etc. Let us take an example of EEPROM memory device. EEPROM is connected with the system by I2C bus. CPU will use the I2C protocol to read/write data from EEPROM. From CPU side, this protocol handling will be done by the I2C protocol controller. I2C controller on the CPU acts as the master device. EEPROM acts as the slave device. All the details of I2C are available in the I2C specification.
In Linux based ARM systems, EEPROM devices are populated with the help of the device tree. Defining the EEPROM in the device tree is enough to declare the device in the system. With this device tree entry device instance will be created by the Linux kernel while booting up. When the Linux boots up, it parses the device tree and created the instance of the devices defined in the device tree.
With this device is created in the Linux but Linux will not be able to understand the device. For the device communication/operations, a special software specific to device is needed. This will be known as the device driver for the device. Coming back to the EEPROM example, EEPROM device driver will be needed to read/write the data from the EEPROM.
For binding the device driver to the specific device, a compatible string is needed. Compatible string is used by Linux kernel to probe the specific driver to the device while booting. Linux kernel also provide the flexibility that a device driver can be loaded at runtime. The only condition is driver should not be needed for the platform to boot. Device driver which are added later to the kernel are compiled as kernel objects. These are the files present as .ko. insmod command is used to add the kernel objects on the running kernel.
After the device driver is probed with the device, device can be used for the operations. EEPROM device can be read/written after the EEPROM driver is initialized in the Linux kernel. EEPROM driver initializes the device and provide the capability to the Linux kernel to read/write the EEPROM.
Let us take an example of EEPROM device driver as AT24, source code for the device can be find at the link: https://github.com/torvalds/linux/blob/master/drivers/misc/eeprom/at24.c
This driver supports very wide number of EEPROM devices as described in the comments of the driver Atmel AT24C or * MicroChip 24LC, etc.
Following is the device tree information to be added to create a device instance:
compatible = "atmel,24c32";
reg = <0x50>;
pagesize = <32>;
}
This should be added to the specific i2c controller node, where this EEPROM device is connected.
As we can see, there is a compatible string. This is the info used by Linux kernel to locate the device driver of EEPROM device.
To get the info on the devices and devices present on the Linux system, sysfs entries are the best place.
For every device and driver on the system, sysfs entries will be created by the kernel. User can refer these sysfs files to diagnose the system.
If we see the content of sys directory in Linux kernel:
/sys/bus: All the busses present on the system are listed inside this.
I2c bus can also be seen. As we were discussing the i2c device example. Inside the bus directory, we have i2c bus directory.
For any bus in the sysfs, we will have all the devices and drivers present on that bus. Let us see the content of i2c bus:
If we further browse the devices and drivers directory, we will get the complete list of devices and drivers known to the Linux kernel.
Inside the devices, we can see there are multiple i2c busses present in the system. I2c-0, i2c-1, i2c-5, etc., are different i2c busses. 0-0018 and 0-001a are the slave devices on i2c-0. 1-0050 and 1-0068 are the i2c slave devices on bus no. 1 i.e. i2c-1.
Inside the driver’s directory we have the list of all the i2c slave device drivers.
Moving back to our EEPROM device example, 1-0050 is the EEPROM slave device. If we further dive into the 1-0050 directory, we will see something like below:
This has provided us the knowledge on the driver which is controlling this device. In the snapshot, we can see AT24 driver controls the EEPROM present in the system. This is the driver which is linked to this EEPROM device.
To access the EEPROM device from the user space, driver has created the file “eeprom” which can also be seen in the snapshot.
To read the 8K EEPROM data and dump to the file, dd command can be used as below:
As it can be seen from the logs that 8K bytes are read from the EEPROM and written to the eeprom_data.bin file. This bin file will have the EEPROM data. Dd command is most popular and commonly used command in Linux world.
Just like this EEPROM device, other i2c devices also must follow the guidelines provided by Linux kernel. Other I2c devices could be RTC, Toch screen, etc. Overall, this device driver framework is applicable even to the devices outside the i2c scope.
It can be a SPI device or any other device. There will be one device instance to be created and another driver instance. Both the device and driver will be linked/connected via bus driver. This is the generic device driver framework in Linux.
Binding and Unbinding of Driver
Binding of driver with device is the process of associating or linking of driver to the device that can control or understands it. Unbinding is the reverse process, when we unlink the driver with the device.
There are sysfs files present in all the drivers. File names are bind and unbind. These are the files which can be used to bind and unbind. Following is the snapshot of the EEPROM driver AT24:
Unbinding of Driver with Device
As we can see, the device instance is present inside at24. This means the device is already linked. We can echo the device name to unbind the driver from device.
Unbinding of the driver with the device can be seen in the snapshot.
echo 1-0050 > /sys/bus/i2c/drivers/at24/unbind; is the command which has done the unbinding. After this command, device is not present. Hence, the device is not linked with the driver now.
Binding of Driver with Device
echo 1-0050 > /sys/bus/i2c/drivers/at24/bind; is the command which does the binding of driver with the device.
First ls command shows that device details are not present inside the AT24 directory, which means that device is not linked with any driver. Secondly, we issued a command to link the device with the driver. As a result, we saw the device information gets populated inside the driver directory. Hence, the driver gets linked to the device successfully.
Device can only be accessed after the successful binding of the driver with the device.
Conclusion
We discussed the device driver framework in Linux kernel with an example of i2c EEPROM device. We explored the EEPROM device creation in device tree and the linking of driver with the device. Some exploration was done on the sysfs files, which provides the very good diagnostic information on devices and drivers present in Linux kernel. We saw an example of EEPROM access with the help of dd command. We also understood the generic framework involving device, drivers, and buses. At last, we also referred the ways to bind and unbind drivers and devices manually from the user space.