dhahelper.c

来自「linux下的MPEG1」· C语言 代码 · 共 1,240 行 · 第 1/3 页

C
1,240
字号
/*    Direct Hardware Access kernel helper        (C) 2002 Alex Beregszaszi <alex@naxine.org>    (C) 2002-2003 Nick Kurshev <nickols_k@mail.ru>    (C) 2002-2004 Måns Rullgård <mru@users.sourceforge.net>    Accessing hardware from userspace as USER (no root needed!)    Tested on 2.2.x (2.2.19), 2.4.x (2.4.3,2.4.17) and 2.6.1.        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!        IF YOU WANT TO USE IT ON PRODUCTION SYSTEMS THEN PLEASE READ 'README'    FILE TO KNOW HOW TO PREVENT ANONYMOUS ACCESS TO THIS MODULE.    Tech:	Communication between userspace and kernelspace goes over character	device using ioctl.    Usage:	mknod -m 600 /dev/dhahelper c 252 0		Also you can change the major number, setting the "dhahelper_major"	module parameter, the default is 252, specified in dhahelper.h.		Note: do not use other than minor==0, the module forbids it.    TODO:	* 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/pagemap.h>#include <linux/init.h>#include <linux/pci.h>#include <linux/interrupt.h>#include <linux/vmalloc.h>#include <linux/string.h>#include <linux/errno.h>#include <asm/io.h>#include <asm/pgtable.h>#include <asm/unistd.h>#include <asm/uaccess.h>#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)#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>#ifdef CONFIG_MTRR #include <asm/mtrr.h>#endif#ifdef CONFIG_DEVFS_FS#include <linux/devfs_fs_kernel.h>#endif#include "dhahelper.h"#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)#define pte_offset(p,a) pte_offset_kernel(p,a)#define LockPage(p) SetPageLocked(p)#define UnlockPage(p) ClearPageLocked(p)#define irqreturn(n) return(n)#else#define irqreturn_t void#define irqreturn(n) return#endifMODULE_AUTHOR("Alex Beregszaszi <alex@naxine.org>, Nick Kurshev <nickols_k@mail.ru>, Måns Rullgård <mru@users.sf.net>");MODULE_DESCRIPTION("Provides userspace access to hardware");#ifdef MODULE_LICENSEMODULE_LICENSE("GPL");#endifstatic 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 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;#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)    MOD_INC_USE_COUNT;#endif    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;#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)    MOD_DEC_USE_COUNT;#endif    return 0;}static int dhahelper_get_version(int * arg){	int version = API_VERSION;	if (copy_to_user(arg, &version, sizeof(int)))	{		if (dhahelper_verbosity > 0)		    printk(KERN_ERR "dhahelper: failed copy to userspace\n");		return -EFAULT;	}	return 0;}static int dhahelper_port(dhahelper_port_t * arg){	dhahelper_port_t port;	if (copy_from_user(&port, 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(arg, &port, sizeof(dhahelper_port_t)))	{		if (dhahelper_verbosity > 0)		    printk(KERN_ERR "dhahelper: failed copy to userspace\n");		return -EFAULT;	}	return 0;}/*******************************//* Memory management functions *//* from kernel:/drivers/media/video/bttv-driver.c *//*******************************/#define MDEBUG(x)	do { } while(0)		/* Debug memory management *//* [DaveM] I've recoded most of this so that: * 1) It's easier to tell what is happening * 2) It's more portable, especially for translating things *    out of vmalloc mapped areas in the kernel. * 3) Less unnecessary translations happen. * * The code used to assume that the kernel vmalloc mappings * existed in the page tables of every process, this is simply * not guarenteed.  We now use pgd_offset_k which is the * defined way to get at the kernel page tables. *//* Given PGD from the address space's page table, return the kernel * virtual mapping of the physical memory mapped at ADR. */static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr){        unsigned long ret = 0UL;	pmd_t *pmd;	pte_t *ptep, pte;  	if (!pgd_none(*pgd)) {                pmd = pmd_offset(pgd, adr);                if (!pmd_none(*pmd)) {                        ptep = pte_offset(pmd, adr);                        pte = *ptep;                        if(pte_present(pte)) {				ret  = (unsigned long) page_address(pte_page(pte));				ret |= (adr & (PAGE_SIZE - 1));							}                }        }        MDEBUG(printk("uv2kva(%lx-->%lx)", adr, ret));	return ret;}static inline unsigned long uvirt_to_bus(unsigned long adr) {        unsigned long kva, ret;        kva = uvirt_to_kva(pgd_offset(current->mm, adr), adr);	ret = virt_to_bus((void *)kva);        MDEBUG(printk("uv2b(%lx-->%lx)", adr, ret));        return ret;}static inline unsigned long uvirt_to_pa(unsigned long adr) {        unsigned long kva, ret;        kva = uvirt_to_kva(pgd_offset(current->mm, adr), adr);	ret = virt_to_phys((void *)kva);        MDEBUG(printk("uv2b(%lx-->%lx)", adr, ret));        return ret;}static inline unsigned long kvirt_to_bus(unsigned long va) {        unsigned long kva, ret;        kva = uvirt_to_kva(pgd_offset_k(va), va);	ret = virt_to_bus((void *)kva);        MDEBUG(printk("kv2b(%lx-->%lx)", adr, ret));        return ret;}/* Here we want the physical address of the memory. * This is used when initializing the contents of the * area and marking the pages as reserved. */static inline unsigned long kvirt_to_pa(unsigned long va) {        unsigned long kva, ret;        kva = uvirt_to_kva(pgd_offset_k(va), va);	ret = __pa(kva);        MDEBUG(printk("kv2pa(%lx-->%lx)", adr, ret));        return ret;}static void * rvmalloc(signed long size){	void * mem;	unsigned long adr, page;	mem=vmalloc_32(size);	if (mem) 	{		memset(mem, 0, size); /* Clear the ram out, no junk to the user */	        adr=(unsigned long) mem;		while (size > 0)                 {	                page = kvirt_to_pa(adr);			SetPageReserved(virt_to_page(__va(page)));			adr+=PAGE_SIZE;			size-=PAGE_SIZE;		}	}	return mem;}static int pag_lock(unsigned long addr){	unsigned long page;	unsigned long kva;	kva = uvirt_to_kva(pgd_offset(current->mm, addr), addr);	if(kva)	{	    lock_it:	    page = uvirt_to_pa((unsigned long)addr);	    LockPage(virt_to_page(__va(page)));	    SetPageReserved(virt_to_page(__va(page)));	}	else	{	    copy_from_user(&page,(char *)addr,1); /* try access it */	    kva = uvirt_to_kva(pgd_offset(current->mm, addr), addr);	    if(kva) goto lock_it;	    else return EPERM;	}	return 0;}static int pag_unlock(unsigned long addr){	    unsigned long page;	    unsigned long kva;	    kva = uvirt_to_kva(pgd_offset(current->mm, addr), addr);	    if(kva)	    {		page = uvirt_to_pa((unsigned long)addr);		UnlockPage(virt_to_page(__va(page)));		ClearPageReserved(virt_to_page(__va(page)));		return 0;	    }	    return EPERM;}static void rvfree(void * mem, signed long size){        unsigned long adr, page;        	if (mem) 	{	        adr=(unsigned long) mem;		while (size > 0)                 {	                page = kvirt_to_pa(adr);			ClearPageReserved(virt_to_page(__va(page)));			adr+=PAGE_SIZE;			size-=PAGE_SIZE;		}		vfree(mem);	}}static int dhahelper_virt_to_phys(dhahelper_vmi_t *arg){	dhahelper_vmi_t mem;	unsigned long i,nitems;	char *addr;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?