Category Archives: Linux Device Driver

Device driver to interface shift reg with Raspberry pi 2

Hello Folks, after a long time my schedule allowed me to write some thing.

Today i am going to explain you the way to interface the shift register  with Linux based raspberry pi 2 board.  The shift register is the sequential logic which can used for data storage/parallel to serial conversation/ parallel to serial conversation. Hardware based stack can be constructed using several shift resister.

Shift registers are broadly distinguished in two types.

  1. Serial in, parallel out (SIPO):  In this configuration data is serially inserted in to shift register and output will be in form of parallel data bits. This is register used when more output ports are needed then available. This allows several binary devices to be controlled using only three pins, the binary controlled devices are attached to the parallel outputs of the shift register, then the desired state of all those devices can be sent out of the microprocessor using a single serial connection.
  2. Parallel in, serial out(PISO):  In this configuration parallely inserted data to shift register and output will be serial stream of bits. This configuration used to add binary inputs and give it in single stream to process. In this less micro-controller  pins are required to read the data.

This tutorials demonstrate how to interface SIPO  shift register (sn74hc595) to the raspberry pi 2. In addition to this, it explains how to write Linux device driver to control this hardware.

The sn74hc595 has an 8 bit storage register and an 8 bit shift register. Data is written to the shift register serially, then latched onto the storage register. The storage register then controls 8 output lines. Lets examine the ping configuration of sn74hc595 first. That will give us understanding about “How to drive sn74hc595 ? “.

Top_view

Ping configuration of sn74hc595

sn74hc595 register has 18 pins. which is shown in the image at left. Pin 16 is for VCC, which should be connected to 5v. Pin 8 is connected to common ground in the system. Pin number 1 to 7 and 15 is parallel data output pins.  Pin 14 is the serial data input pin.  Pin 11 is serial clock pin. when pin 11(SRCLK) goes from Low to High the value of pin 14(SER) is stored into the shift register and the existing values of the register are shifted to make room for the new bit. Pin 12 (RCLK) is used for latch. This pin should be low when data is written in the sift register. When it goes High the values of the shift register are latched to the storage register which are then outputted to pins Q0-Q7.  Pin 13 is to enable output. All latch output is enable if this pin is set to low.  Pin 10 is use for clear the output state on pins. Output pins will be cleared if low to high pulse is given to this pin. Default value of pin 10 is high.

How to co

So to drive this register, we need to control 5 pins.  Pin # 14,13,12,11 and 10. We need to connect output pins to LEDs  to check output of shift register. Based on this, we have derived a circuit diagram.

First-schamatic

Circuit diagram

Above image shows circuit diagram. Here we are using GPIOs of raspberry pi to control shift registers. RPI GPIO 9 is used as serial data line when is connected to SER(pin 14) of sn74hc595 . RPI GPIO 11 is used as latch clock line which is connected to RCLK (pin 12) of sn74hc595. RPI GPIO 25 is used as serial clock line which is connected to SRCLK (pin 11) of sn74hc595.  RPI GPIO 8 is used as serial clear line which is connected to SRCLR(pin 10) of sn74hc595. In addition to this, it is essential to connect VCC ad 5v and Common Ground with RPI.

Now, its time to design driver to control these GPIO pins. Driver will expose sysfs interface to change value represented by leds. Value which is written to this sysfs file will be represented by leds which is connected with sn74hc595.

So, lets start understanding responsibilities of init function of driver. As part of initialization of this driver, we need to Configure pin 21, 22,23 and 24 as GPIO which corresponds to GPIO 8, 9, 25 and 11.  In addition to this, we have to resigter sysfs class and device to control shift register.

Les take a deep dive  into source code of driver.  As first part we have declared to global GPIOs pins which can be used in all driver code. As per our circuit design above we have declared the pins which is shown in below code snippet.

declatation of GPIO

GPIO Declaration

As part of initialization we have to configured this pins as GPIO pins. Below code snippet configures and request this pings as GPIO. The below snippet is part of init function of driver.

requrst_GPIO

Configure Pings as GPIO

Now we have to configure this pins as output pins and set to appropriate default value.  As explained above we need to configure Pin 10(serial clock bar) to high.  The default value of all other pins will be low. The below snippet shows this.

op

Configure GPIOs as output

Lets register sysfs interface to control output of shift register.  I have created new sysfs class and device to represent shift resister.

