view libdha/kernelhelper/dhahelper.c @ 10560:11826d9f90c7

this patch fixes 1) some bugs introduced in the tuner autodetection and in the channel-parsing functions, 3) retries reading when the mplayer/mencoder don't read fast enough (sooner it exited) but especially 4) makes the stream compliant with the new, modular stream api (the one currently in CVS is not and is totally unreachable). [and maybe more, next time please include cvslog in patch! -- A'rpi] patch by Nico <nsabbi@libero.it>
author arpi
date Mon, 11 Aug 2003 00:02:46 +0000
parents 0165fc26bd12
children
line wrap: on
line source

/*
    Direct Hardware Access kernel helper
    
    (C) 2002 Alex Beregszaszi <alex@fsn.hu>
    
    Accessing hardware from userspace as USER (no root needed!)

    Tested on 2.2.x (2.2.19) and 2.4.x (2.4.3,2.4.17).
    
    License: GPL
    
    WARNING! THIS MODULE VIOLATES SEVERAL SECURITY LINES! DON'T USE IT
    ON PRODUCTION SYSTEMS, ONLY AT HOME, ON A "SINGLE-USER" SYSTEM.
    NO WARRANTY!

    Tech:
	Communication between userspace and kernelspace goes over 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 mapping without fops:mmap
	* implement unmap memory
	* select (request?) a "valid" major number (from Linux project? ;)
	* make security
	* is pci handling needed? (libdha does this with lowlevel port funcs)
	* is mttr handling needed?
	* test on older kernels (2.0.x (?))
*/

#ifndef MODULE
#define MODULE
#endif

#ifndef __KERNEL__
#define __KERNEL__
#endif

#include <linux/config.h>

#ifdef CONFIG_MODVERSION
#define MODVERSION
#include <linux/modversions.h>
#endif

#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@fsn.hu>");
MODULE_DESCRIPTION("Provides userspace access to hardware (security violation!)");
#ifdef MODULE_LICENSE
MODULE_LICENSE("GPL");
#endif

static int dhahelper_major = DEFAULT_MAJOR;
MODULE_PARM(dhahelper_major, "i");
MODULE_PARM_DESC(dhahelper_major, "Major number of dhahelper characterdevice");

/* 0 = silent */
/* 1 = report errors (default) */
/* 2 = debug */
static int dhahelper_verbosity = 1;
MODULE_PARM(dhahelper_verbosity, "i");
MODULE_PARM_DESC(dhahelper_verbosity, "Level of verbosity (0 = silent, 1 = only errors, 2 = debug)");

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:
		{
#if 1
		    memcpy(&last_mem_request, &mem, sizeof(dhahelper_memory_t));
#else
		    mem.ret = do_mmap(file, mem.start, mem.size, PROT_READ|PROT_WRITE,
			MAP_SHARED, mem.offset);
#endif

		    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

#if KERNEL_VERSION < KERNEL_VERSION(2,4,0)
int init_module(void)
#else 
static int __init init_dhahelper(void)
#endif
{
    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);
}

#if KERNEL_VERSION < KERNEL_VERSION(2,4,0)
void cleanup_module(void)
#else
static void __exit exit_dhahelper(void)
#endif
{
    unregister_chrdev(dhahelper_major, "dhahelper");
}

EXPORT_NO_SYMBOLS;

#if KERNEL_VERSION >= KERNEL_VERSION(2,4,0)
module_init(init_dhahelper);
module_exit(exit_dhahelper);
#endif