Logo Search packages:      
Sourcecode: qc-usb version File versions  Download package

qc-memory.c

/* Start of file */

/* {{{ [fold] Comments */
/*
 * qce-ga, linux V4L driver for the QuickCam Express and Dexxa QuickCam
 *
 * memory.c - contains all needed memory management functions
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */
 
/* These routines have been derived from the ov511 driver, into which they
 * were derived from the bttv driver.
 */
/* }}} */
/* {{{ [fold] Includes */
#include <linux/autoconf.h>
#include <linux/version.h>

#ifdef CONFIG_SMP
#define __SMP__
#endif

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
#if defined(CONFIG_MODVERSIONS) && !defined(MODVERSIONS)
#define MODVERSIONS
#endif
#ifdef MODVERSIONS
#include <linux/modversions.h>
#endif
#endif

#include "qc-memory.h"
#include <asm/io.h>
#include <linux/mm.h>         /* Required on Alpha, from Bob McElrath <mcelrath@draal.physics.wisc.edu> */
#include <asm/pgtable.h>      /* Required on Alpha */
#include <linux/vmalloc.h>    /* Required on Alpha */
#include <linux/pagemap.h>    /* pmd_offset requires this on SuSE supplied kernels */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
#include <linux/wrapper.h>    /* For proper mem_map_(un)reserve define, the compatibility define below might not work */
#endif
/* }}} */
/* {{{ [fold] Compatibility wrappers */
#ifndef HAVE_VMA
#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,5,3) || (defined(RED_HAT_LINUX_KERNEL) && defined(pte_offset_map))
#define HAVE_VMA 1
#else
#define HAVE_VMA 0
#endif
#endif

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)
#if HAVE_VMA
#warning "HAVE_VMA=1"
/* remap_page_range is still emulated in 2.6.10, in 2.6.11 it's completely removed */
static inline int qc_remap_page_range(struct vm_area_struct *vma, unsigned long from, unsigned long pfn, unsigned long size, pgprot_t prot) { return remap_page_range(vma, from, pfn<<PAGE_SHIFT, size, prot); }
#undef remap_pfn_range
#define remap_pfn_range(vma, from, pfn, size, prot)   qc_remap_page_range((vma), (from), (pfn), (size), (prot))
#else
#warning "HAVE_VMA=0"
static inline int qc_remap_page_range(unsigned long from, unsigned long pfn, unsigned long size, pgprot_t prot) { return remap_page_range(from, pfn<<PAGE_SHIFT, size, prot); }
#undef remap_pfn_range
#define remap_pfn_range(vma, from, pfn, size, prot)   qc_remap_page_range((from),(pfn),(size),(prot))
#endif
#endif

#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,5,3) || defined(pte_offset_map)
#define pte_offset(pmd,adr)   pte_offset_map(pmd,adr) /* Emulation for a kernel using the new rmap-vm */
#endif                                          /* Fix by Michele Balistreri <brain87@gmx.net> */

#ifndef SetPageReserved
#define SetPageReserved(p)    mem_map_reserve(p)
#endif
#ifndef ClearPageReserved
#define ClearPageReserved(p)  mem_map_unreserve(p)
#endif
/* }}} */

/* {{{ [fold] kvirt_to_pa(): obtain physical address from virtual address obtained by vmalloc() */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
/* {{{ [fold] kvirt_to_pa(), 2.4.x and 2.6.x */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,19)
static struct page *vmalloc_to_page(void * vmalloc_addr);
#endif

/* Here we want the physical address of the memory obtained by vmalloc().
 */
static inline unsigned long kvirt_to_pa(unsigned long adr)
{
      unsigned long kva, ret;

      kva = (unsigned long) page_address(vmalloc_to_page((void *)adr));
      kva |= adr & (PAGE_SIZE-1); /* restore the offset */
      ret = __pa(kva);
      return ret;
}
/* }}} */
#else
/* {{{ [fold] kvirt_to_pa() for 2.2.x */
#define page_address(x)       (x | PAGE_OFFSET) /* Damn ugly hack from kcomp.h; replaces original page_address() that made different thing! */