sysfs

Registration of Sysfs interface

Every device interface in sysfs has attributes which can be read or write. Corresponding read write function registered with the attribute will get called when read or write operation performed on the this interface. Below snippet shows the registration of attribute named value with set and get value callback.

The attribute name should be the same as given by the time of creating file. Here we have given name as dev_attr_value. So out attribute is the value for which we have registered set and get routine. when we tried to read the file(sysfs Interface) from application, get_value_callback will be triggeres in the driver. In case we tried to write some value to file(sysfs Interface) from application,  set_value_callback will be triggers with the value to write in shift register.

attribute

Registration of sysfs device attribute

On low to high transmission of clock pulse,  internal register(storage register) value will shift by one and value on data pin will be moved to LSB of internal register. At the end after 8 bit data is shifted to the shift register,  by providing the latch pulse from low to high, all data from the internal register(storage Register) is latched to shift register pins. We have connected LEDs to the output pins of shift register.  This is shown by below snippet. To write some value to shift register,  it is essentials to put that value on data pin of shift register and then provide a clock pulse. On this clock pulse, data on internal register gets shifted and adds new bit(which is present on data pin) at the end presently shifted data.  So here in code, I am setting bit by bit this new_value variable on data pin and after setting one bit providing clock pulse. At the end i am providing the latch pulse so the 8 bit value which stored in the internal storage register will be  latched to the pins.

set_attribute

Routine to set value

When user application tries to  read the value from the sysfs interface, the get_value_callback will get called.  The callback will return old_value which was updated by the get value callback. The below snippet shows that. get_attribute

Here you can get full code for this driver. you can follow this steps to add module to Linux kernel source code and compile it.

Cheers !!!

Leave a comment

Filed under Linux Device Driver, Uncategorized

Fundamentals of PCI device and PCI drivers.

Hello Folks, today i am going to talk about the PCI subsystem and Process of developing PCI based Device driver.

PCI is a local bus standards, which used to attach the peripheral hardware devices with the Computer system. So it defines how different peripherals of a computer should interact. A parallel bus which follows PCI standards is knows as PCI bus. PCI stands for Peripheral Component Interconnect.

The devices which are connected to PCI bus are assigned address in the processor’s address space. This memory(addresses in processor’s address space) contains control, data and status registers for the PCI based  device, which is shared between CPU and PCI based device. This memory will be controlled by the device driver/kernel to control the particular device connected over PCI bus and share information with it. PCI device became  like a memory mapped device.

The PCI address domain contains the three different type of memory which has to be mapped in the processor’s address space.

1. PCI Configuration Address space

Every PCI based  device has a configuration data structure that is in the PCI configuration address space. The length of configuration data structure is 256 bytes. This data structure used by system/kernel to identify the type of device. The location of this data structure is depend upon the slot number where the device is connected on the board. eg. Device on slot 0 has its configuration data structure on 0x00 but if you connect same device on slot 1 its configuration data structure goes to 0xff. You can get more details about layout of this configuration data structure here.

At power on, the device has no memory and no I/O ports mapped in the computer’s address space. The firmware initializes PCI hardware at system boot by mapping each region to a different address. By accessing PCI controller register, the addresses to which these regions are currently mapped can be read/write from the configuration space, By the time a device driver accesses the device, its memory and I/O regions have already been mapped into the processor’s address space.

So To access configuration space, the CPU must write and read registers in the PCI controller. Linux provides the standard API to to read/write the configuration space. The exact implementation of this API is vendor dependent.

int pci_read_config_byte(struct pci_dev *dev, int where, u8 *val);

int pci_read_config_word(struct pci_dev *dev, int where, u16 *val);

int pci_read_config_dword(struct pci_dev *dev, int where, u32 *val);

The above APIs are used to read the different size of data configuration space. The first argument is the pci device node, the second argument is the byte offset from the beginning of configuration space and third argument is the buffer to store the value.

int pci_write_config_byte(struct pci_dev *dev, int where, u8 val);

int pci_write_config_word(struct pci_dev *dev, int where, u16 val);

int pci_write_config_dword(struct pci_dev *dev, int where, u32 val);

The above APIs are used to wirte the different size of data configuration space. The first argument is the pci device node, the second argument is the byte offset from the beginning of configuration space and third argument is a value to write.

