allocator.c
上传用户:wudi5211
上传日期:2010-01-21
资源大小:607k
文件大小:8k
源码类别:

嵌入式Linux

开发平台:

C/C++

  1. /*
  2.  * allocator.c -- allocate after high_memory, if available
  3.  *
  4.  * Copyright (C) 1998,2000   rubini@linux.it (Alessandro Rubini)
  5.  *
  6.  *   This program is free software; you can redistribute it and/or modify
  7.  *   it under the terms of the GNU General Public License as published by
  8.  *   the Free Software Foundation; either version 2 of the License, or
  9.  *   (at your option) any later version.
  10.  *
  11.  *   This program is distributed in the hope that it will be useful,
  12.  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  *   GNU General Public License for more details.
  15.  *
  16.  *   You should have received a copy of the GNU General Public License
  17.  *   along with this program; if not, write to the Free Software
  18.  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  19.  *
  20.  */
  21. #ifndef __KERNEL__
  22. #  define __KERNEL__
  23. #endif
  24. #ifdef MODULE
  25. #  define EXPORT_SYMTAB
  26. #endif
  27. #include <linux/version.h>
  28. #include <linux/config.h>
  29. #include <linux/module.h>
  30. #include <linux/sched.h>
  31. #include <linux/kernel.h>
  32. #include <linux/fs.h>
  33. #include <linux/proc_fs.h>
  34. #include <linux/errno.h>
  35. #include <linux/types.h>
  36. #include <asm/page.h>
  37. #include "sysdep.h"
  38. #include "allocator.h"
  39. MODULE_AUTHOR("Alessandro Rubini");
  40. #ifdef MODULE
  41. /* export symbols, using a different approach for 2.0 */
  42. #  ifdef __USE_OLD_SYMTAB__
  43. static struct symbol_table allocator_syms = {
  44. #include <linux/symtab_begin.h>
  45.         X(allocator_allocate_dma),
  46.         X(allocator_free_dma),
  47. #include <linux/symtab_end.h>
  48. };
  49. #    define allocator_register_symtab()  register_symtab(&allocator_syms)
  50. #  else /* new way (2.2, 2.4) */
  51. EXPORT_SYMBOL(allocator_allocate_dma);
  52. EXPORT_SYMBOL(allocator_free_dma);
  53. #    define  allocator_register_symtab()  /* nothing */
  54. #  endif /* symtab way */
  55. #else
  56. #define allocator_register_symtab() /* nothing */
  57. #endif /* module */
  58. #define ALL_MSG "allocator: "
  59. #undef PDEBUG             /* undef it, just in case */
  60. #ifdef ALL_DEBUG
  61. #  ifdef __KERNEL__
  62.      /* This one if debugging is on, and kernel space */
  63. #    define PDEBUG(fmt, args...) printk( KERN_DEBUG ALL_MSG fmt, ## args)
  64. #  else
  65.      /* This one for user space */
  66. #    define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args)
  67. #  endif
  68. #else
  69. #  define PDEBUG(fmt, args...) /* not debugging: nothing */
  70. #endif
  71. #undef PDEBUGG
  72. #define PDEBUGG(fmt, args...) /* nothing: it's a placeholder */
  73. /* This is meant to be a parameter: 0 = probe, pos. = megs, neg. = disable */
  74. int allocator_himem = 0;
  75. static unsigned long allocator_buffer      = 0;  /* physical address */
  76. static unsigned long allocator_buffer_size = 0;  /* bytes */
  77. /*
  78.  * The allocator keeps a list of DMA areas, so multiple devices
  79.  * can coexist. The list is kept sorted by address
  80.  */
  81. struct allocator_struct {
  82.     unsigned long address;
  83.     unsigned long size;
  84.     struct allocator_struct *next;
  85. };
  86. struct allocator_struct *allocator_list = NULL;
  87. #if 0
  88. static int dump_list(void)
  89. {
  90.     struct allocator_struct *ptr;
  91.     PDEBUG("Current list:n");
  92.     for (ptr = allocator_list; ptr; ptr = ptr->next) {
  93.         PDEBUG("0x%08lx (size %likB)n",ptr->address,ptr->size>>10);
  94.     }
  95.     return 0;
  96. }
  97. #endif
  98. /* ========================================================================
  99.  * This function is the actual allocator.
  100.  *
  101.  * If space is available in high memory (as detected at load time), that
  102.  * one is returned. The return value is a physical address (i.e., it can
  103.  * be used straight ahead for DMA, but needs remapping for program use).
  104.  */
  105. unsigned long allocator_allocate_dma (unsigned long bytes, int prio)
  106. {
  107.     struct allocator_struct *ptr = allocator_list, *newptr;
  108.     /* check if high memory is available */
  109.     if (!allocator_buffer)
  110.         return 0;
  111.      /* Round it to a multiple of the pagesize */
  112.      bytes = PAGE_ALIGN(bytes);
  113.     PDEBUG("request for %li bytesn", bytes);
  114.     while (ptr && ptr->next) {
  115.         if (ptr->next->address - (ptr->address + ptr->size) >= bytes)
  116.             break; /* enough space */
  117.         ptr = ptr->next;
  118.     }
  119.     if (!ptr->next) {
  120.         /* dump_list(); */
  121.         PDEBUG("alloc failedn");
  122.         return 0; /* end of list */
  123.     }
  124.     newptr = kmalloc(sizeof(struct allocator_struct),prio);
  125.     if (!newptr)
  126.         return 0;
  127.     /* ok, now stick it after ptr */
  128.     newptr->address = ptr->address + ptr->size;
  129.     newptr->size = bytes;
  130.     newptr->next = ptr->next;
  131.     ptr->next = newptr;
  132.     /* dump_list(); */
  133.     PDEBUG("returning 0x%08lxn",newptr->address);
  134.     return newptr->address;
  135. }
  136. int allocator_free_dma (unsigned long address)
  137. {
  138.     struct allocator_struct *ptr = allocator_list, *prev;
  139.     while (ptr && ptr->next) {
  140.         if (ptr->next->address == address)
  141.             break;
  142. ptr = ptr->next;
  143. }
  144.     /* the one being freed is ptr->next */
  145.     prev = ptr; ptr = ptr->next;
  146.     if (!ptr) {
  147.         printk(KERN_ERR ALL_MSG "free_dma(0x%08lx) but add. not allocatedn",
  148.                ptr->address);
  149.         return -EINVAL;
  150.     }
  151.     PDEBUGG("freeing: %08lx (%li) next %08lxn",ptr->address,ptr->size,
  152.    ptr->next->address);
  153.     prev->next = ptr->next;
  154.     kfree(ptr);
  155.     /* dump_list(); */
  156.     return 0;
  157. }
  158. /* ========================================================================
  159.  * Init and cleanup
  160.  *
  161.  * On cleanup everything is released. If the list is not empty, that a
  162.  * problem of our clients
  163.  */
  164. int allocator_init(void)
  165. {
  166.     /* check how much free memory is there */
  167.     volatile void *remapped;
  168.     unsigned long trial_size = allocator_himem<<20;
  169.     unsigned long last_trial = 0;
  170.     int step = !(allocator_himem); /* no step if size known */
  171.     unsigned long i=0;
  172.     struct allocator_struct *head, *tail;
  173.     char test_string[]="0123456789abcde"; /* 16 bytes */
  174.     PDEBUGG("himem = %in",allocator_himem);
  175.     if (allocator_himem < 0) /* don't even try */
  176.         return -EINVAL;
  177.     if (!trial_size) trial_size = 1<<20; /* not specified: try one meg */
  178.     while (1) {
  179.         remapped = ioremap(__pa(high_memory), trial_size);
  180.         if (!remapped)
  181.             break;
  182.         PDEBUGG("Trying %li megs (at %p, %p)n",trial_size>>20,
  183.        (void *)__pa(high_memory), remapped);
  184.         for (i=last_trial; i<trial_size; i+=16) {
  185.             strcpy((char *)(remapped)+i, test_string);
  186.             if (strcmp((char *)remapped+i, test_string))
  187.                 break;
  188.         }
  189.         iounmap((void *)remapped);
  190.         schedule();
  191.         last_trial = trial_size;
  192.         if (i==trial_size)
  193.             trial_size <<= step; /* double, if all went well */
  194.         else
  195.             break;
  196.         if (!step) break;
  197.     }
  198.     PDEBUG("%li megs (%li k, %li b)n",i>>20,i>>10,i);
  199.     allocator_buffer_size = i;
  200.     allocator_buffer = __pa(high_memory);
  201.     if (!allocator_buffer_size) {
  202.         printk(KERN_WARNING ALL_MSG "no free high memory to usen");
  203.         return -ENOMEM;
  204.     }
  205.     /*
  206.      * to simplify things, always have two cells in the list:
  207.      * the first and the last. This avoids some conditionals and
  208.      * extra code when allocating and deallocating: we only play
  209.      * in the middle of the list
  210.      */
  211.     head = kmalloc(sizeof(struct allocator_struct),GFP_KERNEL);
  212.     if (!head)
  213.         return -ENOMEM;
  214.     tail = kmalloc(sizeof(struct allocator_struct),GFP_KERNEL);
  215.     if (!tail) {
  216.         kfree(head);
  217.         return -ENOMEM;
  218.     }
  219.     head->size = tail->size = 0;
  220.     head->address = allocator_buffer;
  221.     tail->address = allocator_buffer + allocator_buffer_size;
  222.     head->next = tail;
  223.     tail->next = NULL;
  224.     allocator_list = head;
  225.     allocator_register_symtab(); /* only used for 2.0 if this is a module */
  226.     return 0; /* ok, ready */
  227. }
  228. void allocator_cleanup(void)
  229. {
  230.     struct allocator_struct *ptr, *next;
  231.     for (ptr = allocator_list; ptr; ptr = next) {
  232.         next = ptr->next;
  233.         PDEBUG("freeing list: 0x%08lxn",ptr->address);
  234.         kfree(ptr);
  235.     }
  236.     allocator_buffer      = 0;
  237.     allocator_buffer_size = 0;
  238.     allocator_list = NULL;
  239. }
  240. module_init(allocator_init);
  241. module_exit(allocator_cleanup);