#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/uaccess.h>

static char hello_world[]="Hello World\n";

static dev_t hello_dev_number;
static struct cdev *driver_object;
static struct class *hello_class;
static struct device *hello_dev;

static ssize_t driver_read(struct file *instanz,char __user *user,
    size_t count, loff_t *offset )
{
    unsigned long not_copied, to_copy;

    to_copy = min( count, strlen(hello_world)+1 );
    not_copied=copy_to_user(user,hello_world,to_copy);
    return to_copy-not_copied;
}

static struct file_operations fops = {
    .owner= THIS_MODULE,
    .read= driver_read,
};

static int __init mod_init( void )
{
    if( alloc_chrdev_region(&hello_dev_number,0,1,"Hello")<0 )
        return -EIO;
    driver_object = cdev_alloc(); /* Anmeldeobjekt reserv. */
    if( driver_object==NULL )
        goto free_device_number;
    driver_object->owner = THIS_MODULE;
    driver_object->ops = &fops;
    if( cdev_add(driver_object,hello_dev_number,1) )
        goto free_cdev;
    /* Eintrag im Sysfs, damit Udev die Geraetedatei erzeugt */
    hello_class = class_create( THIS_MODULE, "Hello" );
    if( IS_ERR( hello_class ) ) {
        pr_err( "hello: no udev support\n");
        goto free_cdev;
    }
    hello_dev = device_create(hello_class,NULL,hello_dev_number,
        NULL, "%s", "hello" );
    dev_info( hello_dev, "mod_init called\n" );
    return 0;
free_cdev:
    kobject_put( &driver_object->kobj );
free_device_number:
    unregister_chrdev_region( hello_dev_number, 1 );
    return -EIO;
}

static void __exit mod_exit( void )
{
    dev_info( hello_dev, "mod_exit called\n" );
    /* Loeschen des Sysfs-Eintrags und damit der Geraetedatei */
    device_destroy( hello_class, hello_dev_number );
    class_destroy( hello_class );
    /* Abmelden des Treibers */
    cdev_del( driver_object );
    unregister_chrdev_region( hello_dev_number, 1 );
    return;
}

module_init( mod_init );
module_exit( mod_exit );
MODULE_LICENSE("GPL");