2. PCI I/O address space 

This is 32 bit memory space which can be access by using CPU IO access instructions. PCI devices place their registers for control and status in PCI I/O space.

3. PCI memory Address space

This is 32 bit or 64 bit memory space which can be  access as the normal memory locations. The base address of the this memory space is stored in the BAR register. The PCI memory space have higher performance than access to PCI I/O space.

Specification of I/O and memory address is device depended. I/O and memory address can be access by normal memory read write operations.

We have gone through the basic memories region of PCI device. Now its time to understand the different initialization phase of the of PCI devices.

Linux kernel devices the PCI initialization in to three phase.

1. PCI BIOS :  It is responsible for performing all common PCI bus related task. Enable the access to PCI controlled memory. In some CUP architecture it allocate the interrupts for PCI bus.

2. PCI Fixup : It maps the configuration space, I/O space and Memory space to the RAM. Amount of memory for I/O region and memory region can be identified from the BAR registers in configuration space. In some of CPU architecture Interrupts are allocated at this stage. It also scan all the bus and find out the all present devices in the system and create pci_dev structure for each present device on the bus.

3. PCI Device Driver :  The device driver registers the driver with product Id and vendor Id. The PCI  subsystem checks for the same vendor Id and product id in its list of devices registered at the Fixup phase. if it gets the device with same product Id and vendor Id the it initialize the device by calling the probe function of driver which registers further device services.

So, this was just an overview of the how PCI based devices works with the Linux kernel.

Stay tunes !!

7 Comments

Filed under Linux Device Driver

The story of device tree for platfrom device….

The whole story starts from non discover-able devices in the system. This post will provide you information about non discoverable devices as well it will provide you one of way of Linux kernel to deal with it. The second and fresh way is device tree.

Kernel starts, it has to initialize the drivers for the devices on the board. But often on embedded systems, devices can’t be discover at running time(i2c, spi, etc.). In this case, the Linux kernel has a c (board file) file that initialize these devices for the board. The below image shows structure for non discoverable devices and platform data for same. This will be registered with the virtual bus named platform bus and driver will also register it self with platform bus with the same name.

In this method,  kernel must be modified/compiled for each board or change in hardware on board.  Kernels are typically built around a single board file and cannot boot on any other type of system. Solution of this situation is provided by “Device tree”. A device tree is a tree data structure with nodes that describe the physical devices on the board. While using device tree, kernel no longer contains the description of the hardware, it is located in a separate binary blob called the device tree blob. The device tree is passed to the kernel at boot time. Kernel reads through it to learn about what kind of system it is. So on the change of board only developer needs to change device tree blob and that it new port of kernel is ready.

Platfrom device

Platform device

Here, you will get a good article on device tree format. It is recommended to go through it first at this stage.

Platform devices can work with dtb enabled system with out any extra modification. If the device tree includes a platform device that device will be instantiated and matched against a driver. All resource data will be available to the driver probe() in a usual way. The driver dose now know wither this device is not initialized with hard-cored. in board file.

Every device in the system is represented by a device tree node. The next step is to populate the tree with a node for each of the devices. The snippet below shows the dtb with node name “ps7-xadc”.  Every node in the tree represents a device, which must have a compatible property. compatible is the key using which an operating system uses to decide which device driver to bind to a device. In short compatible(ps7-xadc-1.00-a) specifies the name of the platform device which will get registered with bus.

Platform device in DTB

Platform device in DTB

On other side, in device driver when platform deriver structure is declared, it stores a pointer to “of_device_id”. This is shown by the below snippet. This name should be same which was given in the dtb file.  Now, when driver with name of “ps7-xadc-1.00.a” will get register with the platform bus, probe of that driver will get called.

Platform driver registration

Platform driver registration

The below snippet shows the probe function of the driver. In the probe, function platform_get_resource() provides property described by “reg” in dtb file. In our case base address of register set(0xf8007100) for hardware module and offset from the base address(0x20)  can be retrieved. Using which driver can request the memory region form the kernel. As same, function platform_get_irq() provides the property which id describe by “interrupts” in dtb file.

Getting device specific data

Getting device specific data

After garbing all details from dtb file, probe will register device as a normal way.  This is very straight forward procedure using which platform drivers work with device trees. As a result of this,  no need to declare platform_device in board file.

Cheers !!!