/* 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));
                  }
            }
      }

      return ret;
}

/* Here we want the physical address of the memory obtained by vmalloc().
 * This is used when initializing the contents of the
 * area and marking the pages as reserved.
 */
static unsigned long kvirt_to_pa(unsigned long adr)
{
      unsigned long va, kva, ret;

      va = VMALLOC_VMADDR(adr);
      kva = uvirt_to_kva(pgd_offset_k(va), va);
      ret = __pa(kva);
      return ret;
}
/* }}} */
#endif
/* }}} */
/* {{{ [fold] vmalloc_to_page(): obtain pointer to struct page from virtual address obtained by vmalloc() */
#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,4,0) && LINUX_VERSION_CODE<KERNEL_VERSION(2,4,19)
/* {{{ [fold] vmalloc_to_page() for 2.4.x (returns pointer to page) */
static struct page *vmalloc_to_page(void * vmalloc_addr)
{
      unsigned long addr = (unsigned long) vmalloc_addr;
      struct page *page = NULL;
      pmd_t *pmd;
      pte_t *pte;
      pgd_t *pgd;

      pgd = pgd_offset_k(addr);
      if (!pgd_none(*pgd)) {
            pmd = pmd_offset(pgd, addr);
            if (!pmd_none(*pmd)) {
                  pte = pte_offset(pmd, addr);
                  if (pte_present(*pte)) {
                        page = pte_page(*pte);
                  }
            }
      }
      return page;
}
/* }}} */
#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
/* {{{ [fold] vmalloc_to_page() for 2.2.x (returns page index in mem_map[]) */
static inline unsigned long vmalloc_to_page(void * vmalloc_addr)
{
      unsigned long pagenum;
      unsigned long physaddr;
      physaddr = kvirt_to_pa((unsigned long)vmalloc_addr);
      pagenum = MAP_NR(__va(physaddr));
      return pagenum;
}
/* }}} */
#endif
/* }}} */

/* {{{ [fold] qc_mm_rvmalloc(size) */
void *qc_mm_rvmalloc(unsigned long size)
{
      void *mem;
      unsigned long adr;

      size = PAGE_ALIGN(size);
      mem = vmalloc_32(size);
      if (!mem)
            return NULL;

      memset(mem, 0, size); /* Clear the ram out, no junk to the user */
      adr = (unsigned long) mem;
      while ((long)size > 0) {
            SetPageReserved(vmalloc_to_page((void *)adr));
            adr += PAGE_SIZE;
            size -= PAGE_SIZE;
      }

      return mem;
}
/* }}} */
/* {{{ [fold] qc_mm_rvfree(mem, size) */
void qc_mm_rvfree(void *mem, unsigned long size)
{
      unsigned long adr;

      if (!mem)
            return;

      adr = (unsigned long) mem;
      while ((long) size > 0) {
            ClearPageReserved(vmalloc_to_page((void *)adr));
            adr += PAGE_SIZE;
            size -= PAGE_SIZE;
      }
      vfree(mem);
}
/* }}} */
/* {{{ [fold] qc_mm_remap(vma, src, src_size, dst, dst_size) */
int qc_mm_remap(struct vm_area_struct *vma, void *src, unsigned long src_size, const void *dst, unsigned long dst_size)
{
      unsigned long start = (unsigned long)dst;
      unsigned long size  = dst_size;
      unsigned long physaddr, pos;

      pos = (unsigned long)src;
      while ((long)size > 0) {
            physaddr = kvirt_to_pa(pos);
#if 1
            if ((physaddr & ~PAGE_MASK)!=0) {
                  printk(KERN_CRIT "qc_mm_remap: physaddr=%08X not page aligned!\n", (int)physaddr);
                  return -EIO;
            }
#endif
            if (remap_pfn_range(vma, start, physaddr>>PAGE_SHIFT, PAGE_SIZE, PAGE_SHARED))
                  return -EAGAIN;
            start += PAGE_SIZE;
            pos += PAGE_SIZE;
            size -= PAGE_SIZE;
      }
      return 0;
}
/* }}} */

/* End of file */

Generated by  Doxygen 1.6.0   Back to index