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

嵌入式Linux

开发平台:

C/C++

  1. /*  -*- C -*-
  2.  * mmap.c -- memory mapping for the scullp 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 "scullp.h"        /* local definitions */
  30. /*
  31.  * Given a VMA, get our device pointer from it.
  32.  */
  33. static inline ScullP_Dev *scullp_vma_to_dev (struct vm_area_struct *vma)
  34. {
  35. #if defined(LINUX_24)
  36.     return (ScullP_Dev *) vma->vm_private_data;
  37. #elif defined(LINUX_22)
  38.     struct inode *inode = INODE_FROM_F(vma->vm_file); 
  39.     return scullp_devices + MINOR(inode->i_rdev);
  40. #else /* LINUX_20 */
  41.     return scullp_devices + MINOR(vma->vm_inode->i_rdev);
  42. #endif
  43. }
  44. /*
  45.  * open and close: just keep track of how many times the device is
  46.  * mapped, to avoid releasing it.
  47.  */
  48. void scullp_vma_open(struct vm_area_struct *vma)
  49. {
  50.     ScullP_Dev *dev = scullp_vma_to_dev(vma);
  51.     dev->vmas++;
  52.     MOD_INC_USE_COUNT;
  53. }
  54. void scullp_vma_close(struct vm_area_struct *vma)
  55. {
  56.     ScullP_Dev *dev = scullp_vma_to_dev(vma);
  57.     dev->vmas--;
  58.     MOD_DEC_USE_COUNT;
  59. }
  60. /*
  61.  * The nopage method: the core of the file. It retrieves the
  62.  * page required from the scullp device and returns it to the
  63.  * user. The count for the page must be incremented, because
  64.  * it is automatically decremented at page unmap.
  65.  *
  66.  * For this reason, "order" must be zero. Otherwise, only the first
  67.  * page has its count incremented, and the allocating module must
  68.  * release it as a whole block. Therefore, it isn't possible to map
  69.  * pages from a multipage block: when they are unmapped, their count
  70.  * is individually decreased, and would drop to 0.
  71.  */
  72. struct page *scullp_vma_nopage(struct vm_area_struct *vma,
  73.                                 unsigned long address, int write)
  74. {
  75.     unsigned long offset;
  76.     ScullP_Dev *ptr, *dev = scullp_vma_to_dev(vma);
  77.     struct page *page = NOPAGE_SIGBUS;
  78.     void *pageptr = NULL; /* default to "missing" */
  79.     down(&dev->sem);
  80.     offset = (address - vma->vm_start) + VMA_OFFSET(vma);
  81.     if (offset >= dev->size) goto out; /* out of range */
  82.     /*
  83.      * Now retrieve the scullp device from the list,then the page.
  84.      * If the device has holes, the process receives a SIGBUS when
  85.      * accessing the hole.
  86.      */
  87.     offset >>= PAGE_SHIFT; /* offset is a number of pages */
  88.     for (ptr = dev; ptr && offset >= dev->qset;) {
  89.         ptr = ptr->next;
  90.         offset -= dev->qset;
  91.     }
  92.     if (ptr && ptr->data) pageptr = ptr->data[offset];
  93.     if (!pageptr) goto out; /* hole or end-of-file */
  94.     page = virt_to_page(pageptr);
  95.     
  96.     /* got it, now increment the count */
  97.     get_page(page);
  98. out:
  99.     up(&dev->sem);
  100.     return page;
  101. }
  102. #ifndef LINUX_24
  103. unsigned long scullp_vma_nopage_old(struct vm_area_struct *vma,
  104.                 unsigned long address, int write)
  105. {
  106.     struct page *page = scullp_vma_nopage(vma, address, write);
  107.     if (page)
  108. #ifdef LINUX_20
  109.         return (page->map_nr << PAGE_SHIFT);
  110. #else  /* 2.2 */
  111.         return (unsigned long) __va ((page - mem_map) << PAGE_SHIFT);
  112. #endif
  113.     return 0;
  114. }
  115. #define scullp_vma_nopage scullp_vma_nopage_old /* for the ops table */
  116. #endif /* not 2.4 */
  117. struct vm_operations_struct scullp_vm_ops = {
  118.     open:     scullp_vma_open,
  119.     close:  scullp_vma_close,
  120.     nopage:   scullp_vma_nopage,
  121. };
  122. #ifndef LINUX_20
  123. int scullp_mmap(struct file *filp, struct vm_area_struct *vma)
  124. {
  125.     struct inode *inode = INODE_FROM_F(filp);
  126.     /* refuse to map if order is not 0 */
  127.     if (scullp_devices[MINOR(inode->i_rdev)].order)
  128.         return -ENODEV;
  129.     if (VMA_OFFSET(vma) & (PAGE_SIZE-1))                
  130.         return -ENXIO; /* need aligned offsets */
  131.     /* don't do anything here: "nopage" will fill the holes */
  132.     vma->vm_ops = &scullp_vm_ops;
  133.     vma->vm_flags |= VM_RESERVED;
  134. #ifdef LINUX_24
  135.     vma->vm_private_data = scullp_devices + MINOR(inode->i_rdev);
  136. #else
  137.     vma->vm_file = filp;
  138. #endif
  139.     scullp_vma_open(vma);
  140.     return 0;
  141. }
  142. #else /* LINUX_20 */
  143. int scullp_mmap(struct inode *inode, struct file *filp,
  144.                 struct vm_area_struct *vma)
  145. {
  146.     /* refuse to map if order is not 0 */
  147.     if (scullp_devices[MINOR(inode->i_rdev)].order)
  148.         return -ENODEV;
  149.     if (VMA_OFFSET(vma) & (PAGE_SIZE-1))
  150.         return -ENXIO; /* need aligned offsets */
  151.     /* don't do anything here: "nopage" will fill the holes */
  152.     vma->vm_ops = &scullp_vm_ops;
  153.     vma->vm_inode = inode;
  154.     inode->i_count++;
  155.     scullp_vma_open(vma);
  156.     return 0;
  157. }
  158. #endif /* LINUX_20 */