4 Comments

Filed under Device tree, Linux Device Driver, Platfrom device, Uncategorized

Add new system call to linux kernel…

This post gives you a deep understanding about system call and a way to add a new system call to Linux kernel.

System call is call to kernel service made using software interrupts. An interrupt is a way to notify kernel about occurrence of some event, and this results in changes in the sequence of instructions that is executed by the CPU. A software interrupt, also referred to as an exception, is an interrupt that originates by software in user mode.  User mode is one of two distinct execution modes of operation for the CPU in Linux. It is a non-privileged mode in which each process starts out. So these processes dose not have privilege to access memory allocated by the kernel.

The kernel is a program that constitutes the core of an operating system, and it has complete control over all resources on the system and everything that occurs on it. When a user mode process wants to use a service provided by the kernel (i.e., access system resources other than the limited memory space that is allocated to the user program), it must switch temporarily into kernel mode, also called system mode, by means of a system call.

Kernel mode has all privileges, including root access permissions. This allows the operating system to perform restricted actions such as accessing hardware devices or the memory management unit. System calls can also be viewed as gateway to kernel through which programs request services from the kernel.

Flow Graph of System call

Flow Graph of System call

Above Figure shows the general flow graph of the system call. User application lies in user space and system call body is in kernel space. In the user application the kernel privilege data can accessed by the system call. System call body will start a new kernel thread to serve the user application.

I would like to take you through a sequence of steps in creating your own system call.

1. Editing kernel source code to add system call

2. Compiling modified kernel for x86 machine

3. Building an application to text your system call

Add System Call

Lets install all the dependency packages (libncurses5-dev)

sudo apt-get install libncurses5-dev

Then update and install all the upgrade your machine.

sudo apt-get update && sudo apt-get upgrade

Download kernel source code. Here, i am using the kernel 3.2, there are various other versions are available here. Below command also downloads kernel.

wget http://www.kernel.org/pub/linux/kernel/v3.0/linux-3.X.tar.bz2

Extract the tarball to ~/linux-3.x/

sudo tar -xvf linux-3.X.tar.bz2 -C ~/linux-3.x/

Change the directory to ~/linux-3.x/

cd ~/linux-3.x/

Now lets add the system call in the above downloaded kernel.

First, we need to create a new directory in the root of the kernel sources tree. Name of new directory is “new_syscall”. In this directory, we need to create two files.

1. Implementation of our system call itself, hello.c:

The below snippet is the body of system call. Basically, This function is the one which should  called in the kernel space when appropriate system call is invoked from the user space.

Implimentation of syscall

Implementation of system call

2. Makefile to build it

After creating the system call, we need to set up the Makefile to build it. Following snippet will show you the contain of Makefile which is used to build our system call.

Makefile for hello.c

Makefile for hello.c

This is a very simple Makefile, because the build system of the kernel takes care of most of the work. This concludes the new files that will need to be added to the kernel sources to make a new system call.

There are a few source files in the kernel that will need to be updated in order to add the new system call to be added to the kernel build system. The first, and simplest of these is the root-level Makefile. Find the following line, around line 711 of the root-level Makefile:

core-y := kernel/ mm/ fs/ ipc/ security/ crypto/ block

Add your newly created directory “new_syscall” as shown :

core-y := kernel/ mm/ fs/ ipc/ security/ crypto/ block/ new_syscall/

Now, open the include/linux/syscalls.h file, and add a definition for your new system call.

asmlinkage long sys_hello(void);

This step expose your system call  to other parts of the kernel. While this is not necessary for this simple system call, it is still good practice for when adding a real system call to the kernel.

NOTE: asmlinkage keyword tells your compiler to look on the CPU stack for the function parameters, instead of registers. System calls are services that userspace can call to request the kernel to perform something for them. These functions can not behave like normal functions, where parameters are typically passed by writing to the program stack, but instead they are written to registers. While still in userspace, calling a syscall requires writing certain values to certain registers. The system call number will always be written in eax, while the the rest of the parameters will go into other registers.

At this stage, you simply need to add the name of your system call to arch/x86/include/asm/unistd_32.h. Register the system call symbolic name with the kernel by adding system call number and system call name as below.

#define __NR_setns 346

#define __NR_sys_hello 347 <Add this line >

#ifdef __KERNEL__

