Create a simple platform driver

This post explains how to create simple platform driver and get its probe called. The driver can be compiled as module or inbuilt in kernel. When driver’s compatible string matches with any device’s compatible string, driver’s probe is called. Creating simple platform driver would help to:

  • Get into Linux kernel world
  • Device tree basic concept

There are things needs to be done for this:

  • Add Kconfig option for driver option in kernel menuconfig
  • Add Makefile entry to build driver file(s)
  • Add driver source file
  • Add device node in DTS with compatible string matching with driver

As our driver doesn’t fall into any category, I have created as misc driver(drivers/misc/simple_misc_drv.c).

Add Kconfig option

Add below lines in drivers/misc/Kconfig:

config SIMPLE_MISC_DRIVER
	tristate "Simple misc driver"
	default n
	help
	  Simple misc platform driver. This driver is designed to demonstrate
	  platform driver and DTS compatible string scenarios.

Add Makefile entry to build driver file(s)

Add below lines in drivers/misc/Makefile:

obj-$(CONFIG_SIMPLE_MISC_DRIVER)	+= simple_misc_drv.o

Add driver source file

#include <linux/module.h>
#include <linux/platform_device.h>

static int simple_misc_probe(struct platform_device *pdev)
{
pr_info("#### %s() ####\n", __func__);
return 0;
}

static int simple_misc_remove(struct platform_device *pdev)
{
pr_info("#### %s() ####\n", __func__);
return 0;
}

static const struct of_device_id simple_misc_of_id_table[] = {
{ .compatible = "test,simple-misc-dev" },
{ }
};
MODULE_DEVICE_TABLE(of, simple_misc_of_id_table);

static struct platform_driver simple_misc_driver = {
.driver = {
.name = "simple-misc-drv",
.of_match_table = simple_misc_of_id_table,
},
.probe = simple_misc_probe,
.remove = simple_misc_remove,
};

module_platform_driver(simple_misc_driver);

MODULE_AUTHOR("Rajan Vaja");
MODULE_DESCRIPTION("Simple misc platform driver");
MODULE_LICENSE("GPL v2");

 

Add device node in DTS

Add below node in device tree.

	simple_misc_device: simple-misc-device {
		compatible = "test,simple-misc-dev";
	};

 

Debugging

What to do if your probe is not called. Here is some checklist which I know:

  1. Driver is not getting compiled
  2. Driver registration is failing. This can be due to:
    1. Driver name duplication with any other driver
  3. Device is not registered due to:
    1. Device node “status” property is set to “disabled” in DTS. If so change it to “okay”.
Advertisements

Linux kernel module with multiple source files

lets say we have two source files from which we want to create modules. Lets say we have two source files from which we want to create modules.

Correct method

Correct syntax to be used in Makefile is as below:

obj-m += my_module.o
my_module-objs := my_module_file1.o my_module_file2.o

Incorrect method

Below syntax will try to create two separate independent modules and hence will not work:

obj-m += my_module_file1.o my_module_file2.o

Thanks Tejas Patel for this!

Cross compiling and Linking

  • Compilation
    arm-uclinuxeabi-gcc  -g  -Os   -g2 -mthumb -mcpu=cortex-m3 -fsigned-char -O2 -fno-builtin-puts -fno-common -ffixed-r8 -D__KERNEL__ -I -fno-builtin -ffreestanding -isystem  -pipe  -DCONFIG_ARM -D__ARM__ -D<MACRO="(VALUE)"> -Wall -Wstrict-prototypes -fno-stack-protector   -o   -c
    
  • De-assemble
    arm-uclinuxeabi-objdump -S <O_FILE>
    
  • Library
    arm-uclinuxeabi-ar crv <A_FILENAME> <O_FILES> 
    
  • Linking
    arm-uclinuxeabi-ld -Bstatic -T<LDS_FILENAME> -u<UNDEFINED_SYMBOLS> <O_FILES> <A_FILES> -lgcc -Map <MAP_FILE> -o <OUT_FILENAME>
    

    Example linker script:

    OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
    OUTPUT_ARCH(arm)
    ENTRY(main)
    
    MEMORY
    {
     /* SmartFusion internal eSRAM */
     ram (rwx) : ORIGIN = 0x20000000, LENGTH = 64k
    }
    
    SECTIONS
    {
     .text :
     {
       . = ALIGN(4);
        *(.text*)
    	. = ALIGN(4);
        _etext = .;
     } >ram
    }
    end = .;
  • Other format output
    arm-none-eabi-objcopy --gap-fill=0xff -O ihex myprog  "myprog.hex"
    arm-none-eabi-objcopy --gap-fill=0xff -O binary myprog  "myprog.bin"
    arm-none-eabi-objcopy -O srec myprog "myprog.srec"
    arm-none-eabi-objdump -h -S myprog > "myprog.lst"

Ask for confirmation before running bash command

Sometimes we need to confirm or give warning before running any command.

In my case, I need to check JAVA version before building android source.
Android L needs to be built with Java 1.7 and Android KK needs to be built with Java 1.6. So it is required to change JAVA version while switching between Android K and L.

If we forget to change JAVA version, it tries to build source with improper version consuming lots of time (as java version changes, it tries to rebuild all the things) and results in failure at some point.

To avoid this, I updated bash script so that before build command it checks for JAVA version and asks user if it is proper or not. Upon confirmation from user it proceeds further.

Below is script I wrote and included in bashrc using “source” command at end of bashrc.


#!/bin/bash

function jdkmsg {
echo -e "\e[38;5;82m"
echo "########################################################################"
echo -e "\tHope You are using correct JDK version\n"
echo -e "JAVA_HOME = $JAVA_HOME"
java -version
javac -version
echo -e "You can change java and javac versions using below commands:"
echo -e "sudo update-alternatives --config java and"
echo -e "sudo update-alternatives --config javac"
echo "########################################################################"
echo -e "\e[39;5;82m"
}

debug_trap () {
	if [[ "$BASH_COMMAND" == *"build.sh"* ]]
	then
		jdkmsg
		echo "Allow?"
		select choice in yes no
		do
			if [ "$choice" = "yes" ]
				then break
			elif [ "$choice" = "no" ]
			then return 1
			fi
		done
	fi
}

shopt -s extdebug
trap debug_trap DEBUG