diff libdha/kernelhelper/dhahelper.c @ 4470:2d28c737ed13

initial
author alex
date Sat, 02 Feb 2002 06:28:49 +0000
parents
children 423ce4451ca8
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libdha/kernelhelper/dhahelper.c	Sat Feb 02 06:28:49 2002 +0000
@@ -0,0 +1,332 @@
+/*
+    Direct Hardware Access kernel helper
+    
+    (C) 2002 Alex Beregszaszi <alex@naxine.org>
+    
+    Accessing hardware from userspace as USER (no root needed!)
+    
+    WARNING!
+    Don't use this on a production system! Use only at home, on a
+    "single-user" Unix system.
+
+    Tech:
+	Communication between userspace and kernelspace is going trought
+	character device using ioctl.
+
+    Usage:
+	mknod -m 666 /dev/dhahelper c 180 0
+	
+	Also you can change the major number, setting the "dhahelper_major"
+	module parameter, the default is 180, specified in dhahelper.h.
+	
+	Note: do not use other than minor==0, the module forbids it.
+
+    TODO:
+	* do memory mappin without fops:mmap
+	* implement unmap memory
+	* select (request?) a "valid" major number
+	* make security
+	* is pci handling needed? (libdha does this with lowlevel port funcs)
+	* test on older kernels (2.0.x (?) and 2.2.x)
+*/
+
+#ifndef MODULE
+#define MODULE
+#endif
+
+#ifndef __KERNEL__
+#define __KERNEL__
+#endif
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10)
+#include <linux/malloc.h>
+#else
+#include <linux/slab.h>
+#endif
+
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include <linux/mman.h>
+
+#include <linux/fs.h>
+#include <linux/unistd.h>
+
+#include "dhahelper.h"
+
+MODULE_AUTHOR("Alex Beregszaszi <alex@naxine.org>");
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("GPL"); /* modulelicensesh*t */
+#endif
+
+static int dhahelper_major = DEFAULT_MAJOR;
+MODULE_PARM(dhahelper_major, "i");
+
+/* 0 = silent */
+/* 1 = report errors (default) */
+/* 2 = debug */
+static int dhahelper_verbosity = 1;
+MODULE_PARM(dhahelper_verbosity, "i");
+
+static dhahelper_memory_t last_mem_request;
+
+
+static int dhahelper_open(struct inode *inode, struct file *file)
+{
+    if (dhahelper_verbosity > 1)
+	printk(KERN_DEBUG "dhahelper: device opened\n");
+
+    if (MINOR(inode->i_rdev) != 0)
+	return(-ENXIO);
+
+    MOD_INC_USE_COUNT;
+
+    return(0);
+}
+
+static int dhahelper_release(struct inode *inode, struct file *file)
+{
+    if (dhahelper_verbosity > 1)
+	printk(KERN_DEBUG "dhahelper: device released\n");
+
+    if (MINOR(inode->i_rdev) != 0)
+	return(-ENXIO);
+
+    MOD_DEC_USE_COUNT;
+
+    return(0);
+}
+
+static int dhahelper_ioctl(struct inode *inode, struct file *file,
+    unsigned int cmd, unsigned long arg)
+{
+    if (dhahelper_verbosity > 1)
+	printk(KERN_DEBUG "dhahelper: ioctl(cmd=%x, arg=%lx)\n",
+	    cmd, arg);
+
+    if (MINOR(inode->i_rdev) != 0)
+	return(-ENXIO);
+
+    switch(cmd)
+    {
+	case DHAHELPER_GET_VERSION:
+	{
+	    int version = API_VERSION;
+
+	    if (copy_to_user((int *)arg, &version, sizeof(int)))
+	    {
+		if (dhahelper_verbosity > 0)
+		    printk(KERN_ERR "dhahelper: failed copy to userspace\n");
+		return(-EFAULT);
+	    }
+
+	    break;
+	}
+	case DHAHELPER_PORT:
+	{
+	    dhahelper_port_t port;
+
+	    if (copy_from_user(&port, (dhahelper_port_t *)arg, sizeof(dhahelper_port_t)))
+	    {
+		if (dhahelper_verbosity > 0)
+		    printk(KERN_ERR "dhahelper: failed copy from userspace\n");
+		return(-EFAULT);
+	    }
+
+	    switch(port.operation)
+	    {
+		case PORT_OP_READ:
+		{
+		    switch(port.size)
+		    {
+			case 1:
+			    port.value = inb(port.addr);
+			    break;
+			case 2:
+			    port.value = inw(port.addr);
+			    break;
+			case 4:
+			    port.value = inl(port.addr);
+			    break;
+			default:
+			    if (dhahelper_verbosity > 0)
+				printk(KERN_ERR "dhahelper: invalid port read size (%d)\n",
+				    port.size);
+			    return(-EINVAL);
+		    }
+		    break;
+		}
+		case PORT_OP_WRITE:
+		{
+		    switch(port.size)
+		    {
+			case 1:
+			    outb(port.value, port.addr);
+			    break;
+			case 2:
+			    outw(port.value, port.addr);
+			    break;
+			case 4:
+			    outl(port.value, port.addr);
+			    break;
+			default:
+			    if (dhahelper_verbosity > 0)
+				printk(KERN_ERR "dhahelper: invalid port write size (%d)\n",
+				    port.size);
+			    return(-EINVAL);
+		    }
+		    break;
+		}
+		default:
+		    if (dhahelper_verbosity > 0)
+		        printk(KERN_ERR "dhahelper: invalid port operation (%d)\n",
+		    	    port.operation);
+		    return(-EINVAL);
+	    }
+
+	    /* copy back only if read was performed */
+	    if (port.operation == PORT_OP_READ)
+	    if (copy_to_user((dhahelper_port_t *)arg, &port, sizeof(dhahelper_port_t)))
+	    {
+		if (dhahelper_verbosity > 0)
+		    printk(KERN_ERR "dhahelper: failed copy to userspace\n");
+		return(-EFAULT);
+	    }
+	    
+	    break;
+	}
+	case DHAHELPER_MEMORY:
+	{
+	    dhahelper_memory_t mem;
+
+	    if (copy_from_user(&mem, (dhahelper_memory_t *)arg, sizeof(dhahelper_memory_t)))
+	    {
+		if (dhahelper_verbosity > 0)
+		    printk(KERN_ERR "dhahelper: failed copy from userspace\n");
+		return(-EFAULT);
+	    }
+	    
+	    switch(mem.operation)
+	    {
+		case MEMORY_OP_MAP:
+		{
+		    memcpy(&last_mem_request, &mem, sizeof(dhahelper_memory_t));
+
+		    break;
+		}
+		case MEMORY_OP_UNMAP:
+		    break;
+		default:
+		    if (dhahelper_verbosity > 0)
+			printk(KERN_ERR "dhahelper: invalid memory operation (%d)\n",
+			    mem.operation);
+		    return(-EINVAL);
+	    }
+	    
+	    if (copy_to_user((dhahelper_memory_t *)arg, &mem, sizeof(dhahelper_memory_t)))
+	    {
+		if (dhahelper_verbosity > 0)
+		    printk(KERN_ERR "dhahelper: failed copy to userspace\n");
+		return(-EFAULT);
+	    }
+	    
+	    break;
+	}
+	default:
+    	    if (dhahelper_verbosity > 0)
+		printk(KERN_ERR "dhahelper: invalid ioctl (%x)\n", cmd);
+	    return(-EINVAL);
+    }
+
+    return(0);
+}
+
+static int dhahelper_mmap(struct file *file, struct vm_area_struct *vma)
+{
+    if (last_mem_request.operation != MEMORY_OP_MAP)
+    {
+	if (dhahelper_verbosity > 0)
+	    printk(KERN_ERR "dhahelper: mapping not requested before mmap\n");
+	return(-EFAULT);
+    }
+    
+    if (dhahelper_verbosity > 1)
+	printk(KERN_INFO "dhahelper: mapping %x (size: %x)\n",
+	    last_mem_request.start+last_mem_request.offset, last_mem_request.size);
+    
+    if (remap_page_range(0, last_mem_request.start + last_mem_request.offset,
+	last_mem_request.size, vma->vm_page_prot))
+    {
+	if (dhahelper_verbosity > 0)
+	    printk(KERN_ERR "dhahelper: error mapping memory\n");
+	return(-EFAULT);
+    }
+
+    return(0);
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
+static struct file_operations dhahelper_fops =
+{
+    /*llseek*/	NULL,
+    /*read*/	NULL,
+    /*write*/	NULL,
+    /*readdir*/	NULL,
+    /*poll*/	NULL,
+    /*ioctl*/	dhahelper_ioctl,
+    /*mmap*/	dhahelper_mmap,
+    /*open*/	dhahelper_open,
+    /*flush*/	NULL,
+    /*release*/	dhahelper_release,
+    /* zero out the last 5 entries too ? */
+};
+#else
+static struct file_operations dhahelper_fops =
+{
+    owner:	THIS_MODULE,
+    ioctl:	dhahelper_ioctl,
+    mmap:	dhahelper_mmap,
+    open:	dhahelper_open,
+    release:	dhahelper_release
+};
+#endif
+
+static int __init init_dhahelper(void)
+{
+    printk(KERN_INFO "Direct Hardware Access kernel helper (C) Alex Beregszaszi\n");
+
+    if(register_chrdev(dhahelper_major, "dhahelper", &dhahelper_fops))
+    {
+    	if (dhahelper_verbosity > 0)
+	    printk(KERN_ERR "dhahelper: unable to register character device (major: %d)\n",
+		dhahelper_major);
+	return(-EIO);
+    }
+    
+    return(0);
+}
+
+static void __exit exit_dhahelper(void)
+{
+    unregister_chrdev(dhahelper_major, "dhahelper");
+}
+
+EXPORT_NO_SYMBOLS;
+
+module_init(init_dhahelper);
+module_exit(exit_dhahelper);