#define NR_syscalls 348 <add 1 in total number of system call>

#define

The kernel maintains a list of all registered system calls in the system call table. This table assigns each valid system call a unique system call number which cannot be changed or recycled(which is given in above step). Processes do not refer to system calls by name, but rather by their system call number.

The final file that needs to be updated is the system call table, which resides in arch/x86/kernel/syscall_table_32.S. Add the definition for your new system call, by adding the following:

.long sys_hello

After configuring the systemcall perfectly kernel compilation is done to use system call from user domain.

Compiling modified kernel

Linux kernel can be compiled natively in the Linux environment using the native “gcc” compiler. Make files allow configuration changes using particular make options. The steps involved in compiling the kernel are:

1. Call the make utility within the un-tarred Linux kernel code (in this case Linux-3.2) directory with the required option – menuconfig, defconfig, xconfig, oldconfig and so on. menuconfig is used to edit the text based version of linux. xconfig is used to edit the windows and other GUI tools in KDE system and gconfig is used to edit same but in gnome system. I have used “make oldconfig”, which can configure new kernel as my existing kernel configuration. Before this the directories can be cleaned using the “make mrproper” and “make clean” command.

2. Once the compilation configuration is done use the “make” command to compile the kernel. To compile project, first need to compile each source file into an object file, this in turn needs to be linked with system libraries into the final executable file. This all is done by build system of Linux kernel.

3.  Once the kernel compiles successfully use the make modules and then the make modules_install options to compile and install modules into the kernel. Loadable modules are compiled and installed in the /lib/modules directory

4.  Then use the “make install” command to install the kernel in the /boot partition.

5.  Then switch to the /boot partition and use the “mkinitramfs” command with the –o option to create the RAM disk file as shown mkinitramfs –o initrd.img-<kernel_version_number>. You can get kernel version number by simple typing “uname -a” command on your terminal.

6.  GRUB (GRand Unified Bootloader) is a boot loader package developed to support multiple operating systems/kernel and allow the user to select among them during boot-up. After installing the modules and kernel it is essential to update the grub file to see the installed kernel version in the boot option. So, use “update-grub” command to update the boot entries in the grub file.

Thats it. You have installed new kernel in your system. Now just reboot your system and and select newly compiled kernel from grub selection menu.

Building an application

That’s it ! you have successfully added new system call to the kernel.  For simple testing of the system application program is essential which can call system call. Below application is test application for your system call. This application will invoke __NR_sys_hello from the kernel space using syscall().

Test app for developed system call

Test application for developed system

Compile the program, run it and check the dmesg command to see the “Hello World ! I am your system call.” output. Use below commands to check the output of your system call.

$ gcc hello.c -o hello
$ ./hello 
$ Return Value from syscall is : 0
$ Please check system console(using "dmesg") for system call output. 
 
$ dmesg

Cheers… you have done it !

Leave a comment

Filed under Linux Device Driver

Make Own LED blinking Driver for Raspberry pi ….

In this post, i am going to explain step by step procedure to make simple driver which can blink led on Linux powered raspberry pi. Raspberry pi is a credit-card sized computer developed by Raspberry pi Foundation ,UK. The Raspberry pi is equipped by Brodcom BCM2835 SoC, which includes an ARM1176JZF-S core clocked with 700 MHz. Raspberry pi was originally shipped with 256 MB of RAM, later upgraded to 512 MB of RAM. This card sized computer uses the SD card for booting and data storing purpose.

This tutorial demonstrates how to develop and debug a basic hardware driver for Raspberry PI. It will demonstrate the following techniques:

  • Controlling the BCM2708/BCM2835 peripherals by accessing their hardware registers
  • Handling of interrupts in Device driver
  • Creating a sysfs device object to provide user-mode control interface

Here, for my setup i am using raspberry pi model-A. I have compiled kernel(with my led blinking driver) for raspberry pi.

Compilation of Linux kernel for Raspberry pi

1. Get the kernel Source  code for here.

2. Get the tools(cross-compiler) from here.

3. Extract both files in your home directory. Here, i have extracted in /home/bhargav/rpi/.

3. Set the environment variable CCPREFIX:
export CCPREFIX= /home/bhargav/rpi/tools-master/arm-bcm2708/arm-bcm2708-linux-gnueabi/bin/arm-bcm2708-linux-gnueabi-

