Thursday, May 22, 2014

Platform Device Driver - a practical approach - 1


Platform devices are inherently not discover-able, i.e. the hardware cannot say "Hey! I'm present!" to the software. For example PCI and USB are self discover-able, but I2C is not.
In the embedded and system-on-chip world, non-discoverable devices are, if anything, increasing in number. So the kernel still needs to provide ways to be told about the hardware that is actually present. Platform devices have long been used in this role in the kernel.
There are two important components here.
  1. Platform Driver
  2. Platform Device
Platform Driver - Set of operation done on device
Linux kernel defines set of standard operations which will be performed on a platform device.
Refer http://lxr.free-electrons.com/source/include/linux/platform_device.h#L173
At a minimum, the probe() and remove() callbacks must be supplied; the other callbacks have to do with power management and should be provided if they are relevant.
static int sample_drv_probe(struct platform_device *pdev)
{
   //Empty Probe function.
}
static int sample_drv_remove(struct platform_device *pdev)
{
  //Empty remove function.
}

static struct platform_driver sample_pldriver = {
    .probe          = sample_drv_probe,
    .remove         = sample_drv_remove,
    .driver = {
            .name  = DRIVER_NAME,
    },
};
In the above code just make a note of DRIVER_NAME. We discuss bit later.
So now a platform driver with two operations(probe and remove) is ready. This driver should be register with kernel.
NOTE:: I guess, you know that every loadable device driver is basically kernel module. For making our code complete, moving platform driver in to helloworld kernel module.
#include <linux/module.h>
#include <linux/kernel.h>

MODULE_LICENSE("GPL");

int ourinitmodule(void)
{
    printk(KERN_ALERT "\n Welcome to sample Platform driver.... \n");
    return 0;
}

void ourcleanupmodule(void)
{
    printk(KERN_ALERT "\n Thanks....Exiting sample Platform driver... \n");
    return;
}

module_init(ourinitmodule);
module_exit(ourcleanupmodule);
Now platform driver related stuff in kernel module.
#include <linux/module.h>
#include <linux/kernel.h>

//for platform drivers....
#include <linux/platform_device.h>

#define DRIVER_NAME "Sample_Pldrv"

MODULE_LICENSE("GPL");

/**************/ 
static int sample_drv_probe(struct platform_device *pdev){
}
static int sample_drv_remove(struct platform_device *pdev){
}

static struct platform_driver sample_pldriver = {
    .probe          = sample_drv_probe,
    .remove         = sample_drv_remove,
    .driver = {
            .name  = DRIVER_NAME,
    },
};
/**************/  

int ourinitmodule(void)
{
    printk(KERN_ALERT "\n Welcome to sample Platform driver.... \n");

    /* Registering with Kernel */
    platform_driver_register(&sample_pldriver);

    return 0;
}

void ourcleanupmodule(void)
{
    printk(KERN_ALERT "\n Thanks....Exiting sample Platform driver... \n");

    /* Unregistering from Kernel */
    platform_driver_unregister(&sample_pldriver);

    return;
}

module_init(ourinitmodule);
module_exit(ourcleanupmodule);
Now driver named "Sample_Pldrv" is ready with two empty functions.
Platform Device - Information about device
Kernel knows about device's information like IRQ number, memory locations, etc by registering platform device. To operate on this device, we early wrote platform driver right ?? If you want to bind the platform device to a driver, then device must be registered with same name which driver is registered. In our case "Sample_Pldrv".
/* Specifying my resources information */
static struct resource sample_resources[] = {
        {
                .start          = RESOURCE1_START_ADDRESS,
                .end            = RESOURCE1_END_ADDRESS,
                .flags          = IORESOURCE_MEM,
        },
        {
                .start          = RESOURCE2_START_ADDRESS,
                .end            = RESOURCE2_END_ADDRESS,
                .flags          = IORESOURCE_MEM,
        },
    {
                .start          = SAMPLE_DEV_IRQNUM,
                .end            = SAMPLE_DEV_IRQNUM,
                .flags          = IORESOURCE_IRQ,
        }

    };    

static struct platform_device sample_device = {
        .name           = DRIVER_NAME,
        .id             = -1,
        .num_resources  = ARRAY_SIZE(sample_resources),
        .resource       = sample_resources,
};
Here, there are two memory related information and one IRQ information is mentioned. Now you got the role of DRIVER_NAME ??
Either device and driver can be in single module or as separate module. It's your choice. But probe() will be called when both device and driver is available.
Say you loaded only driver. when the device with same name is loaded, the probe() will be called.
Refer https://github.com/jeyaramvrp/kernel-module-programming/tree/master/sample-platform-driver for simple platform driver and device code written as separate kernel module.

8 comments:

  1. Hi
    Thanks for the nice example
    I am able to compile and load the driver.
    But how to communicate with this driver from user space. I am not seeing any entry in /dev
    Please help

    Thanks

    ReplyDelete
    Replies
    1. https://github.com/jeyaramvrp/kernel-module-programming/tree/master/sample-char-dir

      Delete
  2. In general how to communicate with platform driver/devices from user space e.g. read/write etc

    Thanks

    ReplyDelete
    Replies
    1. For exposing to user space, platform driver should register as char driver as well. So ioctls() will allow to communicate with platform driver.

      Delete
  3. Hi ,I need ur help, I am beginner in embedded linux. I am using beagle bone black debian 3.8.13 Bone 47. I have connected external i2c touch screen controller with beagle bone. touch screen controller is edt-ft5x06. I have created dts file and mapped i2c1 with edt-ft5x06. Interrupts pin for edt-ft5x06 also mapped. I have echoed this dtb file to kernel.
    One more point is edt-ft5x06.ko module not loaded during boot. So i loaded by insmod command. Now i can see my edt-ft module inside /proc/modules. But probe function of edt-ft5x06 module is not executed and interrupt pins are not registred in /proc/interrupts. What I m doing wrong, can u please help me ?
    Please tell me when probe function will be executed?

    ReplyDelete
    Replies
    1. Hi Sorry for late reply.

      Probe will be executed when it matches with platform_device. I don't have much idea about device tree. Possibly the problem is fixed now I hope.

      Delete
  4. Hi jey Jay,

    Can you mention some example of using ioctls() to communicate with platform driver?

    Thanks

    ReplyDelete
    Replies
    1. Here is an similar example. http://lxr.free-electrons.com/source/drivers/scsi/3w-xxxx.c#L2341

      In the above driver, a char driver is registered from probe() function.

      Most of SOC drivers developed will be combination of platform and char drivers.

      Delete