Showing posts with label platform drivers. Show all posts
Showing posts with label platform drivers. Show all posts

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.