4. Set the environment variable KERNEL_SRC:
export KERNEL_SRC=/home/bhargav/rpi/linux-rpi-3.2.27

5. In KERNEL_SRC: execute “make mrproper” to ensure you have a clean kernel source tree

6. In KERNEL_SRC: execute below command  to configure  kernel source tree for raspberry pi

make ARCH=arm CROSS_COMPILE=${CCPREFIX} bcmrpi_defconfig

7. In KERNEL_SRC: execute below compile  kernel source tree for raspberry pi

make ARCH=arm CROSS_COMPILE=${CCPREFIX}

This process will give you kernel Image file at < KERNEL_SRC>/arch/arm/boot/  which can be places as kernel.img in boot partition of MMC.

Adding LED blinking device in board file

To add led blinking driver support in your build, you have to register your device in the board file of your board.

Board file for raspberry pi is located at <KERNEL_SRC>/arch/arm/mach-bcm2708/bcm2708.c which includes the subroutines for registering of all devices.

First, you need to add your header file of driver in to <KERNEL_SRC>/include/linux/ directory.

Here i am adding blinkled.h in the same directory. The below image will provide you more details about contain of file.

Header file for led blink driver

Header file for led blink driver

Include this header file in board file for raspberry pi. Add the below code in board file.

Defining a device in board file

Defining a device in board file

Here, i am declaring device named “LED_Blink” which has gpio number as a platform data on which it is connected.

Its time to register this declared device. In board file, bcm2708_init is a function which register all the peripherals devices with the kernel.

So, in this function we need to registered our device with the kernel platform bus.  Add the below line in in bcm2708_init function which register our device(“LED_Blink”) with kernel.

This device is added as platform device. I am not going in to much details of platform device, explanation can be found here.

Registration of Drivice

Registration of Device

At this stage we have registered our Led_Blink device to Linux kernel virtual bus. Now its time to write a driver for “Led_Blink” Device.

Writing driver for LED Blinking device

In the driver file, we need to declare one driver and register it with the kernel virtual bus with the same name which we gave to register device(“Led_Blink”). Linux kernel will compare the name of device and driver which is available on virtual bus and call the probe function of same driver. This is the basic concept of platform bus which is explained in the previous post.

Registration of Driver

Registration of Driver

Here, driver is declared with the  probe and remove function. Important thing is the name of the driver which is same as the device which we declared in device( In board file). Init function is the first function, which will be called on the successfully insertion of driver in the kernel. In our init function we have registered the platform driver to the bus.

On availability of the same device on the bus, kernel will calls the probe function of the same driver. So, after init function, probe will get called by the Kernel subsystem. Basically, probe will do all the initialization of device(GPIO) .

According to the BCM2835 peripheral  manual, the GPIO pins can be controlled by first configuring them as output by writing to one of GPFSELx registers and then writing to GPFSETx/GPFCLRx registers. We will define a structure describing the GPIO registers and provide functions for controlling GPIO pins using it.

Probe routine of LED blink driver

Probe routine of LED blink driver

Above snippet shows the body of probe function. In the probe function, there are three impotent things  are done.

1. Configure Pin as GPIO

The below snippet shoes the routines for set pin functionality and set output value of pin.  This functions uses the structure pointer to access the registers of SoC.

Gpio Routines

Gpio Routines

2. Setup time for On and Off timing of LED

When timer elapse, state of pin will get changed and again timer will be re initialized from timer subroutine. The snipped below shows the body of time handler.

This subroutine causes the blinking of the LED.

Timer handler subroutine

Timer handler subroutine

3. Register /sys interface  to change blink period from user domain

Sys interface is used to change the blinking period from user space. From the probe function BlinkLed_attr is registered for sys interface which has only one attribute name “period”.  User can get and set time interval using this interface. s_BlinkPeriod variable is used to store blanking period. The snippet below shows the subroutines for same.

Sys interface routines

Sys interface routines

You can download full driver code from here.

You have to add this add this module to linux source code. Here are the steps to do that.

Now, its time to compile your tweaked kernel using the steps shows above. Repeat from steps #5.

Enjoy your driver  !!!

19 Comments

Filed under Linux Device Driver

Platform Device and Platform Driver @ Linux

I was trying to learn Linux device driver and  system programming. Two simple question i had was, how does the Linux kernel know, which devices are present  and what resources(bus channel, interrupts, power on switch, etc..) it is using ? what are the drivers for them ?

