#include <linux/module.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/mm.h>

static int major;
static struct class *demo_class;
static struct page *common_page;

struct page *drv_vma_nopage( struct vm_area_struct *vma,
        unsigned long address, int *type )
{
    printk("drv_vma_nopage( %p, %lx, %p )\n", vma, address, type );
    if( (address-vma->vm_start) > 4095 )
       return NOPAGE_SIGBUS;
    get_page( common_page );
    if( type )
        *type = VM_FAULT_MINOR;
    return common_page;
}

static struct vm_operations_struct drv_vma_ops = {
    .nopage = drv_vma_nopage,
};

static int drv_mmap( struct file *instance, struct vm_area_struct *vma )
{
    printk("drv_mmap(%p, %p)\n", instance, vma);
    vma->vm_ops = &drv_vma_ops;
    vma->vm_flags |= VM_RESERVED;
    return 0;
}

static struct file_operations fops = {
    .mmap = drv_mmap,
};

static int __init drv_init(void)
{
    int *ptr;

    if( (major=register_chrdev(0,"mmap",&fops))==0 ) {
       printk("no major number available\n");
       return -EIO;
    }
    demo_class = class_create(THIS_MODULE, "demo");
    if( IS_ERR(demo_class) ) {
       printk("no udev support\n");
    } else {
       printk("class_device_create...\n");
       class_device_create(demo_class,NULL,MKDEV(major,0),NULL,"mmap");
    }
    common_page = alloc_page( GFP_USER );
    if( !common_page ) {
       if( !IS_ERR(demo_class) ) {
          class_device_destroy( demo_class, MKDEV(major,0) );
          class_destroy( demo_class );
       }
       unregister_chrdev( major, "mmap" );
       return -ENOMEM;
    }
    ptr = (int *)page_address( common_page );
    *ptr = 99; // Nur zur Demonstration!
    return 0;
}

static void __exit drv_exit(void)
{
   unregister_chrdev( major, "mmap" );
   if( !IS_ERR(demo_class) ) {
      class_device_destroy( demo_class, MKDEV(major,0) );
      class_destroy( demo_class );
   }
}

module_init( drv_init );
module_exit( drv_exit );
MODULE_LICENSE("GPL");