#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/pm_runtime.h>

#define TEMPLATE "template"
static dev_t template_dev_number;
static struct cdev *driver_object;
struct class *template_class;
static struct device *template_dev;
static int does_not_make_sense_to_suspend = 0;

#ifdef CONFIG_PM_RUNTIME /* Powermanagement */
static int template_runtime_suspend(struct device *dev )
{
    dev_info(dev,"template_runtime_suspend( %p )\n", dev );
    /* Zustand retten, Stromsparmodus einschalten */
    return 0;
}

static int template_runtime_resume(struct device *dev)
{
    dev_info(dev,"template_runtime_resume( %p )\n",dev);
    /* Stromsparmodus ausschalten, Zustand restaurieren */
    return 0;
}

static int template_runtime_idle(struct device *dev)
{
    dev_info(dev,"template_runtime_idle( %p )\n",dev);
    if ( does_not_make_sense_to_suspend )
        return -EAGAIN;
    pm_runtime_suspend( dev );
    return 0;
}

//static struct dev_pm_domain template_pm_ops = { /* Kernel 3.1 */
static struct dev_power_domain template_pm_ops = {
    .ops = {
        .runtime_resume = template_runtime_resume,
        .runtime_suspend = template_runtime_suspend,
        .runtime_idle = template_runtime_idle,
    },
};
#endif

static void fake_driver_probe( struct device *dev )
{
    dev_info(dev,"probe...\n");
    pm_runtime_set_active( template_dev );
    pm_runtime_enable( template_dev );
    pm_runtime_suspend( dev );
}

static void fake_driver_read( struct device *dev )
{
    dev_info(dev,"fake_driver_read...\n");
    pm_runtime_get_sync( dev );
    dev_info(dev,"i/o...\n");
    pm_runtime_put( dev );
}

static int __init template_init( void )
{
    if( alloc_chrdev_region(&template_dev_number,0,1,TEMPLATE)<0 )
        return -EIO;
    driver_object = cdev_alloc();
    if( driver_object==NULL ) goto free_device_number;
    driver_object->owner = THIS_MODULE;
    if( cdev_add(driver_object,template_dev_number,1) )
        goto free_cdev;
    template_class = class_create( THIS_MODULE, TEMPLATE );
    if( IS_ERR( template_class ) ) {
        pr_err("template: no udev support\n");
        goto free_cdev;
    }
    template_dev = device_create( template_class, NULL,
        template_dev_number, NULL, "%s", TEMPLATE );
#ifdef CONFIG_PM_RUNTIME /* Powermanagement */
    template_dev->pwr_domain = &template_pm_ops;
    //template_dev->pm_domain = &template_pm_ops; /* Kernel 3.1 */
#endif
    fake_driver_probe( template_dev );
    fake_driver_read( template_dev );
    return 0;
free_cdev:
    kobject_put( &driver_object->kobj );
free_device_number:
    unregister_chrdev_region( template_dev_number, 1 );
    return -EIO;
}

static void __exit template_exit( void )
{
    device_destroy( template_class, template_dev_number );
    class_destroy( template_class );
    cdev_del( driver_object );
    unregister_chrdev_region( template_dev_number, 1 );
    return;
}

module_init( template_init );
module_exit( template_exit );
MODULE_LICENSE( "GPL" );