After going through Linux kernel source code and exploring couple of kernel documents I sum up that  “Platform Device, Platform Driver and Platform Data” is the solution of  my questions. In this post i am trying to highlight the concept of platform device and platform driver with the help of  pseudo code.

Unified driver model has introduced in the Linux kernel 2.6 release.
There are several kind of devices are connected to CPU using different type of bus interfaces.
eg : PCI, ISA, I2C, USB, SPI, etc…
According to working mechanism, these buses can be divided in to two categories.

1. Discover-able :
Now a days buses like PCI and USB, which have discover-ability built into them. When a device is connected to a bus, it receives a unique identification which will be used for further communication with the CPU. Which menace that, device sitting on a PCI/USB bus can tell the system what sort of device it is and where its resources are. So the kernel can, enumerate the available device and driver to initialized device using the probe method normally. This kind of bus mechanism usually found with x86 architecture(PC).

2 Non discover-able :
Embedded system usually don’t have a sophisticated bus mechanism found in PC systems. we have buses like I2c or SPI. Devices attached to these buses are not discoverable in the above sense as i tried to explain. The OS has to be explicitly told that, for example, a EEPROM is connected on the I2C bus at an address of 0×DA. In this case, platform device/driver comes in a picture.

So basically, Platform devices are inherently not discoverable, i.e. the hardware cannot say “Hey! I’m present!” to the software .

Unlike PCI or USB devices, I2C devices are not enumerated at the hardware level (at run time). Instead, the software must know (at compile time) which devices are connected on each I2C bus segment. So USB and PCI are not platform devices.

In the embedded and system-on-chip world, non – discoverable devices are increasing rapidly. So basically all non discoverable devices are connected to the virtual bus and declares its name. This virtual bus is known as “platform bus”. On the other side, driver requests a device with the same name on the bus.

The whole story starts from board file. A board file is heart of each Linux kernel, which specifies all the information about what and how peripherals are connected to processor. eg: devices present on buses such as SPI and I2c. In the board file you can find all the devices are registered with the name and appropriate data. This data is known as the platform data. This Platform data will pass to driver. Generally Platform data is the device specific data. Eg: Bus id, interrupts ,etc ..

Here i have created pseudo device and driver to develop clear understanding of all these. In your board header file declare a private data structure according to resources used by your device. Here, i am declaring test_platfrom_data. The snippet 1 below will provide more information about members of test_platform_data.

Snippet 1

Snippet 1

In my board c file i have created instance of this structure  with appropriate data. This  user defined structure will passed to the driver w. Snippet 2 will provide you details about private data which is assigned to structure instance.

Snippet 2

Snippet 2

Now its time to define platform device in board file. In Linux kernel struct platform_device is declared. Lets create a instance of this structure. This will passed to kernel for registration of platform device. Snippet 3 will show you definition of platform device.

Snippet 3

Snippet 3

The main important point is the name of device.  Here in my case name of my device is “drivertest”.  From board init function add line(shown by snippet 4) to register this platform device.

After this edition in board file, compile kernel using appropriate cross compiler and boot your board with this kernel.

Snippet 4

Snippet 4

After success full booting of board, start making driver(.ko) file  for your registered device. Structure platfrom_driver is used to register platform driver. Here, snippet below shows the definition of platfrom_driver structure.  Register platform driver in init function using platfrom_driver_register.

Snippet 5

Snippet 5

Here, the impotent thing is the name of driver. Name of driver is as same as the name of device(in board file). On the registration of new platform driver, linux kernel compare its name with the previously defined all platform device name. If the match is found the probe function of the driver is called with  appropriate data which is responsible to initialize the device. The whole device structure is passed through probe function. The snippet below shows the subroutine for probe function.

Snippet 6

Snippet 6

In the probe function of driver,  i have extracted platform data from probe, which is assigned to the device at the booting time. This platform_data contains all the low level information about device. On the basis of this data, probe function initialize the device and irq using this data. Generally this data contains the information about module id, data rate, interrupt   etc..

I have compiled module and the output i is pasted below which shows the platform data is extracted from the probe.

Output

output sdfdfas

This is how the whole initialization of non dicoverable devices works.

19 Comments

Filed under Linux Device Driver, Platfrom device