⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 memalloc.c.svn-base

📁 linux2.6.18下支持sd2.0的驱动程序源码,支持最大32G的SD卡
💻 SVN-BASE
字号:
/*--------------------------------------------------------------------------------                                                                            ----       This software is confidential and proprietary and may be used        ----        only as expressly authorized by a licensing agreement from          ----                                                                            ----                            Hantro Products Oy.                             ----                                                                            ----      In the event of publication, the following notice is applicable:      ----                                                                            ----                   (C) COPYRIGHT 2002 HANTRO PRODUCTS OY                    ----                            ALL RIGHTS RESERVED                             ----                                                                            ----          The entire notice above must be reproduced on all copies.         ----                                                                            --------------------------------------------------------------------------------------  Project  : Penguin----  Abstract : Encoder device driver (kernel module)--------------------------------------------------------------------------------------  Version control information, please leave untouched.----  $RCSfile$--  $Author$--  $Date$--  $Revision$--  $Name$--------------------------------------------------------------------------------*/#include <linux/kernel.h>#include <linux/module.h>/* needed for __init,__exit directives */#include <linux/init.h>/* needed for remap_page_range */#include <linux/mm.h>/* obviously, for kmalloc */#include <linux/slab.h>/* for struct file_operations, register_chrdev() */#include <linux/fs.h>/* standard error codes */#include <linux/errno.h>/* this header files wraps some common module-space operations ...    here we use mem_map_reserve() macro *///#include <linux/wrapper.h>/* needed for virt_to_phys() */#include <asm/io.h>#include <asm/uaccess.h>#include <linux/ioport.h>#include <linux/list.h>/* our own stuff */#include "memalloc.h"/* module description */MODULE_AUTHOR("Nagy Attila Jozsef, Hantro");MODULE_LICENSE("GPL");MODULE_DESCRIPTION("RAM allocation");static int memalloc_major = 188;    /* and this is our MAJOR *//* here's all the must remember stuff */struct allocation{    struct list_head list;    void *buffer;    unsigned int order;    int fid;};struct list_head heap_list;static kmem_cache_t *memalloc_cache;static spinlock_t mem_lock = SPIN_LOCK_UNLOCKED;static struct allocation *FindEntryByBus(unsigned long busaddr);static int AllocMemory(unsigned long *busaddr, unsigned int size);static int FreeMemory(unsigned long busaddr);static int MapBuffer(struct file *filp, struct vm_area_struct *vma);/* VM operations */static struct page *memalloc_vm_nopage(struct vm_area_struct *vma,                                       unsigned long address, int write_access){    PDEBUG("memalloc_vm_nopage: problem with mem access\n");    return NOPAGE_SIGBUS;   /* send a SIGBUS */}static void memalloc_vm_open(struct vm_area_struct *vma){    //MOD_INC_USE_COUNT;    PDEBUG("memalloc_vm_open:\n");}static void memalloc_vm_close(struct vm_area_struct *vma){    unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;    //    FreeMemory(offset);        //MOD_DEC_USE_COUNT;    PDEBUG("memalloc_vm_close:\n");}static struct vm_operations_struct memalloc_vm_ops = {    open:memalloc_vm_open,    close:memalloc_vm_close,    nopage:memalloc_vm_nopage,};/* the device's mmap method. The VFS has kindly prepared the process's   vm_area_struct for us, so we examine this to see what was requested.   this code is adapted from drivers/char/bttv.c:do_bttv_mmap */static int memalloc_mmap(struct file *filp, struct vm_area_struct *vma){    int result;    unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;    unsigned long size = (unsigned long) (vma->vm_end - vma->vm_start);    PDEBUG("memalloc_mmap: off = 0x%08lx size = %lu\n", offset, size);    spin_lock(&mem_lock);    result = MapBuffer(filp, vma);    if(result == 0)    {        vma->vm_ops = &memalloc_vm_ops;        /* open is not implicitly called when mmap is called */        memalloc_vm_open(vma);    }    spin_unlock(&mem_lock);    return result;}static int memalloc_ioctl(struct inode *inode, struct file *filp,                          unsigned int cmd, unsigned long arg){    int err = 0;    PDEBUG("ioctl cmd 0x%08x\n", cmd);    /*     * extract the type and number bitfields, and don't decode     * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok()     */    if(_IOC_TYPE(cmd) != MEMALLOC_IOC_MAGIC)        return -ENOTTY;    if(_IOC_NR(cmd) > MEMALLOC_IOC_MAXNR)        return -ENOTTY;    /*     * the direction is a bitmask, and VERIFY_WRITE catches R/W     * transfers. `Type' is user-oriented, while     * access_ok is kernel-oriented, so the concept of "read" and     * "write" is reversed     */    if(_IOC_DIR(cmd) & _IOC_READ)        err = !access_ok(VERIFY_WRITE, (void *) arg, _IOC_SIZE(cmd));    else if(_IOC_DIR(cmd) & _IOC_WRITE)        err = !access_ok(VERIFY_READ, (void *) arg, _IOC_SIZE(cmd));    if(err)        return -EFAULT;    switch (cmd)    {    case MEMALLOC_IOCHARDRESET:        /*         * reset the counter to 1, to allow unloading in case         * of problems. Use 1, not 0, because the invoking         * process has the device open.         */			/*        while(MOD_IN_USE)            MOD_DEC_USE_COUNT;        MOD_INC_USE_COUNT;		*/        break;    case MEMALLOC_IOCXGETBUFFER:        {            unsigned long size;            int result;            unsigned long busaddr = 0;                        spin_lock(&mem_lock);                        __get_user(size, (unsigned long *) arg);            result = AllocMemory(&busaddr, size);            __put_user((unsigned long) busaddr, (unsigned long *) arg);                        spin_unlock(&mem_lock);                        return result;            break;        }    case MEMALLOC_IOCSFREEBUFFER:        {            unsigned long busaddr;			int result;            spin_lock(&mem_lock);            __get_user(busaddr, (unsigned long *) arg);            spin_unlock(&mem_lock);            result = FreeMemory(busaddr);        }    }    return 0;}static int memalloc_open(struct inode *inode, struct file *filp){#if 0    if(MOD_IN_USE)  /* just single access */        return -EBUSY;#endif//    MOD_INC_USE_COUNT;    PDEBUG("dev opened\n");    return 0;}static int memalloc_release(struct inode *inode, struct file *filp){//    MOD_DEC_USE_COUNT;    PDEBUG("dev closed\n");    return 0;}/* VFS methods */static struct file_operations memalloc_fops = {    .mmap=memalloc_mmap,    .open=memalloc_open,    .release=memalloc_release,    .ioctl=memalloc_ioctl,};int __init memalloc_init(void){    int result;    /* if you want to test the module, you obviously need to     * "mknod". Note that normally you would use the misc_device     * interface, but I'm too lazy to change it. */    printk("<1>memalloc module init\n");    result = register_chrdev(memalloc_major, "memalloc", &memalloc_fops);    if(result < 0)    {        PDEBUG("unable to get major %d\n", memalloc_major);        goto err;    }    INIT_LIST_HEAD(&heap_list);    memalloc_cache = kmem_cache_create("memalloc", sizeof(struct allocation), 0,                                       SLAB_HWCACHE_ALIGN, NULL, NULL);    printk(KERN_INFO "memalloc: module inserted,major:%d\n",memalloc_major);    //EXPORT_NO_SYMBOLS;    return 0;  err:    printk(KERN_INFO "memalloc: module not inserted\n");    unregister_chrdev(memalloc_major, "memalloc");    return result;}void __exit memalloc_cleanup(void){    struct list_head *ptr;    struct allocation *entry;    struct page *pg;    PDEBUG("clenup called\n");    unregister_chrdev(memalloc_major, "memalloc");    while(!list_empty(&heap_list))    {        ptr = heap_list.next;        entry = list_entry(ptr, struct allocation, list);        /* first unreserve */        for(pg = virt_to_page(entry->buffer);            pg < (virt_to_page(entry->buffer) + (1 << entry->order)); pg++)        {            mem_map_unreserve(pg);        }        PDEBUG("Free buffer: base = 0x%08lx size = %lu\n",               virt_to_phys(entry->buffer), PAGE_SIZE << entry->order);        /* and now free the memory */        list_del(ptr);        free_pages((long) entry->buffer, entry->order);        kmem_cache_free(memalloc_cache, entry);    }    kmem_cache_destroy(memalloc_cache);    printk(KERN_INFO "memalloc: module removed\n");    return;}//arch_initcall(memalloc_init);module_init(memalloc_init);module_exit(memalloc_cleanup);static int AllocMemory(unsigned long *busaddr, unsigned int size){    struct page *pg;    void *start_addr, *end_addr;    u32 order, sz;    struct allocation *new;    *busaddr = 0;    for(order = 0, sz = PAGE_SIZE; sz < size; order++, sz <<= 1) ;    if(size != PAGE_SIZE << order)    {        PDEBUG("Alloc size >= PAGE_SIZE & must be a power of 2\n");        return -EINVAL;    }    new = kmem_cache_alloc(memalloc_cache, GFP_KERNEL);    if(!new)    {        printk(KERN_INFO "memalloc: failed to alloc memory\n");        return -ENOMEM;    }    /* alloc memory */    start_addr = (void *) __get_free_pages(GFP_KERNEL, order);    if(!start_addr)    {        printk(KERN_INFO "memalloc: failed to alloc memory\n");        kmem_cache_free(memalloc_cache, new);        return -ENOMEM;    }//    memset(start_addr, 0, size);    /* clear the mem *///    strcpy(start_addr, "Mapping test, data written from kernel!");    new->buffer = start_addr;    new->order = order;    list_add_tail(&new->list, &heap_list);    end_addr = start_addr + (PAGE_SIZE << order) - 1;    for(pg = virt_to_page(start_addr); pg <= virt_to_page(end_addr); pg++)    {        mem_map_reserve(pg);    }    *busaddr = virt_to_phys(start_addr);    PDEBUG("Alloc buffer: base = 0x%08lx size = %lu\n", *busaddr,           PAGE_SIZE << order);    return 0;}static int FreeMemory(unsigned long busaddr){    struct page *pg;    struct allocation *entry = NULL;    entry = FindEntryByBus(busaddr);    if(entry == NULL)    {        return -EINVAL;    }    /* first unreserve */    for(pg = virt_to_page(entry->buffer);        pg < (virt_to_page(entry->buffer) + (1 << entry->order)); pg++)    {        mem_map_unreserve(pg);    }    /* and now free the memory */    list_del(&entry->list);    free_pages((long) entry->buffer, entry->order);    kmem_cache_free(memalloc_cache, entry);    PDEBUG("Free buffer: base = 0x%08lx size = %lu\n",           virt_to_phys(entry->buffer), PAGE_SIZE << entry->order);    return 0;}static int MapBuffer(struct file *filp, struct vm_area_struct *vma){    unsigned long phys;    unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;    unsigned long start = (unsigned long) vma->vm_start;    unsigned long size = (unsigned long) (vma->vm_end - vma->vm_start);    struct allocation *entry;    entry = FindEntryByBus(offset);    if(entry == NULL)    {        PDEBUG("map buffer entry not found in list\n");        return -EINVAL;    }    /* if userspace tries to mmap beyond end of our buffer, fail */    if(size != (PAGE_SIZE << entry->order))    {        PDEBUG("map buffer size does not match\n");        return -EINVAL;    }    vma->vm_flags |= VM_RESERVED;    vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);    /* Remember this won't work for vmalloc()d memory ! */    phys = virt_to_phys(entry->buffer);	/* stiger: old    if(remap_page_range(start, phys, size, vma->vm_page_prot))	*/    //printk("<1>memalloc:remap_page_range,%x,%x,%x, :%x\n",start, phys , size, pgprot_val(vma->vm_page_prot) );    if(remap_pfn_range(vma, start, phys >> PAGE_SHIFT, size, vma->vm_page_prot))//    if(remap_page_range(vma, start, phys , size, vma->vm_page_prot))    {        return -EAGAIN;    }    return 0;}struct allocation *FindEntryByBus(unsigned long busaddr){    struct list_head *ptr;    struct allocation *entry;    list_for_each(ptr, &heap_list)    {        entry = list_entry(ptr, struct allocation, list);        if(virt_to_phys(entry->buffer) == busaddr)        {            return entry;        }    }    return NULL;}

⌨️ 快捷键说明

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