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

嵌入式Linux

开发平台:

C/C++

  1. /*  -*- C -*-
  2.  * mmap.c -- memory mapping for the scullv char module
  3.  *
  4.  * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet
  5.  * Copyright (C) 2001 O'Reilly & Associates
  6.  *
  7.  * The source code in this file can be freely used, adapted,
  8.  * and redistributed in source or binary form, so long as an
  9.  * acknowledgment appears in derived source files.  The citation
  10.  * should list that the code comes from the book "Linux Device
  11.  * Drivers" by Alessandro Rubini and Jonathan Corbet, published
  12.  * by O'Reilly & Associates.   No warranty is attached;
  13.  * we cannot take responsibility for errors or fitness for use.
  14.  *
  15.  * $Id: _mmap.c.in,v 1.21 2001/07/18 22:28:18 rubini Exp $
  16.  */
  17. #ifndef __KERNEL__
  18. #  define __KERNEL__
  19. #endif
  20. #ifndef MODULE
  21. #  define MODULE
  22. #endif
  23. #include <linux/config.h>
  24. #define __NO_VERSION__
  25. #include <linux/module.h>
  26. #include <linux/mm.h>     /* everything */
  27. #include <linux/errno.h>  /* error codes */
  28. #include <asm/pgtable.h>
  29. #include "scullv.h"        /* local definitions */
  30. #ifdef LINUX_20
  31. static struct mm_struct *init_mm_ptr;
  32. #define init_mm (*init_mm_ptr) /* to avoid ifdefs later */
  33. static void retrieve_init_mm_ptr(void)
  34. {
  35.     struct task_struct *p;
  36.     for (p = current ; (p = p->next_task) != current ; )
  37.         if (p->pid == 0)
  38.             break;
  39.     init_mm_ptr = p->mm;
  40. }
  41. #endif
  42. /*
  43.  * Given a VMA, get our device pointer from it.
  44.  */
  45. static inline ScullV_Dev *scullv_vma_to_dev (struct vm_area_struct *vma)
  46. {
  47. #if defined(LINUX_24)
  48.     return (ScullV_Dev *) vma->vm_private_data;
  49. #elif defined(LINUX_22)
  50.     struct inode *inode = INODE_FROM_F(vma->vm_file); 
  51.     return scullv_devices + MINOR(inode->i_rdev);
  52. #else /* LINUX_20 */
  53.     return scullv_devices + MINOR(vma->vm_inode->i_rdev);
  54. #endif
  55. }
  56. /*
  57.  * open and close: just keep track of how many times the device is
  58.  * mapped, to avoid releasing it.
  59.  */
  60. void scullv_vma_open(struct vm_area_struct *vma)
  61. {
  62.     ScullV_Dev *dev = scullv_vma_to_dev(vma);
  63.     dev->vmas++;
  64.     MOD_INC_USE_COUNT;
  65. }
  66. void scullv_vma_close(struct vm_area_struct *vma)
  67. {
  68.     ScullV_Dev *dev = scullv_vma_to_dev(vma);
  69.     dev->vmas--;
  70.     MOD_DEC_USE_COUNT;
  71. }
  72. /*
  73.  * The nopage method: the core of the file. It retrieves the
  74.  * page required from the scullv device and returns it to the
  75.  * user. The count for the page must be incremented, because
  76.  * it is automatically decremented at page unmap.
  77.  *
  78.  * For this reason, "order" must be zero. Otherwise, only the first
  79.  * page has its count incremented, and the allocating module must
  80.  * release it as a whole block. Therefore, it isn't possible to map
  81.  * pages from a multipage block: when they are unmapped, their count
  82.  * is individually decreased, and would drop to 0.
  83.  */
  84. struct page *scullv_vma_nopage(struct vm_area_struct *vma,
  85.                                 unsigned long address, int write)
  86. {
  87.     unsigned long offset;
  88.     ScullV_Dev *ptr, *dev = scullv_vma_to_dev(vma);
  89.     struct page *page = NOPAGE_SIGBUS;
  90.     void *pageptr = NULL; /* default to "missing" */
  91.     pgd_t *pgd; pmd_t *pmd; pte_t *pte;
  92.     unsigned long lpage;
  93.     down(&dev->sem);
  94.     offset = (address - vma->vm_start) + VMA_OFFSET(vma);
  95.     if (offset >= dev->size) goto out; /* out of range */
  96.     /*
  97.      * Now retrieve the scullv device from the list,then the page.
  98.      * If the device has holes, the process receives a SIGBUS when
  99.      * accessing the hole.
  100.      */
  101.     offset >>= PAGE_SHIFT; /* offset is a number of pages */
  102.     for (ptr = dev; ptr && offset >= dev->qset;) {
  103.         ptr = ptr->next;
  104.         offset -= dev->qset;
  105.     }
  106.     if (ptr && ptr->data) pageptr = ptr->data[offset];
  107.     if (!pageptr) goto out; /* hole or end-of-file */
  108.     /*
  109.      * After scullv lookup, "page" is now the address of the page
  110.      * needed by the current process. Since it's a vmalloc address,
  111.      * first retrieve the unsigned long value to be looked up
  112.      * in page tables.
  113.      */
  114.     lpage = VMALLOC_VMADDR(pageptr);
  115. #ifdef LINUX_24
  116.     spin_lock(&init_mm.page_table_lock);
  117. #endif  
  118.     pgd = pgd_offset(&init_mm, lpage);
  119.     pmd = pmd_offset(pgd, lpage);
  120.     pte = pte_offset(pmd, lpage);
  121. #ifdef LINUX_24  
  122.     page = pte_page(*pte);
  123.     spin_unlock(&init_mm.page_table_lock);
  124. #else  
  125.     page = mem_map + MAP_NR(pte_page(*pte));  
  126. #endif  
  127.     
  128.     /* got it, now increment the count */
  129.     get_page(page);
  130. out:
  131.     up(&dev->sem);
  132.     return page;
  133. }
  134. #ifndef LINUX_24
  135. unsigned long scullv_vma_nopage_old(struct vm_area_struct *vma,
  136.                 unsigned long address, int write)
  137. {
  138.     struct page *page = scullv_vma_nopage(vma, address, write);
  139.     if (page)
  140. #ifdef LINUX_20
  141.         return (page->map_nr << PAGE_SHIFT);
  142. #else  /* 2.2 */
  143.         return (unsigned long) __va ((page - mem_map) << PAGE_SHIFT);
  144. #endif
  145.     return 0;
  146. }
  147. #define scullv_vma_nopage scullv_vma_nopage_old /* for the ops table */
  148. #endif /* not 2.4 */
  149. struct vm_operations_struct scullv_vm_ops = {
  150.     open:     scullv_vma_open,
  151.     close:  scullv_vma_close,
  152.     nopage:   scullv_vma_nopage,
  153. };
  154. #ifndef LINUX_20
  155. int scullv_mmap(struct file *filp, struct vm_area_struct *vma)
  156. {
  157.     struct inode *inode = INODE_FROM_F(filp);
  158.     if (VMA_OFFSET(vma) & (PAGE_SIZE-1))                
  159.         return -ENXIO; /* need aligned offsets */
  160.     /* don't do anything here: "nopage" will fill the holes */
  161.     vma->vm_ops = &scullv_vm_ops;
  162.     vma->vm_flags |= VM_RESERVED;
  163. #ifdef LINUX_24
  164.     vma->vm_private_data = scullv_devices + MINOR(inode->i_rdev);
  165. #else
  166.     vma->vm_file = filp;
  167. #endif
  168.     scullv_vma_open(vma);
  169.     return 0;
  170. }
  171. #else /* LINUX_20 */
  172. int scullv_mmap(struct inode *inode, struct file *filp,
  173.                 struct vm_area_struct *vma)
  174. {
  175.     if (VMA_OFFSET(vma) & (PAGE_SIZE-1))
  176.         return -ENXIO; /* need aligned offsets */
  177.     /*
  178.      * A pointer to init_mm is needed to access page tables
  179.      */
  180.     if (!init_mm_ptr)
  181.         retrieve_init_mm_ptr();
  182.     /* don't do anything here: "nopage" will fill the holes */
  183.     vma->vm_ops = &scullv_vm_ops;
  184.     vma->vm_inode = inode;
  185.     inode->i_count++;
  186.     scullv_vma_open(vma);
  187.     return 0;
  188. }
  189. #endif /* LINUX_20 */