/****************************************************************************
 *                                                                          *
 * Copyright 1999-2005 ATI Technologies Inc., Markham, Ontario, CANADA.     *
 * All Rights Reserved.                                                     *
 *                                                                          *
 * Your use and or redistribution of this software in source and \ or       *
 * binary form, with or without modification, is subject to: (i) your       *
 * ongoing acceptance of and compliance with the terms and conditions of    *
 * the ATI Technologies Inc. software End User License Agreement; and (ii)  *
 * your inclusion of this notice in any version of this software that you   *
 * use or redistribute.  A copy of the ATI Technologies Inc. software End   *
 * User License Agreement is included with this software and is also        *
 * available by contacting ATI Technologies Inc. at http://www.ati.com      *
 *                                                                          *
 ****************************************************************************/

#ifdef __KERNEL__

#ifndef MODULE
!!! This is not currently supported,
!!! since it requires changes to linux/init/main.c.
#endif /* !MODULE */

// ============================================================
#include <linux/version.h>
#include <linux/autoconf.h>

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,71)
#define EXPORT_SYMTAB	1
#endif

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,71)
#if !defined(CONFIG_X86_PC) 
#if !defined(CONFIG_X86_64)
#if !defined(CONFIG_X86_VOYAGER)
#if !defined(CONFIG_X86_NUMAQ)
#if !defined(CONFIG_X86_SUMMIT)
#if !defined(CONFIG_X86_BIGSMP)
#if !defined(CONFIG_X86_VISWS)
#if !defined(CONFIG_X86_GENERICARCH)
#error unknown or undefined architecture configured
#endif
#endif
#endif
#endif
#endif
#endif
#endif
#endif
#endif /* LINUX_VERSION_CODE */

// ============================================================

// always defined
#define __AGP__BUILTIN__

//#define FGL_USE_SCT /* for developer use only */
// ============================================================

#include <asm/unistd.h> /* for installing the patch wrapper */
#ifdef MODVERSIONS
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,71)
#include <linux/modversions.h>
#endif
#endif
#include <linux/module.h>

#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/init.h>
#include <linux/file.h>
#include <linux/pci.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,71)
#include <linux/wrapper.h>
#endif
#include <linux/wait.h>
#include <linux/miscdevice.h>
#include <linux/smp_lock.h>
// newer SuSE kernels need this
#include <linux/highmem.h>
#include <linux/pagemap.h> // for lock_page and unlock_page

#if defined(__ia64__)
#include <linux/vmalloc.h>
#include <asm/unistd.h>
#endif

#include <linux/interrupt.h>
#include <linux/delay.h>

//#include <linux/signal.h>
#include <asm/io.h>
#include <asm/mman.h>
#include <asm/uaccess.h>
#include <asm/processor.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,71)
#include <asm/cpufeature.h>
#endif
#ifdef CONFIG_MTRR
#include <asm/mtrr.h>
#endif
#include <asm/delay.h>
#ifdef __AGP__
#include <linux/agp_backend.h>
#endif /* __AGP__ */
#include "agp_backend.h"

#if LINUX_VERSION_CODE < 0x020400
#include "compat-pre24.h"
#endif

#ifndef EXPORT_NO_SYMBOLS
#define EXPORT_NO_SYMBOLS
#endif

#ifdef __x86_64__
#include "asm/ioctl32.h"
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,2)
#include "linux/syscalls.h"
#endif
#endif

#include "firegl_public.h"

// ============================================================
// pte_offset_map will be used by the kernel (2.4.21-15EL) come with RHEL3.0. 
// This is for mapping pte with highmem enabled.
// With this call, pte_unmap has to be called for releasing the atomic lock.
// For vanilla 2.4 kernels, pte_offset is defined, pte_offset_map and 
// pte_unmap are not.
#ifndef pte_offset
    #ifdef pte_offset_map
        #define pte_offset  pte_offset_map
    #endif
#endif

#ifndef pte_unmap
#define pte_unmap
#endif

#ifndef VMALLOC_VMADDR
#define VMALLOC_VMADDR(x)  ((unsigned long)(x))
#endif
// ============================================================
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef NULL
#define NULL (void*)0
#endif

#ifdef pte_offset_atomic 
#define FGL_ATOMIC_PTE
#endif


#ifdef FGL_USE_SCT
// get direct function pointers from sys_call_table for calling
#else /* FGL_USE_SCT */
// call functions indirectly by using the syscall macros,
// entrypoints get defined by below constructs

#if !defined(__ia64__)
// the macros do use errno variable
static int errno;
#endif // __ia64__

// int mlock(const void *addr, size_t len);
_syscall2(int, mlock, const void *, addr, size_t, len )
// int munlock(const void *addr, size_t len);
_syscall2(int, munlock, const void *, addr, size_t, len )
#if !defined(__ia64__)
#if !defined(__x86_64__)
// TODO: ia64
// int modify_ldt(int func, void *ptr, unsigned long bytecount);
_syscall3( int, modify_ldt, int, func, void *, ptr, unsigned long, bytecount )
#endif
#endif
#endif /* FGL_USE_SCT */

#ifdef FGL_LINUX253P1_VMA_API
// Linux 2.5.3-pre1 and compatibles
#define FGL_VMA_API_TYPE        struct vm_area_struct *
#define FGL_VMA_API_NAME        vma
#define FGL_VMA_API_PROTO       FGL_VMA_API_TYPE FGL_VMA_API_NAME,
#define FGL_VMA_API_PASS        FGL_VMA_API_NAME,
#else /* FGL_253P1_VMA_API */
// Linux 2.4.0 and compatibles
#define FGL_VMA_API_TYPE        /* none */
#define FGL_VMA_API_NAME        /* none */
#define FGL_VMA_API_PROTO       /* none */
#define FGL_VMA_API_PASS        /* none */
#endif /* FGL_253P1_VMA_API */

#ifndef preempt_disable
#define preempt_disable()
#define preempt_enable()
#endif

#ifndef pte_offset_map 
#define pte_offset_map pte_offset
#define pte_unmap(pte)
#endif
// ============================================================
/* globals */

char* firegl = NULL;
int __ke_debuglevel = 0;
int __ke_moduleflags = 0;

/* global module vars and constants - defined trough macros */
MODULE_AUTHOR("Fire GL - ATI Research GmbH, Germany");
MODULE_DESCRIPTION("ATI Fire GL");
MODULE_PARM(firegl, "s");
#ifdef MODULE_LICENSE
MODULE_LICENSE("Proprietary. (C) 2002 - ATI Technologies, Starnberg, GERMANY");
#endif

#define FIREGL_KERNEL_DRIVER_NAME   "fglrx"   // needed for kernel message macros

/* globals constants */
const char*         __ke_UTS_RELEASE        = UTS_RELEASE;
const unsigned int  __ke_PAGE_SHIFT         = PAGE_SHIFT;
const unsigned int  __ke_PAGE_SIZE          = PAGE_SIZE;
const unsigned long __ke_PAGE_MASK          = PAGE_MASK;
const unsigned long __ke_LINUX_VERSION_CODE = LINUX_VERSION_CODE;

// create global constants and hint symbols (i.e. for objdump checking)
#ifdef MODVERSIONS
const unsigned long __ke_MODVERSIONS_State = 1;
const char BUILD_KERNEL_HAS_MODVERSIONS_SET;
#else
const unsigned long __ke_MODVERSIONS_State = 0;
const char BUILD_KERNEL_HAS_MODVERSIONS_CLEARED;
#endif

#ifdef __SMP__
const unsigned long __ke_SMP_State = 1;
const char BUILD_KERNEL_HAS_SMP_SET;
#else
const unsigned long __ke_SMP_State = 0;
const char BUILD_KERNEL_HAS_SMP_CLEARED;
#endif

/* globals vars that are in fact constants */
unsigned long __ke_HZ;

// ============================================================
/* global structures */
int ip_firegl_open(struct inode* inode, struct file* filp)
{ return firegl_open(inode, filp); }
int ip_firegl_release(struct inode* inode, struct file* filp)
{ return firegl_release(inode, filp); }
int ip_firegl_ioctl(struct inode* inode, struct file* filp, unsigned int cmd, unsigned long arg)
{ return firegl_ioctl(inode, filp, cmd, arg); }
int ip_firegl_mmap(struct file* filp, struct vm_area_struct* vma)
{ return firegl_mmap(filp, vma); }

static struct file_operations firegl_fops =
{
#ifdef THIS_MODULE
    owner:   THIS_MODULE,
#endif
    open:    ip_firegl_open,
    release: ip_firegl_release,
    ioctl:   ip_firegl_ioctl,
    mmap:    ip_firegl_mmap,
};

typedef struct {
    __ke_device_t       pubdev;     // MUST BE FIRST MEMBER
    dev_t               device;     // Device number for mknod

    /* Locking */
    spinlock_t          spinlock[__KE_MAX_SPINLOCKS];      /* For inuse, open_count, buf_use   */
    struct semaphore    struct_sem[__KE_MAX_SEMAPHORES];   /* For linked list manipulations    */
    sigset_t            sigmask;
} device_t;

static device_t     firegl_public_device;   // The Fire GL public device

/*****************************************************************************/
// standard XFree86 DRM proc support

#define DRM(x) FGLDRM_##x

#include "drm.h"

// mem_info() is missing in drm_proc.h. But, it is a DRM design problem anyway!
// The first registered DRM device could never report memory statisticts of another
// DRM device, cause the DRM mem_info routine uses local variables. So, let's use a dummy.
static int DRM(mem_info)(char *buf __attribute__((unused)), char **start __attribute__((unused)), off_t offset __attribute__((unused)), int len __attribute__((unused)), int *eof, void *data __attribute__((unused)))
{
    *eof = 1;
    return 0;
}
#if 0
#ifdef vmalloc_to_page
#undef vmalloc_to_page
#endif
#define vmalloc_to_page     drm_inline_vmalloc_to_page
#endif
#include "drm_proc.h"

#ifndef DRM_MAJOR
#define DRM_MAJOR       226
#endif // DRM_MAJOR

static __ke_proc_list_t *drm_proclist = NULL;

/*****************************************************************************/
// Fire GL DRM stub support (compatible with standard DRM stub)

#define FIREGL_STUB_MAXCARDS    16

typedef struct firegl_stub_list_tag {
	const char             *name;
	struct file_operations *fops;
	struct proc_dir_entry  *dev_root;
    __ke_proc_list_t       *proclist;
} firegl_stub_list_t;
static firegl_stub_list_t firegl_stub_list[FIREGL_STUB_MAXCARDS];

static struct proc_dir_entry *firegl_stub_root;
static int firegl_minor;

typedef struct firegl_drm_stub_info_tag {
    int (*info_register)(const char *name, struct file_operations *fops, device_t *dev);
	int (*info_unregister)(int minor);
    unsigned long signature; // to check for compatible Fire GL DRM device
} firegl_drm_stub_info_t;
static firegl_drm_stub_info_t firegl_stub_info;

#if LINUX_VERSION_CODE < 0x020400
struct firegl_drm_stub_info_t *firegl_stub_pointer = NULL;
#define inter_module_put(x)
#define inter_module_unregister(x)
#define inter_module_get(x)             firegl_stub_pointer
#define inter_module_register(x,y,z)    do { firegl_stub_pointer = z; } while (0)
/* This is a kludge for backward compatibility that is only useful in DRM(stub_open) */
#define fops_put(fops)      MOD_DEC_USE_COUNT
#define fops_get(fops)      (fops); MOD_INC_USE_COUNT
#endif // LINUX_VERSION_CODE < 0x020400

#define DRM_MODULE_GET          (firegl_drm_stub_info_t *)inter_module_get("drm")
#define DRM_MODULE_PUT          inter_module_put("drm")

#define DRM_AGP_MODULE_GET      (drm_agp_t *)inter_module_get("drm_agp")
#define DRM_AGP_MODULE_PUT      inter_module_put("drm_agp")

unsigned long ATI_API_CALL __ke_cpu_to_le32(unsigned long _u)
{
    return cpu_to_le32(_u);
}

unsigned long ATI_API_CALL __ke_cpu_to_le64(unsigned long _u)
{
    return cpu_to_le64(_u);
}

static struct proc_dir_entry *firegl_proc_init( device_t *dev,
                                                int minor,
				                                struct proc_dir_entry *root,
				                                struct proc_dir_entry **dev_root,
                                                __ke_proc_list_t *proc_list ) // proc_list must be terminated!
{
	struct proc_dir_entry *ent;
	char    name[64];
    __ke_proc_list_t *list = proc_list;
    __KE_DEBUG("minor %d, proc_list 0x%08lx\n", minor, (unsigned long)proc_list);

	if (!minor)
        root = create_proc_entry("dri", S_IFDIR, NULL);

	if (!root) {
		__KE_ERROR("Cannot create /proc/dri\n");
		return NULL;
	}

	sprintf(name, "%d", minor);
	*dev_root = create_proc_entry(name, S_IFDIR, root);
	if (!*dev_root) {
		__KE_ERROR("Cannot create /proc/%s\n", name);
		return NULL;
	}

    while (list->f)
    {
		ent = create_proc_entry(list->name, S_IFREG|S_IRUGO, *dev_root);
		if (!ent)
        {
			__KE_ERROR("Cannot create /proc/dri/%s/%s\n", name, list->name);
            while (proc_list != list)
            {
				remove_proc_entry(proc_list->name, *dev_root);
                proc_list++;
            }
			remove_proc_entry(name, root);
			if (!minor)
                remove_proc_entry("dri", NULL);
			return NULL;
		}

		ent->read_proc = (read_proc_t*)list->f;
        ent->data      = (dev->pubdev.signature == FGL_DEVICE_SIGNATURE) ? 
                            ((void*)dev->pubdev.privdev) : (dev);

        list++;
	}

	return root;
}

static int firegl_proc_cleanup( int minor,
                                struct proc_dir_entry *root,
		                        struct proc_dir_entry *dev_root,
                                __ke_proc_list_t *proc_list )
{
	char name[64];
    __KE_DEBUG("minor %d\n", minor);

	if (!root || !dev_root)
    {
        __KE_ERROR("no root\n");
        return 0;
    }

    while (proc_list->f)
    {
		remove_proc_entry(proc_list->name, dev_root);
        proc_list++;
    }
	sprintf(name, "%d", minor);
	remove_proc_entry(name, root);
	if (!minor)
        remove_proc_entry("dri", NULL);

	return 0;
}

static int firegl_stub_open(struct inode *inode, struct file *filp)
{
#ifndef MINOR
	int                    minor = minor(inode->i_rdev);
#else
	int                    minor = MINOR(inode->i_rdev);
#endif
	int                    err   = -ENODEV;
	struct file_operations *old_fops;
    __KE_DEBUG("\n");

	if (!firegl_stub_list[minor].fops)
        return -ENODEV;
	old_fops   = filp->f_op;
	filp->f_op = fops_get(firegl_stub_list[minor].fops);
	if (filp->f_op->open && (err = filp->f_op->open(inode, filp))) {
		fops_put(filp->f_op);
		filp->f_op = fops_get(old_fops);
	}
	fops_put(old_fops);
	return err;
}

static struct file_operations firegl_stub_fops = {
#if LINUX_VERSION_CODE >= 0x020400
	owner:   THIS_MODULE,
#endif
	open:	 firegl_stub_open
};

static int firegl_stub_getminor(const char *name, struct file_operations *fops, device_t *dev)
{
	int i;
    __KE_DEBUG("name=\"%s\"\n", name);
	for (i = 0; i < FIREGL_STUB_MAXCARDS; i++)
    {
		if (!firegl_stub_list[i].fops)
        {
			firegl_stub_list[i].name = name;
			firegl_stub_list[i].fops = fops;
            firegl_stub_list[i].proclist = (dev->pubdev.signature == FGL_DEVICE_SIGNATURE) ? dev->pubdev.proclist : drm_proclist;
            firegl_stub_root = firegl_proc_init(dev, i, firegl_stub_root, &firegl_stub_list[i].dev_root, firegl_stub_list[i].proclist);
            __KE_DEBUG("minor=%d\n", i);
			return i;
		}
	}
    __KE_DEBUG("no more free minor\n");
	return -1;
}

static int firegl_stub_putminor(int minor)
{
    __KE_DEBUG("minor=%d\n", minor);
	if (minor < 0 || minor >= FIREGL_STUB_MAXCARDS)
        return -1;

	firegl_proc_cleanup(minor, firegl_stub_root, firegl_stub_list[minor].dev_root, firegl_stub_list[minor].proclist);
	firegl_stub_list[minor].name = NULL;
	firegl_stub_list[minor].fops = NULL;
    firegl_stub_list[minor].proclist = NULL;

	if (minor) {
		DRM_MODULE_PUT;
	} else {
		inter_module_unregister("drm");
		unregister_chrdev(DRM_MAJOR, "drm");
	}
	return 0;
}

static int __init firegl_stub_register(const char *name, struct file_operations *fops, device_t *dev)
{
    int err;
    __KE_DEBUG("name=\"%s\"\n", name);

    // try to register a drm char device for the firegl module
    err = register_chrdev(DRM_MAJOR, "drm", &firegl_stub_fops);
    if(err == 0)
    {
        __KE_DEBUG("register_chrdev() succeeded\n");

        // register our own module handler will handle the DRM device
		firegl_stub_info.info_register   = firegl_stub_getminor;
		firegl_stub_info.info_unregister = firegl_stub_putminor;
		inter_module_register("drm", THIS_MODULE, &firegl_stub_info);
    }
    else
    if(err == -EINVAL)
    {
        __KE_ERROR("register_chrdev() failed with -EINVAL\n");
        return -1;
    }
    else
	if(err == -EBUSY)
    {
    	firegl_drm_stub_info_t *drm_stub = NULL;

        // the registering of the module's device has failed 
        // because there was already some other drm module loaded.
        __KE_DEBUG("register_chrdev() failed with -EBUSY\n");

        // we now do try to attach to the existing module stub
        // for getting some more detailed info about the servicing code behind.
		drm_stub = DRM_MODULE_GET;
        if (!drm_stub)
        {
            __KE_ERROR("Unable to the open some already present DRM kernel module!\n");
            return -1;
        }

        // if the prior loaded module is not a compatible FireGL device
        if (drm_stub->signature != FGL_DEVICE_SIGNATURE)
        {
            // then we cant use it and we have to quit our init attempt here
            __KE_ERROR("Fire GL kernel module has to be loaded prior to any other DRM kernel module!\n");
            DRM_MODULE_PUT;
            return -1;
        }

        // the detected module is a firegl compatible module
        // so we can simply attach to and use the existing infrastructure.
        firegl_stub_info.info_register   = drm_stub->info_register;
        firegl_stub_info.info_unregister = drm_stub->info_unregister;
    }
    else
    {
        __KE_ERROR("register_chrdev() failed with %i\n", err);
        return -1;
    }

    return firegl_stub_info.info_register(name, fops, dev);
}

static int __exit firegl_stub_unregister(int minor)
{
	__KE_DEBUG("%d\n", minor);
	if (firegl_stub_info.info_unregister)
		return firegl_stub_info.info_unregister(minor);
	return -1;
}


/*****************************************************************************/
/* init_module is called when insmod is used to load the module */
static int __init firegl_init_module(void)
{
    device_t* dev = &firegl_public_device;
    unsigned int i;
    int retcode;

	EXPORT_NO_SYMBOLS;

    // init global vars that are in fact constants
    __ke_HZ = HZ;

    // init DRM proc list
    drm_proclist = kmalloc((DRM_PROC_ENTRIES + 1) * sizeof(__ke_proc_list_t), GFP_KERNEL);
    if ( drm_proclist == NULL )
        return -ENOMEM;

    for ( i=0; i<DRM_PROC_ENTRIES; i++ )
    {
        drm_proclist[i].name = DRM(proc_list)[i].name;
        drm_proclist[i].f = (__ke_read_proc_t)DRM(proc_list)[i].f;
    }
    drm_proclist[i].f = NULL; // terminate list

    memset(&firegl_stub_list, 0, sizeof(firegl_stub_list));
    memset(&firegl_stub_info, 0, sizeof(firegl_stub_info));
    firegl_stub_info.signature = FGL_DEVICE_SIGNATURE;

    memset(dev, 0, sizeof(*dev));
    dev->pubdev.signature = FGL_DEVICE_SIGNATURE;

    for (i = 0; i < __KE_MAX_SPINLOCKS; i++)
        dev->spinlock[i] = SPIN_LOCK_UNLOCKED;

    for (i=0; i < __KE_MAX_SEMAPHORES; i++)
        sema_init(&dev->struct_sem[i], 1);

    dev->pubdev.psigmask = (__ke_sigset_t*)&dev->sigmask;

	if ( (retcode = firegl_init(&dev->pubdev)) )
    {
        kfree(drm_proclist);
        return retcode;
    }

	// get the minor number
	firegl_minor = firegl_stub_register(dev->pubdev.name, &firegl_fops, dev);
	if (firegl_minor < 0)
    {
        kfree(drm_proclist);
		return -EPERM;
    }

	dev->device = MKDEV(DRM_MAJOR, firegl_minor);

    __KE_INFO("module loaded - %s %d.%d.%d [%s] on minor %d\n",
            dev->pubdev.name,
	        dev->pubdev.major_version,
	        dev->pubdev.minor_version,
	        dev->pubdev.patchlevel,
	        dev->pubdev.date,
		    firegl_minor);

    return 0; // OK!
}

/* cleanup_module is called when rmmod is used to unload the module */
static void __exit firegl_cleanup_module(void)
{
    device_t* dev = &firegl_public_device;
    __KE_DEBUG("module cleanup started\n");

	if ( firegl_stub_unregister(firegl_minor) ) {
		__KE_ERROR("Cannot unload module\n");
	}

    firegl_cleanup(&dev->pubdev);

    if (drm_proclist)
        kfree(drm_proclist);

	__KE_INFO("module unloaded - %s %d.%d.%d [%s] on minor %d\n",
            dev->pubdev.name,
	        dev->pubdev.major_version,
	        dev->pubdev.minor_version,
	        dev->pubdev.patchlevel,
	        dev->pubdev.date,
		    firegl_minor);
}

module_init( firegl_init_module );
module_exit( firegl_cleanup_module );

/*****************************************************************************/

// wait queues
#ifdef DECLARE_WAITQUEUE
// kernel 2.3.1 and higher defines complex declaration & initializer macros
#else
// kernel 2.3.0 and earlier does not provide any of these macros
// -> define a simple type alias and macro set for easier coding
typedef struct wait_queue   wait_queue_t;
typedef wait_queue_t*       wait_queue_head_t;
#endif

__ke_wait_queue_head_t* ATI_API_CALL __ke_alloc_wait_queue_head_struct(void)
{
    __ke_wait_queue_head_t* queue_head;
    queue_head = kmalloc(sizeof(wait_queue_head_t), GFP_ATOMIC);

    if (queue_head)
    {
#ifdef DECLARE_WAITQUEUE
        init_waitqueue_head((wait_queue_head_t*)(void *)queue_head);
#else
        *queue_head = NULL;
#endif
    }

    return queue_head;
}

void ATI_API_CALL __ke_free_wait_queue_head_struct(__ke_wait_queue_head_t* queue_head)
{
    if (queue_head)
        kfree(queue_head);
}

__ke_wait_queue_t* ATI_API_CALL __ke_alloc_wait_queue_struct(void)
{
    __ke_wait_queue_t* queue;
    queue = kmalloc(sizeof(wait_queue_t), GFP_KERNEL);

    return queue;
}

void ATI_API_CALL __ke_free_wait_queue_struct(__ke_wait_queue_t* queue)
{
    if (queue)
        kfree(queue);
}

void ATI_API_CALL __ke_wake_up_interruptible(__ke_wait_queue_head_t* queue_head)
{
    wake_up_interruptible((wait_queue_head_t*)(void *)queue_head);
}

void ATI_API_CALL __ke_add_wait_queue(__ke_wait_queue_head_t* queue_head, __ke_wait_queue_t* entry)
{
    // initialisation (delayed)
#ifdef __WAITQUEUE_INITIALIZER
    wait_queue_t template =
        __WAITQUEUE_INITIALIZER(((wait_queue_t*)(void *)entry), current);

    *((wait_queue_t*)(void *)entry) = template;
#else
    ((wait_queue_t*)(void *)entry)->task = current;
#if LINUX_VERSION_CODE >= 0x020400
    ((wait_queue_t*)(void *)entry)->flags = 0x0;
    ((wait_queue_t*)(void *)entry)->task_list.next = NULL;
    ((wait_queue_t*)(void *)entry)->task_list.prev = NULL;
    ((wait_queue_t*)(void *)entry)->func = NULL;
#if WAITQUEUE_DEBUG
    ((wait_queue_t*)(void *)entry)->__magic = &(((wait_queue_t*)(void *)entry)->__magic);
    ((wait_queue_t*)(void *)entry)->__waker = 0;
#endif /* WAITQUEUE_DEBUG */
#else /* LINUX_VERSION_CODE < 0x020400 */
    ((wait_queue_t*)entry)->next = NULL;
#endif /* LINUX_VERSION_CODE < 0x020400 */
#endif /* __WAITQUEUE_INITIALIZER */

    // addition
    add_wait_queue((wait_queue_head_t*)(void *)queue_head, (wait_queue_t*)(void *)entry);
}

void ATI_API_CALL __ke_remove_wait_queue(__ke_wait_queue_head_t* queue_head, __ke_wait_queue_t* entry)
{
//    current->state = TASK_RUNNING;
    remove_wait_queue((wait_queue_head_t*)(void *)queue_head, 
									(wait_queue_t*)(void *)entry);
}

// sheduler
void ATI_API_CALL __ke_schedule(void)
{
	schedule();
}
/** /brief This routine will let the current processor yield for other threads.
 */
void ATI_API_CALL __ke_yield(void)
{
    yield();
}


int ATI_API_CALL __ke_signal_pending(void)
{
    return signal_pending(current);
}

void ATI_API_CALL __ke_set_current_state_task_interruptible(void)
{
    current->state = TASK_INTERRUPTIBLE;
}

void ATI_API_CALL __ke_set_current_state_task_running(void)
{
    current->state = TASK_RUNNING;
}

void ATI_API_CALL __ke_configure_sigmask(__ke_sigset_t *pSigMask)
{
    sigemptyset((sigset_t*)(void *)pSigMask);
    sigaddset((sigset_t*)(void *)pSigMask, SIGSTOP);
    sigaddset((sigset_t*)(void *)pSigMask, SIGTSTP);
    sigaddset((sigset_t*)(void *)pSigMask, SIGTTIN);
    sigaddset((sigset_t*)(void *)pSigMask, SIGTTOU);
}

void ATI_API_CALL __ke_block_all_signals(int (*ATI_API_CALL notifier)(void *priv), void *pPriv, __ke_sigset_t *pSigMask)
{
#if LINUX_VERSION_CODE >= 0x020400
    block_all_signals(notifier,pPriv,(sigset_t*)(void *)pSigMask);
#endif
}

void ATI_API_CALL __ke_unblock_all_signals(void)
{
#if LINUX_VERSION_CODE >= 0x020400
    unblock_all_signals();
#endif
}

#if defined(__i386__) 
#ifndef __HAVE_ARCH_CMPXCHG
static inline 
unsigned long __fgl_cmpxchg(volatile void *ptr, unsigned long old,            
                        unsigned long new, int size)                      
{                                                                                       
    unsigned long prev;                                                             
    switch (size) {                                                                 
    case 1:                                                                         
        __asm__ __volatile__(LOCK_PREFIX "cmpxchgb %b1,%2"
                             : "=a"(prev)
                             : "q"(new), "m"(*__xg(ptr)), "0"(old)
                             : "memory");
        return prev;
    case 2:
        __asm__ __volatile__(LOCK_PREFIX "cmpxchgw %w1,%2"
                             : "=a"(prev)
                             : "q"(new), "m"(*__xg(ptr)), "0"(old)
                             : "memory");
        return prev;
    case 4:
        __asm__ __volatile__(LOCK_PREFIX "cmpxchgl %1,%2"
                             : "=a"(prev)
                             : "q"(new), "m"(*__xg(ptr)), "0"(old)
                             : "memory");
        return prev;
    }
    return old;
}
#endif /* cmpxchg */
#elif defined(__alpha__)
todo !!!
#endif

#if !defined(__ia64__)
unsigned long ATI_API_CALL __ke__cmpxchg(volatile void *ptr, unsigned long old,
         unsigned long new, int size)
{
#ifndef __HAVE_ARCH_CMPXCHG
    return __fgl_cmpxchg(ptr,old,new,size);
#else
    return __cmpxchg(ptr,old,new,size);
#endif
}
#endif

/*****************************************************************************/

__ke_dev_t ATI_API_CALL __ke_getdevice(__ke_device_t *dev)
{
    return ((device_t*)dev)->device;
}

const char* ATI_API_CALL __ke_module_parm(void)
{
    return firegl;
}

/*****************************************************************************/

int ATI_API_CALL __ke_inode_rdev_minor(struct inode* inode)
{
#ifndef MINOR
    return minor(inode->i_rdev);
#else
    return MINOR(inode->i_rdev);
#endif
}

/*****************************************************************************/

void* ATI_API_CALL __ke_get_file_priv(struct file* filp)
{
    return filp->private_data;
}

void ATI_API_CALL __ke_set_file_priv(struct file* filp, void* private_data)
{
    filp->private_data = private_data;
}

int ATI_API_CALL __ke_file_excl_open(struct file* filp)
{
    return (filp->f_flags & O_EXCL) != 0;
}

int ATI_API_CALL __ke_file_rw_open(struct file* filp)
{
    return (filp->f_flags & 3) != 0;
}

unsigned int ATI_API_CALL __ke_file_counter(struct file *filp)
{
#if LINUX_VERSION_CODE >= 0x020400
    return filp->f_count.counter;
#else		  
    return filp->f_count;
#endif
}

/*****************************************************************************/

int ATI_API_CALL __ke_getpid(void)
{
    return current->pid;
}

int ATI_API_CALL __ke_geteuid(void)
{
    return current->euid;
}

/*****************************************************************************/

unsigned long ATI_API_CALL __ke_jiffies(void)
{
    return jiffies;
}

void ATI_API_CALL __ke_udelay(unsigned long usecs) // delay in usec
{
    unsigned long start;
    unsigned long stop;
    unsigned long period;
    unsigned long wait_period;
    struct timespec tval;

#ifdef NDELAY_LIMIT
    // kernel provides delays with nano(=n) second accuracy
#define UDELAY_LIMIT    (NDELAY_LIMIT/1000) /* supposed to be 10 msec */
#else
    // kernel provides delays with micro(=u) second accuracy
#define UDELAY_LIMIT    (10000)             /* 10 msec */
#endif

    if (usecs > UDELAY_LIMIT)
    {
        start = jiffies;
        tval.tv_sec = usecs / 1000000;
        tval.tv_nsec = (usecs - tval.tv_sec * 1000000) * 1000;
        wait_period = timespec_to_jiffies(&tval);
        do {
            stop = jiffies;

            if (stop < start) // jiffies overflow
                period = ((unsigned long)-1 - start) + stop + 1;
            else
                period = stop - start;

        } while (period < wait_period);
    }
    else
        udelay(usecs);  /* delay value might get checked once again */
}

void ATI_API_CALL __ke_mdelay(unsigned long msecs) // delay in msec
{
        mdelay(msecs);
}

/*****************************************************************************/
// TODO: These here get obsolete in future, use the ia64 code below
// Johannes
unsigned long ATI_API_CALL __ke_virt_to_bus(void* address)
{
    return virt_to_bus(address);
}

unsigned long ATI_API_CALL __ke_virt_to_phys(void* address)
{
    return virt_to_phys(address);
}

void* ATI_API_CALL __ke_high_memory(void)
{
    return high_memory;
}

int ATI_API_CALL __ke_pci_enable_device(__ke_pci_dev_t* dev)
{
    return (pci_enable_device( (struct pci_dev*)(void *)dev ));
}

#if defined(__x86_64__) || defined(__ia64__)
void* ATI_API_CALL __ke_pci_alloc_consistent(__ke_pci_dev_t* dev, int size, void *dma_handle)
{
	return (pci_alloc_consistent( (struct pci_dev*)(void *)dev, size, dma_handle)); 
}

void ATI_API_CALL __ke_pci_free_consistent(__ke_pci_dev_t* dev, int size, unsigned long cpu_addr,
						 unsigned int dma_handle)
{
	pci_free_consistent( (struct pci_dev*)(void *)dev, size, (void *)cpu_addr, 
		(unsigned long)dma_handle);
}
#endif // __ia64__

/*****************************************************************************/

int ATI_API_CALL __ke_error_code(enum __ke_error_num errcode)
{
    switch (errcode)
    {
        case __KE_EBUSY:
            return EBUSY;
        case __KE_EINVAL:
            return EINVAL;
        case __KE_EACCES:
            return EACCES;
        case __KE_EFAULT:
            return EFAULT;
        case __KE_EIO:
            return EIO;
        case __KE_EBADSLT:
            return EBADSLT;
        case __KE_ENOMEM:
            return ENOMEM;
        case __KE_EPERM:
            return EPERM;
        case __KE_ENODEV:
            return ENODEV;
        case __KE_EINTR:
            return EINTR;
        case __KE_ERESTARTSYS:
            return ERESTARTSYS;
        case __KE_ELIBBAD:
            return ELIBBAD;
        default:
            return EFAULT;
    }
}

/*****************************************************************************/
#ifdef __x86_64__
int ATI_API_CALL firegl_call_ioctl(unsigned int fd, unsigned int cmd, void *data)
{
  mm_segment_t seg;
  int err;

  seg = get_fs();
  set_fs(KERNEL_DS);
  err = sys_ioctl(fd, cmd, (unsigned long)data);
  set_fs(seg);

  return err;
}

int ATI_API_CALL __ke_sys_ioctl(unsigned int fd, unsigned int cmd, void *data)
{
  return sys_ioctl(fd, cmd, (unsigned long)data);
}

int ATI_API_CALL firegl_get_user_ptr(u32 *src, void **dst)
{
  void *temp=NULL;
  int err = get_user(temp, src); 
  *dst = temp;
  return err;
}

int ATI_API_CALL firegl_get_user_u16(u16 *src, u16 *dst)
{
  u16 temp;
  int err = get_user(temp, src);
  *dst = temp;
  return err;
}

int ATI_API_CALL firegl_get_user_u32(u32 *src, u32 *dst)
{
  u32 temp;
  int err = get_user(temp, src);
  *dst = temp;
  return err;
}

int ATI_API_CALL firegl_get_user_u64(u32 *src, u64 *dst)
{
  u64 temp;
  int err = get_user(temp, src);
  *dst = temp;
  return err;
}

int ATI_API_CALL firegl_put_user_ptr(void *src, u32 *dst)
{
  void *temp = src;
  return put_user(temp, dst);
}

int ATI_API_CALL firegl_put_user_u16(u16 src, u16 *dst)
{
  u16 temp = src;
  return put_user(temp, dst);
}

int ATI_API_CALL firegl_put_user_u32(u32 src, u32 *dst)
{
  u32 temp = src;
  return put_user(temp, dst);
}

int ATI_API_CALL firegl_put_user_u64(u64 src, u32 *dst)
{
  u64 temp = src;
  return put_user(temp, dst);
}
#endif /* __x86_64__ */


/*****************************************************************************/

void ATI_API_CALL __ke_mod_inc_use_count(void)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
    __module_get(THIS_MODULE);
#else
    MOD_INC_USE_COUNT;
#endif
}

void ATI_API_CALL __ke_mod_dec_use_count(void)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
    module_put(THIS_MODULE);
#else
    MOD_DEC_USE_COUNT;
#endif
}

/*****************************************************************************/

void ATI_API_CALL __ke_down_struct_sem(__ke_device_t *dev, int index)
{
    down(&(((device_t*)dev)->struct_sem[index]));
}

void ATI_API_CALL __ke_up_struct_sem(__ke_device_t *dev, int index)
{
    up(&(((device_t*)dev)->struct_sem[index]));
}

void ATI_API_CALL __ke_sema_init(struct semaphore* sem, int value)
{
    sema_init(sem, value);
}

__ke_size_t ATI_API_CALL __ke_sema_size(void)
{
    return sizeof(struct semaphore);
}

void ATI_API_CALL __ke_down(struct semaphore* sem)
{
    down(sem);
}

void ATI_API_CALL __ke_up(struct semaphore* sem)
{
    up(sem);
}

/*****************************************************************************/

void ATI_API_CALL __ke_atomic_inc(void* v)
{
    atomic_inc((atomic_t*)v);
}

void ATI_API_CALL __ke_atomic_dec(void* v)
{
    atomic_dec((atomic_t*)v);
}

void ATI_API_CALL __ke_atomic_add(int val, void* v)
{
    atomic_add(val, (atomic_t*)v);
}

void ATI_API_CALL __ke_atomic_sub(int val, void* v)
{
    atomic_sub(val, (atomic_t*)v);
}

int ATI_API_CALL __ke_atomic_read(void* v)
{
    return atomic_read((atomic_t*)v);
}

void ATI_API_CALL __ke_atomic_set(void* v, int val)
{
    atomic_set((atomic_t*)v, val);
}

/*****************************************************************************/

void ATI_API_CALL __ke_spin_lock(__ke_device_t *dev, int ndx)
{
    spin_lock(&(((device_t*)dev)->spinlock[ndx]));
}

void ATI_API_CALL __ke_spin_unlock(__ke_device_t *dev __attribute__((unused)), int ndx __attribute__((unused)))
{
    spin_unlock(&(((device_t*)dev)->spinlock[ndx]));
}

void ATI_API_CALL __ke_lock_kernel(void)
{
    lock_kernel();
}

void ATI_API_CALL __ke_unlock_kernel(void)
{
    unlock_kernel();
}

/*****************************************************************************/

#ifdef FGL_USE_SCT
extern unsigned long sys_call_table[];
#endif

typedef int (*PFNMLOCK)(unsigned long start, __ke_size_t len);
typedef int (*PFNMUNLOCK)(unsigned long start, __ke_size_t len);


int ATI_API_CALL __ke_sys_mlock(unsigned long start, __ke_size_t len)
{
#ifdef FGL_USE_SCT
    PFNMLOCK sys_mlock = (PFNMLOCK)sys_call_table[__NR_mlock];
    if (!sys_mlock) {
        __KE_ERROR("sys_call_table[__NR_mlock] == 0\n");
        return -1;
    }

    return (*sys_mlock)(start, len);
#else
    return mlock((void*)start, len);
#endif
}

int ATI_API_CALL __ke_sys_munlock(unsigned long start, __ke_size_t len)
{
#ifdef FGL_USE_SCT
    PFNMUNLOCK sys_munlock = (PFNMUNLOCK)sys_call_table[__NR_munlock];
    if (!sys_munlock) {
        __KE_ERROR("sys_call_table[__NR_munlock] == 0\n");
        return -1;
    }

    return (*sys_munlock)(start, len);
#else
    return munlock((void*)start, len);
#endif
}


typedef int (*PFNMODIFYLDT)(int func, void *ptr, unsigned long bytecount);

int ATI_API_CALL __ke_sys_modify_ldt(int func, void *ptr, unsigned long bytecount)
{
#ifdef FGL_USE_SCT
    PFNMODIFYLDT sys_modify_ldt = (PFNMODIFYLDT)sys_call_table[__NR_modify_ldt];
    if (!sys_modify_ldt) {
        __KE_ERROR("sys_call_table[__NR_modify_ldt] == 0\n");
        return -1;
    }

    return (*sys_modify_ldt)(func, ptr, bytecount);
#else
#if !defined(__ia64__) && !defined(__x86_64__)
    return modify_ldt(func, ptr, bytecount);
#else
	// TODO: how should this be down on ia64????
	return 0;
#endif
#endif
}

/*****************************************************************************/

#ifdef __KE_NO_VSPRINTF

#if LINUX_VERSION_CODE >= 0x020400
void ATI_API_CALL __ke_printk(const char* fmt, ...)
{
	char buffer[256];
    va_list marker;

    va_start(marker, fmt);
    vsprintf(buffer, fmt, marker);
    va_end(marker);

	printk(buffer);
}
#else /* LINUX_VERSION_CODE < 0x020400 */
#ifdef __x86_64__
#error NOT FOR X86_64
#endif //  __x86_64__
#define sym2str(_sym) #_sym

#define JmpFunction(name, _kernel_name)         \
    __asm__ (                                   \
        ".globl " name "                    \n" \
        ".type  " name ",@function          \n" \
        name ":                             \n" \
        "   jmp " sym2str(_kernel_name) "   \n" \
    )

JmpFunction("__ke_printk", printk);

#endif /* LINUX_VERSION_CODE < 0x020400 */

#else

void ATI_API_CALL __ke_print_info(const char* fmt, ...)
{
    char msg[256] = KERN_INFO;:
    va_list marker;

    va_start(marker, fmt);
    vsprintf(msg + strlen(msg), fmt, marker);
    va_end(marker);
}

void ATI_API_CALL __ke_print_error(const char* fmt, ...)
{
    char msg[256] = KERN_ERR;
    va_list marker;

    va_start(marker, fmt);
    vsprintf(msg + strlen(msg), fmt, marker);
    va_end(marker);
}

void ATI_API_CALL __ke_print_debug(const char* fmt, ...)
{
    char msg[256] = KERN_DEBUG;
    va_list marker;

    va_start(marker, fmt);
    vsprintf(msg + strlen(msg), fmt, marker);
    va_end(marker);
}

#endif

/*****************************************************************************/

int ATI_API_CALL __ke_capable(enum __ke_cap cap)
{
    switch (cap)
    {
        case __KE_CAP_SYS_ADMIN:
            cap = CAP_SYS_ADMIN;
			break;
        case __KE_CAP_IPC_LOCK:
            cap = CAP_IPC_LOCK;
			break;
        default:
            return 0;
    }
    return capable(cap);
}

void ATI_API_CALL __ke_cap_effective_raise(enum __ke_cap cap)
{
    switch (cap)
    {
        case __KE_CAP_SYS_ADMIN:
            cap = CAP_SYS_ADMIN;
			break;
        case __KE_CAP_IPC_LOCK:
            cap = CAP_IPC_LOCK;
			break;
        default:
            return;
    }
    cap_raise(current->cap_effective, cap);
}

__ke_u32 ATI_API_CALL __ke_get_cap_effective()
{
    return cap_t(current->cap_effective);
}

void ATI_API_CALL __ke_set_cap_effective(__ke_u32 cap)
{
    cap_t(current->cap_effective) = cap;
}

unsigned long ATI_API_CALL __ke_ram_available(void)
{
	struct sysinfo si;

    si_meminfo(&si);
#if LINUX_VERSION_CODE < 0x020317
    /* Changed to page count in 2.3.23 */
	return si.totalram >> PAGE_SHIFT;
#else
	return si.totalram;
#endif
}

int ATI_API_CALL __ke_copy_from_user(void* to, const void* from, __ke_size_t size)
{
    return copy_from_user(to, from, size);
}

int ATI_API_CALL __ke_copy_to_user(void* to, const void* from, __ke_size_t size)
{
    return copy_to_user(to, from, size);
}

int ATI_API_CALL __ke_verify_area(int type, const void * addr, unsigned long size)
{
    return verify_area(type, addr, size);
}

int ATI_API_CALL __ke_get_pci_device_info(__ke_pci_dev_t* dev, __ke_pci_device_info_t *pinfo)
{
    if ( dev )
    {
        pinfo->vendor = ((struct pci_dev*)(void *)dev)->vendor;
        pinfo->device = ((struct pci_dev*)(void *)dev)->device;
        pinfo->subsystem_vendor = ((struct pci_dev*)(void *)dev)->subsystem_vendor;
        pinfo->subsystem_device = ((struct pci_dev*)(void *)dev)->subsystem_device;
    }
    return -EINVAL;
}

int ATI_API_CALL __ke_check_pci(int busnum, int devnum, int funcnum, __ke_u16* vendor, __ke_u16* device, unsigned int* irq)
{
    struct pci_dev* pci_dev;

    pci_dev = pci_find_slot(busnum, PCI_DEVFN(devnum, funcnum));
    if (!pci_dev)
        return 0;

    if (vendor)
        *vendor = pci_dev->vendor;

    if (device)
        *device = pci_dev->device;

    if (irq)
        *irq = pci_dev->irq;

    return 1;
}

int ATI_API_CALL __ke_pci_get_irq(__ke_pci_dev_t *dev, unsigned int* irq)
{
    if (!dev)
        return 0;
    if (!irq)
        return 0;

    *irq = ((struct pci_dev*)dev)->irq;
    return 1;
}

__ke_pci_dev_t* ATI_API_CALL __ke_pci_find_device (unsigned int vendor, unsigned int dev, __ke_pci_dev_t* from)
{
	return (__ke_pci_dev_t*)pci_find_device( vendor, dev, (struct pci_dev *)(void *)from );
}

void* ATI_API_CALL __ke_malloc(__ke_size_t size)
{
    return kmalloc(size, GFP_KERNEL);
}

void ATI_API_CALL __ke_free_s(void* p, __ke_size_t size)
{
    kfree(p);
}

void* ATI_API_CALL __ke_vmalloc(__ke_size_t size)
{
    return vmalloc(size);
}
void* ATI_API_CALL __ke_vmalloc_32(__ke_size_t size)
{
    return vmalloc_32(size);
}

void ATI_API_CALL __ke_vfree(void* p)
{
    return vfree(p);
}

void* ATI_API_CALL __ke_get_free_page(void)
{
    return (void*)__get_free_page(GFP_KERNEL);
}

void* ATI_API_CALL __ke_get_free_pages(int order)
{
    return (void*)__get_free_pages(GFP_KERNEL, order);
}

void ATI_API_CALL __ke_free_page(void* pt)
{
    free_page((unsigned long)pt);
}

void ATI_API_CALL __ke_free_pages(void* pt, int order)
{
    free_pages((unsigned long)pt, order);
}

void ATI_API_CALL __ke_get_page(void* pt)
{
    get_page(virt_to_page((unsigned long)pt));
}

void ATI_API_CALL __ke_put_page(void* pt)
{
    put_page(virt_to_page((unsigned long)pt));
}

void ATI_API_CALL __ke_unlock_page(void *virt)
{
#if LINUX_VERSION_CODE < 0x020400
    clear_bit(PG_locked,
        &mem_map[MAP_NR(virt)].flags);
#else
    unlock_page(virt_to_page((unsigned long)virt));
#endif
}

int ATI_API_CALL __ke_PageCompound(void *virt)
{
#ifdef PageCompound
    return PageCompound(virt_to_page((unsigned long)virt));
#else
    return 0;
#endif
}

void ATI_API_CALL __ke_mem_map_reserve(void* pt)
{
#if LINUX_VERSION_CODE < 0x020400
    mem_map_reserve(MAP_NR((unsigned long)pt));
#else
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,71)
    mem_map_reserve(virt_to_page((unsigned long)pt));
#else
    SetPageReserved(virt_to_page((unsigned long)pt));
#endif
#endif
}

void ATI_API_CALL __ke_mem_map_unreserve(void* pt)
{
#if LINUX_VERSION_CODE < 0x020400
    mem_map_unreserve(MAP_NR((unsigned long)pt));
#else
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,71)
    mem_map_unreserve(virt_to_page((unsigned long)pt));
#else
    ClearPageReserved(virt_to_page((unsigned long)pt));
#endif
#endif
}

void ATI_API_CALL __ke_virt_reserve(void* virt)
{
#if LINUX_VERSION_CODE < 0x020400
    set_bit(PG_reserved,
        &mem_map[MAP_NR(virt)].flags);
#else
    set_bit(PG_reserved,
        &virt_to_page((unsigned long)virt)->flags);
#endif
}

void ATI_API_CALL __ke_virt_unreserve(void* virt)
{
#if LINUX_VERSION_CODE < 0x020400
    clear_bit(PG_reserved,
        &mem_map[MAP_NR(virt)].flags);
#else
    clear_bit(PG_reserved,
        &virt_to_page((unsigned long)virt)->flags);
#endif
}

void ATI_API_CALL __ke_lock_page(void* virt)
{
#if LINUX_VERSION_CODE < 0x020400
    set_bit(PG_locked,
        &mem_map[MAP_NR(virt)].flags);
#else
    lock_page(virt_to_page((unsigned long)virt));
#endif
}

#ifdef __ia64__
void* ATI_API_CALL __ke_get_vmptr( struct _agp_memory* memory )
{
	return memory->vmptr;
}
#endif
                              
void* ATI_API_CALL __ke_ioremap(unsigned long offset, unsigned long size)
{
    return ioremap(offset, size);
}

void* ATI_API_CALL __ke_ioremap_nocache(unsigned long offset, unsigned long size)
{
    return ioremap_nocache(offset, size);
}

void ATI_API_CALL __ke_iounmap(void* pt)
{
    iounmap(pt);
}

int ATI_API_CALL __ke_verify_read_access(void* addr, __ke_size_t size)
{
    return access_ok(VERIFY_READ, addr, size) ? 0 : -EFAULT;
}

int ATI_API_CALL __ke_verify_write_access(void* addr, __ke_size_t size)
{
    return access_ok(VERIFY_WRITE, addr, size) ? 0 : -EFAULT;
}

struct mm_struct* ATI_API_CALL __ke_init_mm(void)
{
    return &init_mm;
}

struct mm_struct* ATI_API_CALL __ke_current_mm(void)
{
    return current->mm;
}

unsigned long ATI_API_CALL __ke_get_vm_phys_addr(struct mm_struct* mm, unsigned long virtual_addr)
{
    unsigned long pte_linear;
    pgd_t* pgd_p;
    pmd_t* pmd_p;
    pte_t* pte_p;
    pte_t  pte;

    pte_linear = VMALLOC_VMADDR(virtual_addr);  // convert to pte linear address (x86 => nop)
    pgd_p = pgd_offset(mm, pte_linear);
    pmd_p = pmd_offset(pgd_p, pte_linear);
#ifndef FGL_ATOMIC_PTE
#if LINUX_VERSION_CODE > 0x020500
    pte_p = pte_offset_kernel(pmd_p, pte_linear);
    pte = *pte_p;       // duplicate object contents
#else
    pte_p = pte_offset(pmd_p, pte_linear);
    pte = *pte_p;       // duplicate object contents
    pte_unmap(pte_p);
#endif
#else
    pte_p = pte_offset_atomic(pmd_p, pte_linear);
    pte = *pte_p;       // duplicate object contents
    pte_kunmap(pte_p);  // release atomic lock on pte object
#endif

#if LINUX_VERSION_CODE >= 0x020400
#if defined(__x86_64__) || defined(__ia64__)
    return pte.pte & PAGE_MASK;
#else
    return pte.pte_low & PAGE_MASK;
#endif
#else /* LINUX_VERSION_CODE < 0x020400 */
    return virt_to_phys((void*)pte_page(pte_p));
#endif /* LINUX_VERSION_CODE < 0x020400 */
}

unsigned long* ATI_API_CALL __ke_get_vm_phys_addr_list(struct mm_struct* mm, unsigned long virtual_addr, unsigned long pages)
{
    unsigned long   *phys_table, *pt;
    unsigned long   n, va;

    /* caller is responsible for the respective kfree call */
    phys_table = kmalloc(pages*sizeof(unsigned long),GFP_KERNEL);
    if (!phys_table)
        return NULL;

    pt = phys_table;
    va = virtual_addr;
    for(n=0; n<pages; n++)
    {
        *pt = __ke_get_vm_phys_addr(mm,va);
        pt++;
        va += PAGE_SIZE;
    }

    return phys_table;
}

void* ATI_API_CALL __ke_memset(void* s, int c, __ke_size_t count)
{
    return memset(s, c, count);
}

void* ATI_API_CALL __ke_memcpy(void* d, const void* s, __ke_size_t count)
{
    return memcpy(d, s, count);
}

__ke_size_t ATI_API_CALL __ke_strlen(const char *s)
{
    return strlen(s);
}

char* ATI_API_CALL __ke_strcpy(char* d, const char* s)
{
    return strcpy(d, s);
}

char* ATI_API_CALL __ke_strncpy(char* d, const char* s, __ke_size_t count)
{
    return strncpy(d, s, count);
}

int ATI_API_CALL __ke_strcmp(const char* string1, const char* string2)
{
    return strcmp(string1, string2);
}

int ATI_API_CALL __ke_strncmp(const char* string1, const char* string2, __ke_size_t count)
{
    return strncmp(string1, string2, count);
}

int ATI_API_CALL __ke_sprintf(char* buf, const char* fmt, ...)
{
    va_list marker;

    va_start(marker, fmt);
    vsprintf(buf, fmt, marker);
    va_end(marker);

    return strlen(buf);
}

/*****************************************************************************/

void ATI_API_CALL __ke_set_bit(int nr, volatile void * addr)
{
    set_bit(nr, addr);
}

void ATI_API_CALL __ke_clear_bit(int nr, volatile void * addr)
{
    clear_bit(nr, addr);
}

/*****************************************************************************/

#ifdef __SMP__
static atomic_t cpus_waiting;

static void deferred_flush(void* contextp)
{
#if defined(__i386__) || defined(__x86_64__)
	asm volatile ("wbinvd":::"memory");
#elif defined(__alpha__) || defined(__ia64__) || defined(__sparc__)
	mb();
#else
#error "Please define flush_cache."
#endif
	atomic_dec(&cpus_waiting);
	while (atomic_read(&cpus_waiting) > 0)
		barrier();
}
#endif /* __SMP__ */

int ATI_API_CALL __ke_flush_cache(void)
{
#ifdef __SMP__
#if LINUX_VERSION_CODE < 0x020501
	atomic_set(&cpus_waiting, smp_num_cpus - 1);
#endif

    /* write back invalidate all other CPUs (exported by kernel) */
	if (smp_call_function(deferred_flush, NULL, 1, 0) != 0)
		panic("timed out waiting for the other CPUs!\n");

    /* invalidate this CPU */
#if defined(__i386__) || defined(__x86_64__)
	asm volatile ("wbinvd":::"memory");
#elif defined(__alpha__) || defined(__ia64__) || defined(__sparc__)
	mb();
#else
#error "Please define flush_cache for your architecture."
#endif

	while (atomic_read(&cpus_waiting) > 0)
		barrier();
#else /* !__SMP__ */
#if defined(__i386__) || defined(__x86_64__)
	asm volatile ("wbinvd":::"memory");
#elif defined(__alpha__) || defined(__ia64__) || defined(__sparc__)
	mb();
#else
#error "Please define flush_cache for your architecture."
#endif
#endif /* !__SMP__ */
    return 0;
}

/*****************************************************************************/

int ATI_API_CALL __ke_config_mtrr(void)
{
#ifdef CONFIG_MTRR
    return 1;
#else /* !CONFIG_MTRR */
    return 0;
#endif /* !CONFIG_MTRR */
}

int ATI_API_CALL __ke_mtrr_add_wc(unsigned long base, unsigned long size)
{
#ifdef CONFIG_MTRR
    return mtrr_add(base, size, MTRR_TYPE_WRCOMB, 1);
#else /* !CONFIG_MTRR */
    return -EPERM;
#endif /* !CONFIG_MTRR */
}

int ATI_API_CALL __ke_mtrr_del(int reg, unsigned long base, unsigned long size)
{
#ifdef CONFIG_MTRR
    return mtrr_del(reg, base, size);
#else /* !CONFIG_MTRR */
    return -EPERM;
#endif /* !CONFIG_MTRR */
}

#ifdef __x86_64__
int ATI_API_CALL __ke_config_iommu(void)
{
#ifdef CONFIG_GART_IOMMU
    return 1;
#else /* !CONFIG_GART_IOMMU */
    return 0;
#endif /* !CONFIG_GART_IOMMU */
}

int ATI_API_CALL __ke_no_iommu(void)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,1)
#ifdef CONFIG_GART_IOMMU
    return no_iommu;
#else /* !CONFIG_GART_IOMMU */
    return 0;
#endif /* !CONFIG_GART_IOMMU */
#else
    return 0;
#endif
}
#endif 

/*****************************************************************************/

int ATI_API_CALL __ke_pci_read_config_byte(__ke_pci_dev_t* dev, __ke_u8 where, __ke_u8 *val)
{
    return pci_read_config_byte((struct pci_dev*)(void *)dev, where, val);
}

int ATI_API_CALL __ke_pci_read_config_word(__ke_pci_dev_t* dev, __ke_u8 where, __ke_u16 *val)
{
    return pci_read_config_word((struct pci_dev*)(void *)dev, where, val);
}

int ATI_API_CALL __ke_pci_read_config_dword(__ke_pci_dev_t* dev, __ke_u8 where, __ke_u32 *val)
{
    return pci_read_config_dword((struct pci_dev*)(void *)dev, where, val);
}

int ATI_API_CALL __ke_pci_write_config_byte(__ke_pci_dev_t* dev, __ke_u8 where, __ke_u8 val)
{
    return pci_write_config_byte((struct pci_dev*)(void *)dev, where, val);
}

int ATI_API_CALL __ke_pci_write_config_word(__ke_pci_dev_t* dev, __ke_u8 where, __ke_u16 val)
{
    return pci_write_config_word((struct pci_dev*)(void *)dev, where, val);
}

int ATI_API_CALL __ke_pci_write_config_dword(__ke_pci_dev_t* dev, __ke_u8 where, __ke_u32 val)
{
    return pci_write_config_dword((struct pci_dev*)(void *)dev, where, val);
}

__ke_dma_addr_t ATI_API_CALL __ke_pci_map_single (__ke_pci_dev_t *pdev, void *buffer, __ke_size_t size, int direction)
{
    return pci_map_single((struct pci_dev*)(void*)pdev, buffer, size, direction);
}

void ATI_API_CALL __ke_pci_unmap_single (__ke_pci_dev_t *pdev, __ke_dma_addr_t bus_addr, __ke_size_t size, int direction)
{
    pci_unmap_single((struct pci_dev*)(void*)pdev, bus_addr, size, direction);
}

__ke_dma_addr_t ATI_API_CALL __ke_pci_map_page (__ke_pci_dev_t *pdev, unsigned long buffer, unsigned long offset, __ke_size_t size, int direction)
{
    return pci_map_page((struct pci_dev*)(void*)pdev, virt_to_page(buffer), offset, size, direction);
}

void ATI_API_CALL __ke_pci_unmap_page (__ke_pci_dev_t *pdev, __ke_dma_addr_t bus_addr, __ke_size_t size, int direction)
{
    pci_unmap_page((struct pci_dev*)(void*)pdev, bus_addr, size, direction);
}


/*****************************************************************************/

void ATI_API_CALL __ke_outb(unsigned char value, unsigned short port)
{
    outb(value, port);
}

void ATI_API_CALL __ke_outw(unsigned short value, unsigned short port)
{
    outw(value, port);
}

void ATI_API_CALL __ke_outl(unsigned int value, unsigned short port)
{
    outl(value, port);
}

char ATI_API_CALL __ke_inb(unsigned short port)
{
    return inb(port);
}

short ATI_API_CALL __ke_inw(unsigned short port)
{
    return inw(port);
}

int ATI_API_CALL __ke_inl(unsigned short port)
{
    return inl(port);
}

int ATI_API_CALL __ke_log2(unsigned long x)
{
   return ffz(~(x));
}

/*****************************************************************************/
// Interrupt support

void ATI_API_CALL __ke_enable_irq(int irq)
{
    enable_irq( irq );
}

void ATI_API_CALL __ke_disable_irq(int irq)
{
    disable_irq( irq );
}

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,71)
int ATI_API_CALL __ke_request_irq(unsigned int irq, 
    void (*ATI_API_CALL handler)(int, void *, void *),
    const char *dev_name, void *dev_id)
{
    return request_irq(irq,
        (void(*)(int, void *, struct pt_regs *))handler,
        SA_SHIRQ, dev_name, dev_id);
}

void ATI_API_CALL __ke_free_irq(unsigned int irq, void *dev_id)
{
    free_irq(irq, dev_id);
}
#else
static void (*irq_handler_func)(int, void*, void*); /* function pointer variable */

static irqreturn_t ke_irq_handler_wrap(int irq, void *arg1, struct pt_regs *regs)
{
    irq_handler_func(irq, arg1, regs);
    return IRQ_HANDLED;
}

int ATI_API_CALL __ke_request_irq(unsigned int irq, 
    void (*ATI_API_CALL handler)(int, void *, void *),
    const char *dev_name, void *dev_id)
{
    irq_handler_func = handler;
    return request_irq(irq,
        ke_irq_handler_wrap,
        SA_SHIRQ, dev_name, dev_id);
}

void ATI_API_CALL __ke_free_irq(unsigned int irq, void *dev_id)
{
    free_irq(irq, dev_id);
    irq_handler_func = NULL;
}
#endif

#ifdef __x86_64__
int ATI_API_CALL __ke_register_ioctl32_conversion(unsigned int cmd, int (*handler)(unsigned int, unsigned int, unsigned long, struct file*))
{
    return register_ioctl32_conversion(cmd, handler);
}

void ATI_API_CALL __ke_unregister_ioctl32_conversion(unsigned int cmd)
{
    unregister_ioctl32_conversion(cmd);
}
#endif

/*****************************************************************************/

#ifndef NOPAGE_SIGBUS
#define NOPAGE_SIGBUS 0
#endif /* !NOPAGE_SIGBUS */

#if LINUX_VERSION_CODE > 0x020500
typedef struct page mem_map_t;
typedef mem_map_t *vm_nopage_ret_t;
#elif LINUX_VERSION_CODE >= 0x020400
typedef mem_map_t* vm_nopage_ret_t;
#else
/* LINUX_VERSION_CODE < 0x020400 */
typedef unsigned long vm_nopage_ret_t;

static mem_map_t* virt_to_page(unsigned long kaddr)
{
    return mem_map + MAP_NR(kaddr);
}
#endif /* LINUX_VERSION_CODE < 0x020400 */

static __inline__ vm_nopage_ret_t do_vm_nopage(struct vm_area_struct* vma,
                                                     unsigned long address)
{
    return 0;   /* Disallow mremap */
}

#ifdef __AGP__BUILTIN__
#ifdef __ia64__
static __inline__ vm_nopage_ret_t do_vm_cant_nopage(struct vm_area_struct* vma,
                                                          unsigned long address)
{
	void *dev;
	unsigned long offset = address - vma->vm_start;
	unsigned long baddr = VM_OFFSET(vma) + offset;
	unsigned long mem;
	struct page *page;

    // TODO
    dev = firegl_get_dev_from_vm(vma);
//	if (firegl_cant_use_agp(dev))
	{
		mem = firegl_get_virt_agp_mem( dev, baddr, vma);
		if (mem)
		{
			page = virt_to_page((unsigned long)__va(mem));
			get_page(page);
			return page;
		}
	}
	return NOPAGE_SIGBUS;		/* Disallow mremap */
}

#endif /* __ia64__ */
#endif /* __AGP__BUILTIN__ */


static __inline__ vm_nopage_ret_t do_vm_shm_nopage(struct vm_area_struct* vma,
                                                   unsigned long address)
{
    pgd_t* pgd_p;
    pmd_t* pmd_p;
    pte_t* pte_p;
    pte_t  pte;
    unsigned long vma_offset;
#ifndef __x86_64__
    unsigned long linear;
#endif
    unsigned long pte_linear;
#if LINUX_VERSION_CODE < 0x020400
    unsigned long kaddr;
#endif /*  LINUX_VERSION_CODE < 0x020400 */
    mem_map_t* pMmPage;

    /*
        vm_start           => start of vm-area,  regular address
        vm_end             => end of vm-area,    regular address
        vm_offset/vm_pgoff => start of area,     linear address
        address            => requested address, regular address

        Check range
        Seems the surrounding framework already does that test -
        skip it here, anyone does.
     */
#if 0
    if (address < vma->vm_start)
        return NOPAGE_SIGBUS; /* Address is out of range */
#endif
    /*
        Note: vm_end is not member of range but this border
        hmm, might be used when growing the VMA, not sure - keep it as it is.
     */

    __KE_DEBUG3("start=0x%08lx, "
            "end=0x%08lx, "
            "offset=0x%08lx\n",
            vma->vm_start,
            vma->vm_end,
            (unsigned long)__ke_vm_offset(vma));

    if (address > vma->vm_end)
        return NOPAGE_SIGBUS; /* address is out of range */

    /*  Calculate offset into VMA */
    vma_offset = address - vma->vm_start;

#if defined(__x86_64__) 
    /*
        The old calculation below doesn't work with 32on64.
        Use internal routine for the proper address. 
        This should also apply to 32-bit address, need to test out.  
    */
    pte_linear = firegl_get_addr_from_vm(vma) + vma_offset;
#else

    /*
        TRICK:
        We dont have to keep a correspondence list between VMA offsets and
        physiucal address. But since that memory is mapped into kernel space
        we just have to climb down the kernel page table structures to get
        the matching kernel (mem_map/page) address for the given offset.
        This works because memory is created in kernel memory and therefore
        it is just present even if its not yet mapped to current VMA.

        Sample: 32 MB adapter RAM -> 8192 pages a 4kB -> 32 kB Data on x86

        Translate VMA offset into a linear address (=virtual address),
        at kernel scope, by simply adding the base (=VM_OFFSET) address of
        the VMA in kernel space.
     */
    linear = __ke_vm_offset(vma) + vma_offset;

    /*
        For historical reasons:
        pte used some offset on x86 kernels previous to 2.1.1

        Convert to pte linear address (x86 => nop)
     */
    pte_linear = VMALLOC_VMADDR(linear);
#endif

    /*
        Locate responsible kernel PTE for this linear address
        in paging system of the kernel VM
        (or is it the init process? - not sure yet)
     */
#if defined(__x86_64__) || defined(__ia64__)
	/* I am not sure whether that is the way to do it, but lets give it a try
		its the way it is done in ioremap.c in the kernel
	*/
	pgd_p = pgd_offset_k(pte_linear);
#else
    pgd_p = pgd_offset(&init_mm, pte_linear); /* locate page directory entry */
#endif
    if (!pgd_present(*pgd_p))
    {
        __KE_ERROR("FATAL ERROR: User queue buffer not present! (pgd)\n");
        return NOPAGE_SIGBUS;   /* Something bad happened; generate SIGBUS */
        /* alternatively we could generate a NOPAGE_OOM "out of memory" */
    }
    /*  locate medium level page table (x86 => nop) */
    pmd_p = pmd_offset(pgd_p, pte_linear);
    if (!pmd_present(*pmd_p))
    {
        __KE_ERROR("FATAL ERROR: User queue buffer not present! (pmd)\n");
        return NOPAGE_SIGBUS;   /* Something bad happened; generate SIGBUS */
        /* alternatively we could generate a NOPAGE_OOM "out of memory" */
    }
    /*  locate page table entry itself */
#ifndef FGL_ATOMIC_PTE
#if LINUX_VERSION_CODE > 0x020500
    pte_p = pte_offset_kernel(pmd_p, pte_linear);
    pte = *pte_p;       // duplicate contents
#else
    pte_p = pte_offset(pmd_p, pte_linear);
    pte = *pte_p;       // duplicate contents
    pte_unmap(pte_p);
#endif
#else
    pte_p = pte_offset_atomic(pmd_p, pte_linear);
    pte = *pte_p;       // duplicate contents
    pte_kunmap(pte_p);  // release atomic lock
#endif
    if (!pte_present(pte))
    {
        __KE_ERROR("FATAL ERROR: User queue buffer not present! (pte)\n");
        return NOPAGE_SIGBUS;   /* Something bad happened; generate SIGBUS */
        /* alternatively we could generate a NOPAGE_OOM "out of memory" */
    }

    /*
        Resolve the kernel (mem_map/page) address for the VMA-address
        we got queried about.
    */
#if LINUX_VERSION_CODE >= 0x020400
    /*
        Pretty straight in new kernel. The pte is represented by the requested
        pointer to the page table entry.
     */
    pMmPage = pte_page(pte);
#else /* LINUX_VERSION_CODE < 0x020400 */
    kaddr = pte_page(pte);
    pMmPage = virt_to_page(kaddr);
#endif /* LINUX_VERSION_CODE < 0x020400 */

    get_page(pMmPage);  /* inc usage count of page */

#if LINUX_VERSION_CODE >= 0x020400
  //  __KE_DEBUG3("vm-address 0x%08lx => kernel-page-address 0x%p\n",
    //    address, page_address(pMmPage));
    return pMmPage;
#else /* LINUX_VERSION_CODE < 0x020400 */
    __KE_DEBUG3("vm-address 0x%08lx => kernel-page-address 0x%p\n",
        address, (void*)kaddr);
    return kaddr;
#endif /* LINUX_VERSION_CODE < 0x020400 */
}

/*

    This routine is intended to remap addresses of a OpenGL context
      (which is one ore more pages in size)

*/
static __inline__ vm_nopage_ret_t do_vm_dma_nopage(struct vm_area_struct* vma,
                                                         unsigned long address)
{
    unsigned long kaddr;
    mem_map_t* pMmPage;

    if (address > vma->vm_end)
        return 0; /* Disallow mremap */

    /*
        Have we ever got an acces from user land into context structure?

        Resolve the kernel (mem_map/page) address for the VMA-address
        we got queried about.
    */
#if defined(__x86_64__)
    kaddr = firegl_get_addr_from_vm(vma) + (address - vma->vm_start);
#else
    kaddr = __ke_vm_offset(vma) + (address - vma->vm_start);
#endif
    pMmPage = virt_to_page(kaddr);

#if 0
    // WARNING WARNINIG WARNNING WARNNING WARNNING WARNNING WARNNING WARNNING
    // Don't increment page usage count, cause ctx pages are allocated
    // with drm_alloc_pages, which marks all pages as reserved. Reserved
    // pages' usage count is not decremented by the kernel during unmap!!!
    get_page(pMmPage); /* inc usage count of page */
#endif

#if LINUX_VERSION_CODE >= 0x020400
    __KE_DEBUG3("vm-address 0x%08lx => kernel-page-address 0x%p\n",
        address, page_address(pMmPage));
    return pMmPage;
#else /*  LINUX_VERSION_CODE < 0x020400 */
    __KE_DEBUG3("vm-address 0x%08lx => kernel-page-address 0x%p\n",
        address, (void*)kaddr);
    return kaddr;
#endif /* LINUX_VERSION_CODE < 0x020400 */
}

/** 
 **
 **  This routine is intented to locate the page table through the 
 **  pagelist table created earlier in dev-> pcie
 **/
static __inline__ vm_nopage_ret_t do_vm_pcie_nopage(struct vm_area_struct* vma,
                                                         unsigned long address)
{

    unsigned long vma_offset;
    unsigned long i; 
    mem_map_t* pMmPage;
    struct firegl_pcie_mem* pciemem;
    unsigned long* pagelist;
    
    drm_device_t *dev = (drm_device_t *)firegl_get_dev_from_vm(vma);
    if (dev == NULL)
    {
        __KE_ERROR("dev is NULL\n");
        return NOPAGE_SIGBUS;
    }

    if (address > vma->vm_end)
    {
        __KE_ERROR("address out of range\n");
        return NOPAGE_SIGBUS; /* address is out of range */
    }
    pciemem = firegl_get_pciemem_from_addr ( vma, address);
    if (pciemem == NULL)
    {
        __KE_ERROR("No pciemem found! \n");
        return NOPAGE_SIGBUS;
    }    
    pagelist = firegl_get_pagelist_from_vm(vma);

    if (pagelist == NULL) 
    {
        __KE_ERROR("No pagelist! \n");
        return NOPAGE_SIGBUS;
    }
     
    /** Find offset in  vma */
    vma_offset = address - vma->vm_start;
    /** Which entry in the pagelist */
    i = vma_offset >> PAGE_SHIFT;
    pMmPage = virt_to_page(firegl_get_pcie_pageaddr_from_vm(vma,pciemem, i));

    get_page(pMmPage);

    if (page_address(pMmPage) == 0x0)
    {
        __KE_ERROR("Invalid page address\n");
        return NOPAGE_SIGBUS;
    }
    return pMmPage;
}

#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)

static vm_nopage_ret_t vm_nopage(struct vm_area_struct* vma,
                                 unsigned long address,
                                 int *type)
{
    if (type) *type = VM_FAULT_MINOR;
        return do_vm_nopage(vma, address);
}

#ifdef __AGP__BUILTIN__
#ifdef __ia64__


static vm_nopage_ret_t vm_cant_nopage(struct vm_area_struct* vma,
                                      unsigned long address,
                                      int *type)
{
    if (type) *type = VM_FAULT_MINOR;
        return do_cant_nopage(vma, address);

}
#endif /* __ia64__ */
#endif /* __AGP__BUILTIN__ */

/*

    This function is called when a page of a mmap()'ed area is not currently
    visible in the specified VMA.
    Return value is the associated physical address for the requested page.
    (If not implemented, then the kernel default routine would allocate a new,
     zeroed page for servicing us)

    Possible errors: SIGBUS, OutOfMem

    This routine is intended to remap addresses of SHM SAREA
    (which is one or more pages in size)

 */
static vm_nopage_ret_t vm_shm_nopage(struct vm_area_struct* vma,
                                     unsigned long address,
                                     int *type)
{
    if (type) *type = VM_FAULT_MINOR;
        return do_vm_shm_nopage(vma, address);
}

/*

    This routine is intended to remap addresses of a OpenGL context
      (which is one ore more pages in size)

*/
static vm_nopage_ret_t vm_dma_nopage(struct vm_area_struct* vma,
                                     unsigned long address,
                                     int *type)
{
    if (type) *type = VM_FAULT_MINOR;
        return do_vm_dma_nopage(vma, address);
}

static vm_nopage_ret_t vm_pcie_nopage(struct vm_area_struct* vma,
                                     unsigned long address,
                                     int write_access)
{  
       return do_vm_pcie_nopage(vma, address);
}

#else   /* LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,0) */

static vm_nopage_ret_t vm_nopage(struct vm_area_struct* vma,
                                 unsigned long address,
                                 int write_access)
{
    return do_vm_nopage(vma, address);
}

#ifdef __AGP__BUILTIN__
#ifdef __ia64__


static vm_nopage_ret_t vm_cant_nopage(struct vm_area_struct* vma,
                                 unsigned long address,
                                 int write_access)
{
    return do_vm_cant_nopage(vma, address);
}
#endif /* __ia64__ */
#endif /* __AGP__BUILTIN__ */

/*

    This function is called when a page of a mmap()'ed area is not currently
    visible in the specified VMA.
    Return value is the associated physical address for the requested page.
    (If not implemented, then the kernel default routine would allocate a new,
     zeroed page for servicing us)

    Possible errors: SIGBUS, OutOfMem

    This routine is intended to remap addresses of SHM SAREA
    (which is one or more pages in size)

 */
static vm_nopage_ret_t vm_shm_nopage(struct vm_area_struct* vma,
                                     unsigned long address,
                                     int write_access)
{
    return do_vm_shm_nopage(vma, address);
}

/*

    This routine is intended to remap addresses of a OpenGL context
      (which is one ore more pages in size)

*/
static vm_nopage_ret_t vm_dma_nopage(struct vm_area_struct* vma,
                                     unsigned long address,
                                     int write_access)
{
     return do_vm_dma_nopage(vma, address);
}

static vm_nopage_ret_t vm_pcie_nopage(struct vm_area_struct* vma,
                                     unsigned long address,
                                     int write_access)
{
        return do_vm_pcie_nopage(vma, address);
}

#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) */

void* ATI_API_CALL __ke_vma_file_priv(struct vm_area_struct* vma)
{
    return vma->vm_file->private_data;
}

unsigned long ATI_API_CALL __ke_vm_start(struct vm_area_struct* vma)
{
    return vma->vm_start;
}

unsigned long ATI_API_CALL __ke_vm_end(struct vm_area_struct* vma)
{
    return vma->vm_end;
}

unsigned long ATI_API_CALL __ke_vm_offset(struct vm_area_struct* vma)
{
#if LINUX_VERSION_CODE < 0x020319
    return vma->vm_offset;
#else /* LINUX_VERSION_CODE >= 0x020319 */
    return vma->vm_pgoff << PAGE_SHIFT;
#endif/* LINUX_VERSION_CODE >= 0x020319 */
}

char* ATI_API_CALL __ke_vm_flags_str(struct vm_area_struct* vma, char* buf)
{
   *(buf + 0) = vma->vm_flags & VM_READ	    ? 'r' : '-';
   *(buf + 1) = vma->vm_flags & VM_WRITE	? 'w' : '-';
   *(buf + 2) = vma->vm_flags & VM_EXEC	    ? 'x' : '-';
   *(buf + 3) = vma->vm_flags & VM_MAYSHARE ? 's' : 'p';
   *(buf + 4) = vma->vm_flags & VM_LOCKED   ? 'l' : '-';
   *(buf + 5) = vma->vm_flags & VM_IO	    ? 'i' : '-';
   *(buf + 6) = 0;
   return buf;
}

char* ATI_API_CALL __ke_vm_page_prot_str(struct vm_area_struct* vma, char* buf)
{
    int i = 0;

#ifdef __i386__
	unsigned int pgprot;

    pgprot = pgprot_val(vma->vm_page_prot);
    *(buf + i++) = pgprot & _PAGE_PRESENT  ? 'p' : '-';
    *(buf + i++) = pgprot & _PAGE_RW       ? 'w' : 'r';
    *(buf + i++) = pgprot & _PAGE_USER     ? 'u' : 's';
    *(buf + i++) = pgprot & _PAGE_PWT      ? 't' : 'b';
    *(buf + i++) = pgprot & _PAGE_PCD      ? 'u' : 'c';
    *(buf + i++) = pgprot & _PAGE_ACCESSED ? 'a' : '-';
    *(buf + i++) = pgprot & _PAGE_DIRTY    ? 'd' : '-';
#if LINUX_VERSION_CODE >= 0x020400
    *(buf + i++) = pgprot & _PAGE_PSE      ? 'm' : 'k';
#else /* LINUX_VERSION_CODE < 0x020400 */
    *(buf + i++) = pgprot & _PAGE_4M       ? 'm' : 'k';
#endif /* LINUX_VERSION_CODE < 0x020400 */
    *(buf + i++) = pgprot & _PAGE_GLOBAL   ? 'g' : 'l';
#endif /* __i386__ */		
    *(buf + i++) = 0;

    return buf;
}

char* ATI_API_CALL __ke_vm_phys_addr_str(struct vm_area_struct* vma, 
                            char* buf, 
                            unsigned long virtual_addr, 
                            unsigned long* phys_address)
{
    unsigned long pte_linear;
    pgd_t* pgd_p;
    pmd_t* pmd_p;
    pte_t* pte_p;
    pte_t  pte;

    pte_linear = VMALLOC_VMADDR(virtual_addr);  // convert to pte linear address (x86 => nop)
    pgd_p = pgd_offset(vma->vm_mm, pte_linear);
    pmd_p = pmd_offset(pgd_p, pte_linear);
#ifndef FGL_ATOMIC_PTE
#if LINUX_VERSION_CODE > 0x020500
    pte_p = pte_offset_kernel(pmd_p, pte_linear);
    pte = *pte_p;                                                                
#else
    pte_p = pte_offset(pmd_p, pte_linear);
    pte = *pte_p;                                                                
    pte_unmap(pte_p);
#endif
#else
    pte_p = pte_offset_atomic(pmd_p, pte_linear);                                
    pte = *pte_p;       // duplicate data
    pte_kunmap(pte_p);  // release atomic lock
#endif

	if (pte_present(pte))
    {
#if LINUX_VERSION_CODE >= 0x020400
#if defined(__x86_64__) || defined(__ia64__)
        *phys_address = pte.pte & PAGE_MASK;
#else
        *phys_address = pte.pte_low & PAGE_MASK;
#endif
#else /* LINUX_VERSION_CODE < 0x020400 */
        *phys_address = virt_to_phys((void*)pte_page(pte));
#endif /* LINUX_VERSION_CODE < 0x020400 */

        sprintf(buf, "0x%08lx %c%c%c%c%c\n",
           *phys_address,
           pte_read (pte) ? 'r' : '-',
           pte_write(pte) ? 'w' : '-',
           pte_exec (pte) ? 'x' : '-',
           pte_dirty(pte) ? 'd' : '-',
           pte_young(pte) ? 'a' : '-');
    }
    else
        *buf = 0;

    return buf;
}

void ip_drm_vm_open(struct vm_area_struct* vma)
{ drm_vm_open(vma); }
void ip_drm_vm_close(struct vm_area_struct* vma)
{ drm_vm_close(vma); }

static struct vm_operations_struct vm_ops =
{
    nopage:  vm_nopage,
    open:    ip_drm_vm_open,
    close:   ip_drm_vm_close,
};

#ifdef __AGP__BUILTIN__
#ifdef __ia64__
static struct vm_operations_struct vm_cant_ops =
{
    nopage:  vm_cant_nopage,
    open:    ip_drm_vm_open,
    close:   ip_drm_vm_close,
};
#endif /* __ia64_ */
#endif /* __AGP__BUILTIN__ */

static struct vm_operations_struct vm_shm_ops =
{
    nopage:  vm_shm_nopage,
    open:    ip_drm_vm_open,
    close:   ip_drm_vm_close,
};

static struct vm_operations_struct vm_pci_bq_ops =
{
    nopage:  vm_dma_nopage,
    open:    ip_drm_vm_open,
    close:   ip_drm_vm_close,
};

static struct vm_operations_struct vm_ctx_ops =
{
    nopage:  vm_dma_nopage,
    open:    ip_drm_vm_open,
    close:   ip_drm_vm_close,
};

static struct vm_operations_struct vm_pcie_ops = 
{
    nopage:  vm_pcie_nopage,
    open:    ip_drm_vm_open,
    close:   ip_drm_vm_close,
};


#ifdef __AGP__BUILTIN__
#ifndef __ia64__
static struct vm_operations_struct vm_agp_bq_ops =
{
    nopage:  vm_nopage,
    open:    ip_drm_vm_open,
    close:   ip_drm_vm_close,
};
#else		
static struct vm_operations_struct vm_cant_agp_bq_ops =
{
    nopage:  vm_cant_nopage,
    open:    ip_drm_vm_open,
    close:   ip_drm_vm_close,
};
#endif /* __ia64_ */
#endif /* __AGP__BUILTIN__ */

int ATI_API_CALL __ke_vm_map(struct file* filp,
                struct vm_area_struct* vma,
                enum __ke_vm_maptype type,
                int readonly)
{
    unsigned int pages;
    __KE_DEBUG3("start=0x%08lx, "
            "end=0x%08lx, "
            "offset=0x%08lx\n",
            vma->vm_start,
            vma->vm_end,
            (unsigned long)__ke_vm_offset(vma));

    switch (type)
    {
        case __KE_ADPT:
			{
#ifdef __ia64__
			struct page 	*page = virt_to_page((unsigned long)__va(VM_OFFSET(vma)));
			if (!VALID_PAGE(page) || PageReserved(page))
#else
            if (__ke_vm_offset(vma) >= __pa(high_memory))
#endif
            {
#ifdef __i386__
                if (boot_cpu_data.x86 > 3)
                {
                    pgprot_val(vma->vm_page_prot) |= _PAGE_PCD;
                    pgprot_val(vma->vm_page_prot) &= ~_PAGE_PWT;
                }
#endif /* __i386__ */
#ifdef __ia64__
				vma->vm_page_prot =
					pgprot_writecombine(vma->vm_page_prot);
#endif /* __ia64__ */
                vma->vm_flags |= VM_IO; /* not in core dump */
            }
            if (remap_page_range(FGL_VMA_API_PASS
                                 vma->vm_start,
                                 __ke_vm_offset(vma),
                                 vma->vm_end - vma->vm_start,
                                 vma->vm_page_prot))
            {
                __KE_DEBUG("remap_page_range failed\n");
                return -EAGAIN;
            }
            vma->vm_flags |= VM_SHM | VM_RESERVED; /* Don't swap */
            vma->vm_ops = &vm_ops;
            }
			break;

        case __KE_SHM:
            vma->vm_flags |= VM_SHM | VM_RESERVED; /* Don't swap */
            vma->vm_ops = &vm_shm_ops;
            break;

        case __KE_SG:

            pages = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;

#if LINUX_VERSION_CODE <= 0x02040e /* KERNEL_VERSION(2,4,14) */
            vma->vm_flags |= VM_LOCKED | VM_SHM; /* Don't swap */
#else
            vma->vm_flags |= VM_RESERVED;
#endif

            //vma->vm_flags |=  VM_SHM | VM_LOCKED; /* DDDDDDDDDDon't swap */
            //vma->vm_mm->locked_vm += pages; /* Kernel tracks aqmount of locked pages */
            vma->vm_ops = &vm_pcie_ops;
            break;

        case __KE_CTX:
            pages = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
            vma->vm_flags |= VM_LOCKED | VM_SHM | VM_RESERVED; /* Don't swap */
            vma->vm_mm->locked_vm += pages; /* Kernel tracks aqmount of locked pages */
            vma->vm_ops = &vm_ctx_ops;
            break;

        case __KE_PCI_BQS:
            pages = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
            vma->vm_flags |= VM_LOCKED | VM_SHM | VM_RESERVED; /* Don't swap */
            vma->vm_mm->locked_vm += pages; /* Kernel tracks aqmount of locked pages */
            vma->vm_ops = &vm_pci_bq_ops;
            break;

#ifdef __AGP__BUILTIN__
        case __KE_AGP:
		//	if(dev->agp->cant_use_aperture == 1) 
#ifdef __ia64__
			{
                	/*
                	 * On some systems we can't talk to bus dma address from
                	 * the CPU, so for memory of type DRM_AGP, we'll deal
                	 * with sorting out the real physical pages and mappings
                	 * in our page fault handler
                	 */
				vma->vm_flags |= VM_SHM | VM_RESERVED; /* Don't swap */
				vma->vm_ops = &vm_cant_ops;
			}
#else
			//			else
			{
				if (__ke_vm_offset(vma) >= __pa(high_memory))
					vma->vm_flags |= VM_IO; /* not in core dump */
				if (remap_page_range(FGL_VMA_API_PASS
									 vma->vm_start,
									 __ke_vm_offset(vma),
									 vma->vm_end - vma->vm_start,
									 vma->vm_page_prot))
				{
					__KE_DEBUG("remap_page_range failed\n");
					return -EAGAIN;
				}
#ifdef __x86_64__
				vma->vm_flags |= VM_RESERVED;
#else
				vma->vm_flags |= VM_SHM | VM_RESERVED; /* Don't swap */
#endif
				vma->vm_ops = &vm_ops;
			}
#endif
            break;
        case __KE_AGP_BQS:
//			if(dev->agp->cant_use_aperture == 1) 
#ifdef __ia64__			
			{
                	/*
                	 * On some systems we can't talk to bus dma address from
                	 * the CPU, so for memory of type DRM_AGP, we'll deal
                	 * with sorting out the real physical pages and mappings
                	 * in our page fault handler
                	 */
				vma->vm_flags |= VM_SHM | VM_RESERVED; /* Don't swap */
				vma->vm_ops = &vm_cant_agp_bq_ops;

			}
//			else
#else
			{
				if (__ke_vm_offset(vma) >= __pa(high_memory))
					vma->vm_flags |= VM_IO; /* not in core dump */
				if (remap_page_range(FGL_VMA_API_PASS
									 vma->vm_start,
									 __ke_vm_offset(vma),
									 vma->vm_end - vma->vm_start,
									 vma->vm_page_prot))
				{
					__KE_DEBUG("remap_page_range failed\n");
					return -EAGAIN;
				}
#ifdef __x86_64__
				vma->vm_flags |= VM_RESERVED;
#else
				vma->vm_flags |= VM_SHM | VM_RESERVED; /* Don't swap */
#endif
				vma->vm_ops = &vm_agp_bq_ops;
			}
#endif
            break;
#endif /* __AGP__BUILTIN__ */

        default:
            /*  This should never happen anyway! */
            __KE_ERROR("__ke_vm_map: Unknown type %d\n", type);
            return -EINVAL;
    }

    if (readonly)
#if defined(__i386__)
		pgprot_val(vma->vm_page_prot) &= ~_PAGE_RW;
#else
		vma->vm_page_prot = __pgprot(pte_val(pte_wrprotect(
			__pte(pgprot_val(vma->vm_page_prot)))));
#endif

#if LINUX_VERSION_CODE < 0x020203 /* KERNEL_VERSION(2,2,3) */
    /* In Linux 2.2.3 and above, this is handled in do_mmap() in mm/mmap.c. */
    ++filp->f_count;
#endif /* LINUX_VERSION_CODE < 0x020203 */
    vma->vm_file = filp;    /* Needed for drm_vm_open() */

    return 0;
}


/*****************************************************************************/

#ifdef __AGP__

#if LINUX_VERSION_CODE >= 0x020400

static const drm_agp_t  *drm_agp_module_stub = NULL;

#define AGP_FUNCTIONS		8
#define AGP_AVAILABLE(func)	(drm_agp_module_stub && drm_agp_module_stub-> func )
#define AGP_FUNC(func)		(*drm_agp_module_stub-> func )
// note: avoid ##-tokens with latest GCC (i.e. RedHat 7.1 beta)

#else /* LINUX_VERSION_CODE < 0x020400 */
enum agp_func_ndx {
    __ndx_agp_free_memory,
    __ndx_agp_allocate_memory,
    __ndx_agp_bind_memory,
    __ndx_agp_unbind_memory,
    __ndx_agp_enable,
    __ndx_agp_acquire,
    __ndx_agp_release,
    __ndx_agp_copy_info,
#ifdef FGL
    __ndx_agp_allocate_memory_phys_list,
#endif
};

/* The C standard says that 'void *' is not guaranteed to hold a function
   pointer, so we use this union to define a generic pointer that is
   guaranteed to hold any of the function pointers we care about. */
typedef union 
{
	void (*free_memory)(agp_memory*);
	agp_memory *(*allocate_memory)(size_t, u32);
	int (*bind_memory)(agp_memory*, off_t);
	int (*unbind_memory)(agp_memory*);
	void (*enable)(u32);
	int (*acquire)(void);
	void (*release)(void);
	void (*copy_info)(agp_kern_info*);
	unsigned long address;
} agp_func_u;

typedef struct agp_fill
{
    const char* name;   /*  pointer to the name of the symbol to resolve */
    agp_func_u f;       /*  pointer to function from agp_gart-module */
} agp_fill_t;

static agp_fill_t drm_agp_module_stub[] = {
    [__ndx_agp_free_memory] = 
        { __MODULE_STRING(agp_free_memory),		{ address: 0 } },
    [__ndx_agp_allocate_memory] = 
        { __MODULE_STRING(agp_allocate_memory), { address: 0 } },
    [__ndx_agp_bind_memory] = 
        { __MODULE_STRING(agp_bind_memory),     { address: 0 } },
    [__ndx_agp_unbind_memory] = 
        { __MODULE_STRING(agp_unbind_memory),   { address: 0 } },
    [__ndx_agp_enable] = 
        { __MODULE_STRING(agp_enable),          { address: 0 } },
    [__ndx_agp_acquire] = 
        { __MODULE_STRING(agp_backend_acquire), { address: 0 } },
    [__ndx_agp_release] = 
        { __MODULE_STRING(agp_backend_release), { address: 0 } },
    [__ndx_agp_copy_info] = 
        { __MODULE_STRING(agp_copy_info),       { address: 0 } },
#ifdef FGL
    [__ndx_agp_allocate_memory_phys_list] = 
        { __MODULE_STRING(agp_allocate_memory_phys_list), { address: 0 } },
#endif
};

#define AGP_FUNCTIONS		(sizeof(drm_agp_module_stub) / sizeof(drm_agp_module_stub[0]))
#define AGP_AVAILABLE(func)	(drm_agp_module_stub[__ndx_agp_##func].f.address != 0)
#define AGP_FUNC(func)		(*drm_agp_module_stub[__ndx_agp_##func].f. func)

#endif /* LINUX_VERSION_CODE < 0x020400 */

#endif /* __AGP__ */

static int firegl_agp = 0;
int __ke_agp_try_unsupported = 0;

// WARNING: Don't enable USE_FIREGL_AGPGART_IMPLEMENTATION, because it's not yet implemented!!!
//#define USE_FIREGL_AGPGART_IMPLEMENTATION
#ifdef USE_FIREGL_AGPGART_IMPLEMENTATION
#define FIREGL_agp_init             firegl_agp_init
#define FIREGL_agp_cleanup          firegl_agp_cleanup
#ifdef FGL
#define FIREGL_agp_allocate_memory_phys_list    firegl_agp_allocate_memory_phys_list
#endif
#define FIREGL_agp_free_memory      firegl_agp_free_memory
#define FIREGL_agp_allocate_memory  firegl_agp_allocate_memory
#define FIREGL_agp_bind_memory      firegl_agp_bind_memory
#define FIREGL_agp_unbind_memory    firegl_agp_unbind_memory
#define FIREGL_agp_enable           firegl_agp_enable
#define FIREGL_agp_backend_acquire  firegl_agp_acquire
#define FIREGL_agp_backend_release  firegl_agp_release
#define FIREGL_agp_memory           struct _agp_memory
#else // !USE_FIREGL_AGPGART_IMPLEMENTATION
#define FIREGL_agp_init             _X(agp_init)
#define FIREGL_agp_cleanup          _X(agp_cleanup)
#ifdef FGL
#define FIREGL_agp_allocate_memory_phys_list    _X(agp_allocate_memory_phys_list)
#endif
#define FIREGL_agp_free_memory      _X(agp_free_memory)
#define FIREGL_agp_allocate_memory  _X(agp_allocate_memory)
#define FIREGL_agp_bind_memory      _X(agp_bind_memory)
#define FIREGL_agp_unbind_memory    _X(agp_unbind_memory)
#define FIREGL_agp_enable           _X(agp_enable)
#define FIREGL_agp_backend_acquire  _X(agp_backend_acquire)
#define FIREGL_agp_backend_release  _X(agp_backend_release)
#define FIREGL_agp_memory           _X(agp_memory)
#endif // !USE_FIREGL_AGPGART_IMPLEMENTATION

static
int ATI_API_CALL __ke_firegl_agpgart_available(void)
{
    int retval;

#ifndef USE_FIREGL_AGPGART_IMPLEMENTATION
    if (__ke_agp_try_unsupported)
        _X(agp_try_unsupported) = __ke_agp_try_unsupported;
#endif // !USE_FIREGL_AGPGART_IMPLEMENTATION

    retval = FIREGL_agp_init();
    if (retval) 
    {
        __KE_DEBUG("Initialization of built-in AGP-support failed (ret=%d).\n", retval);
        return 0;
    }

    __KE_DEBUG("Initialization of built-in AGP-support successful.\n");
    firegl_agp = 1;

    return 1;
}

static
int ATI_API_CALL __ke_agpgart_available(void)
{
#ifdef __AGP__
    unsigned int found = 0;
    
    __KE_DEBUG("\n");

#if LINUX_VERSION_CODE >= 0x020400
	drm_agp_module_stub = DRM_AGP_MODULE_GET;
    if (!drm_agp_module_stub) 
    {
        __KE_DEBUG("getting module stub failed for AGP/GART kernel module\n");
        return 0; /* failed */
    }

    {
#ifdef FGL_xxx
	    if (drm_agp_module_stub->allocate_memory_phys_list) 
	    {
            __KE_DEBUG("agp_allocate_memory_phys_list resolves to 0x%08lx\n", drm_agp_module_stub->allocate_memory_phys_list);
		    found++;
	    }
        else
        {
            found++;    // we consider this as an optional function!
            // but we have to check for return values and might do a fallback instead
        }
#endif /* FGL_xxx */

	    if (drm_agp_module_stub->free_memory) 
	    {
            __KE_DEBUG("agp_free_memory resolves to 0x%08lx\n", drm_agp_module_stub->free_memory);
		    found++;
	    }
	    if (drm_agp_module_stub->allocate_memory) 
	    {
            __KE_DEBUG("agp_allocate_memory resolves to 0x%08lx\n", drm_agp_module_stub->allocate_memory);
		    found++;
	    }
	    if (drm_agp_module_stub->bind_memory) 
	    {
            __KE_DEBUG("agp_bind_memory resolves to 0x%08lx\n", drm_agp_module_stub->bind_memory);
		    found++;
	    }
	    if (drm_agp_module_stub->unbind_memory) 
	    {
            __KE_DEBUG("agp_unbind_memory resolves to 0x%08lx\n", drm_agp_module_stub->unbind_memory);
		    found++;
	    }
	    if (drm_agp_module_stub->enable) 
	    {
            __KE_DEBUG("agp_enable resolves to 0x%08lx\n", drm_agp_module_stub->enable);
		    found++;
	    }
	    if (drm_agp_module_stub->acquire) 
	    {
            __KE_DEBUG("agp_acquire resolves to 0x%08lx\n", drm_agp_module_stub->acquire);
		    found++;
	    }
	    if (drm_agp_module_stub->release) 
	    {
            __KE_DEBUG("agp_release resolves to 0x%08lx\n", drm_agp_module_stub->release);
		    found++;
	    }
	    if (drm_agp_module_stub->copy_info) 
	    {
            __KE_DEBUG("agp_copy_info resolves to 0x%08lx\n", drm_agp_module_stub->copy_info);
		    found++;
	    }
    }
#else /* LINUX_VERSION_CODE < 0x020400 */
	{
	    int i;

		for (i = 0; i < sizeof(drm_agp_module_stub) / sizeof(drm_agp_module_stub[0]); i++)
		{
			drm_agp_module_stub[i].f = 
				(agp_func_u)get_module_symbol(NULL, (char*)drm_agp_module_stub[i].name);
			__KE_DEBUG("%s resolves to 0x%08lx\n", 
				drm_agp_module_stub[i].name, drm_agp_module_stub[i].f.address);
			if (drm_agp_module_stub[i].f.address)
				found++;
		}
	}
#endif /* LINUX_VERSION_CODE < 0x020400 */
    
    if (found == AGP_FUNCTIONS) {
        __KE_DEBUG("AGP/GART kernel module present, API is complete.\n");
        return 1; /* success */
    }

    if (found == 0)
        __KE_DEBUG("AGP/GART kernel module not found\n");
    else
    {
        __KE_DEBUG("AGP/GART kernel module present but API is incomplete\n");

        /* do a complete cleanup of our entry point table now */
        __ke_agp_uninit();
    }

#else /* !__AGP__ */
    __KE_ERROR("Kernel native AGP support was not present at module build time.\n");
#endif /* !__AGP__ */

    return 0; /* failed */
}

int ATI_API_CALL __ke_agp_available(int use_internal)
{
    int available = __ke_agpgart_available();

    if ( available )
    {
        if ( use_internal )
        {
            __KE_INFO("Internal AGP support requested, but kernel AGP support active.\n");
            __KE_INFO("Have to use kernel AGP support to avoid conflicts.\n");
        }
        if ( __ke_moduleflags & __KE_FLAG_DISABLE_AGPGART )
        {
            __KE_ERROR("Have to use kernel AGP support, but module parameters forbid that\n");
            __ke_agp_uninit();
            available = 0;
        }
    }
    else if ( use_internal &&
              !( __ke_moduleflags & __KE_FLAG_DISABLE_FIREGL_AGPGART ) )
    {
        available = __ke_firegl_agpgart_available();
    }

    return available;
}

void ATI_API_CALL __ke_agp_uninit(void)
{
    if (firegl_agp) 
    {
        FIREGL_agp_cleanup();
        firegl_agp = 0;
        return;
    }

#ifdef __AGP__
#if LINUX_VERSION_CODE >= 0x020400
    if (drm_agp_module_stub)
    {
	    DRM_AGP_MODULE_PUT;
    }
	drm_agp_module_stub = NULL;
#else /* LINUX_VERSION_CODE < 0x020400 */
    {
        int i;
        for (i = 0; i < sizeof(drm_agp_module_stub) / sizeof(drm_agp_module_stub[0]); i++)
        {
            drm_agp_module_stub[i].f.address = 0;
        }
    }
#endif /* LINUX_VERSION_CODE < 0x020400 */
#endif /* __AGP__ */
}

#ifdef FGL
struct _agp_memory* ATI_API_CALL __ke_agp_allocate_memory_phys_list(__ke_size_t pages, unsigned long type, unsigned long * phys_addr)
{
#if 0
#ifdef __AGP__
	if (AGP_AVAILABLE(allocate_memory_phys_list))
		return AGP_FUNC(allocate_memory_phys_list)(pages, type, phys_addr);
#endif /* __AGP__ */
#endif
	if (firegl_agp)
		return (struct _agp_memory*)FIREGL_agp_allocate_memory_phys_list(pages, type, phys_addr);
    return NULL;
}
#endif

void ATI_API_CALL __ke_agp_free_memory(struct _agp_memory* handle)
{
#ifdef __AGP__
	if (AGP_AVAILABLE(free_memory))
		AGP_FUNC(free_memory)(handle);
#endif /* __AGP__ */
	if (firegl_agp)
		return FIREGL_agp_free_memory((FIREGL_agp_memory*)handle);
}

struct _agp_memory* ATI_API_CALL __ke_agp_allocate_memory(__ke_size_t pages, unsigned long type)
{
#ifdef __AGP__
	if (AGP_AVAILABLE(allocate_memory))
		return AGP_FUNC(allocate_memory)(pages, type);
#endif /* __AGP__ */
	if (firegl_agp)
		return (struct _agp_memory*)FIREGL_agp_allocate_memory(pages, type);
    return NULL;
}

int ATI_API_CALL __ke_agp_bind_memory(struct _agp_memory* handle, __ke_off_t start)
{
#ifdef __AGP__
	if (AGP_AVAILABLE(bind_memory))
		return AGP_FUNC(bind_memory)(handle, start);
#endif /* __AGP__ */
	if (firegl_agp)
		return FIREGL_agp_bind_memory((FIREGL_agp_memory*)handle, start);
    return -EINVAL;
}

int ATI_API_CALL __ke_agp_unbind_memory(struct _agp_memory* handle)
{
#ifdef __AGP__
	if (AGP_AVAILABLE(unbind_memory))
		return AGP_FUNC(unbind_memory)(handle);
#endif /* __AGP__ */
	if (firegl_agp)
		return FIREGL_agp_unbind_memory((FIREGL_agp_memory*)handle);
    return -EINVAL;
}

int ATI_API_CALL __ke_agp_enable(unsigned long mode)
{
#ifdef __AGP__
	if (AGP_AVAILABLE(enable))
    {
		AGP_FUNC(enable)(mode);
        return 0;
    }
#endif /* __AGP__ */
	if (firegl_agp)
    {
		FIREGL_agp_enable(mode);
        return 0;
    }
    return -EINVAL;
}

int ATI_API_CALL __ke_read_agp_caps_registers(__ke_pci_dev_t* dev, unsigned int *caps)
{
    u8 capndx;
	u32 cap_id;

    if (!caps)
        return -EINVAL;

    if (!(struct pci_dev*)(void *)dev)
        return -ENODEV;

    pci_read_config_byte((struct pci_dev*)(void *)dev, 0x34, &capndx);
    if (capndx == 0x00)
        return -ENODATA;

    do { // search capability list for AGP caps
        pci_read_config_dword((struct pci_dev*)(void *)dev, capndx, &cap_id);
        if ((cap_id & 0xff) == 0x02)
        {
            pci_read_config_dword((struct pci_dev*)(void *)dev, capndx + 0, &(caps[0])); /* AGP CAPPTR */
            pci_read_config_dword((struct pci_dev*)(void *)dev, capndx + 4, &(caps[1])); /* AGP STATUS */
            pci_read_config_dword((struct pci_dev*)(void *)dev, capndx + 8, &(caps[2])); /* AGP COMMAND */

            return 0; /* success */
        }
        capndx = (cap_id >> 8) & 0xff;
    } while (capndx != 0x00);

    return -ENODATA;
}

int ATI_API_CALL __ke_agp_acquire(void)
{
#ifdef __AGP__
	if (AGP_AVAILABLE(acquire))
		return AGP_FUNC(acquire)();
#endif /* __AGP__ */
	if (firegl_agp)
		return FIREGL_agp_backend_acquire();
    return -EINVAL;
}

void ATI_API_CALL __ke_agp_release(void)
{
#ifdef __AGP__
	if (AGP_AVAILABLE(release))
		AGP_FUNC(release)();
#endif /* __AGP__ */
	if (firegl_agp)
		FIREGL_agp_backend_release();
}

void ATI_API_CALL __ke_agp_copy_info(__ke_agp_kern_info_t* info)
{
    struct pci_dev *device = NULL;

    memset(info, 0, sizeof(__ke_agp_kern_info_t));

#ifdef __AGP__
	if (AGP_AVAILABLE(copy_info))
    {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,71)
        struct
#endif
        agp_kern_info kern;    

        AGP_FUNC(copy_info)(&kern);
        device = kern.device;

        info->version.major = kern.version.major;
        info->version.minor = kern.version.minor;
        if( kern.device )
        {
            info->vendor = kern.device->vendor;
            info->device = kern.device->device;
        }
        info->mode = kern.mode;
        info->aper_base = kern.aper_base;
        info->aper_size = kern.aper_size;
        info->max_memory = kern.max_memory;
        info->current_memory = kern.current_memory;
#if LINUX_VERSION_CODE <= 0x020408
        info->cant_use_aperture = 0;
        info->page_mask = ~(0xfff);
#else
		info->cant_use_aperture = kern.cant_use_aperture;
		info->page_mask = kern.page_mask;
#endif
    }
#endif /* __AGP__ */

    if (firegl_agp) 
    {
#ifdef USE_FIREGL_AGPGART_IMPLEMENTATION
        firegl_agp_copy_info(info);
        device = kern.device;
#else // !USE_FIREGL_AGPGART_IMPLEMENTATION
        _X(agp_kern_info) kern;    

        _X(agp_copy_info)(&kern);
        device = kern.device;

        info->version.major = kern.version.major;
        info->version.minor = kern.version.minor;
        if( kern.device )
        {
            info->vendor = kern.device->vendor;
            info->device = kern.device->device;
        }
        info->mode = kern.mode;
        info->aper_base = kern.aper_base;
        info->aper_size = kern.aper_size;
        info->max_memory = kern.max_memory;
        info->current_memory = kern.current_memory;
		info->cant_use_aperture = kern.cant_use_aperture;
		info->page_mask = kern.page_mask;
#endif // !USE_FIREGL_AGPGART_IMPLEMENTATION
    }

    /* FGL_FIX: some chipset drivers do not read the mode member from hardware */
    if( device )
    {
        if( !info->mode )
        {
            u8 capptr;

            capptr = pci_find_capability(device, PCI_CAP_ID_AGP);
            if( capptr )
            {
                u32 tmp;
                pci_read_config_dword(device,
                    capptr + PCI_AGP_STATUS, &tmp);

                info->mode = tmp; /* note: unsigned int (32/64) = u32; */
            }
        }
    }
}

unsigned long ATI_API_CALL __ke_agp_memory_handle(struct _agp_memory* handle)
{
    if (firegl_agp) 
#ifdef USE_FIREGL_AGPGART_IMPLEMENTATION
        return firegl_agp_memory_handle(handle);
#else // !USE_FIREGL_AGPGART_IMPLEMENTATION
        return (unsigned long)((_X(agp_memory)*)handle)->memory;
#endif // !USE_FIREGL_AGPGART_IMPLEMENTATION

#ifdef __AGP__
    return (unsigned long)handle->memory;
#else /* !__AGP__ */
    return 0;
#endif /* !__AGP__ */
}

unsigned long ATI_API_CALL __ke_agp_memory_page_count(struct _agp_memory* handle)
{
    if (firegl_agp) 
#ifdef USE_FIREGL_AGPGART_IMPLEMENTATION
        return firegl_agp_memory_page_count(handle);
#else // !USE_FIREGL_AGPGART_IMPLEMENTATION
        return ((_X(agp_memory)*)handle)->page_count;
#endif // !USE_FIREGL_AGPGART_IMPLEMENTATION

#ifdef __AGP__
    return handle->page_count;
#else /* !__AGP__ */
    return 0;
#endif /* !__AGP__ */
}

int ATI_API_CALL __ke_smp_processor_id(void)
{
	return (int)(smp_processor_id());
}


void ATI_API_CALL __ke_smp_call_function( void (*ATI_API_CALL func)(void *info) )
{
	smp_call_function( func, NULL, 0, 1 );
}

#if !defined(__x86_64__) && !defined(__ia64__)

#define vendorStringLen 12

static inline int check_cpu_vendor(const char* vendorName)
{
    int sVendor[vendorStringLen/sizeof(int)];
    __asm
    (
        "\txor %%eax, %%eax\n"  // function #0: get vendor string
        "\tcpuid\n"             // perform CPUID
        "\tmov %%ebx, %0\n"     // store 3 times 4 bytes of vendor string
        "\tmov %%edx, %1\n"
        "\tmov %%ecx, %2\n"
        : "=m" (sVendor[0]), "=m" (sVendor[1]), "=m"(sVendor[2])   // results go to this location
        :                       // no input
        : "eax", "ebx", "ecx", "edx"    // modifiys this registers
    );

    if( strncmp((char*)sVendor,vendorName,vendorStringLen)==0 )
        return 1;   // thats a match
    else
        return 0;   // does not match
}

int ATI_API_CALL __ke_is_athlon(void)
{
    register int bAthlon;
    __asm
    (
        // Check for CPUID instruction
        "\tpushf\n"              	//; save EFLAGS
        "\tpop %%eax\n"             	//; store EFLAGS in EAX
        "\tmov %%eax, %%ebx\n"        	//; save in EBX for later testing
        "\txor $0x00200000, %%eax\n"  	//; toggle bit 21
        "\tpush %%eax\n"            	//; put to stack
        "\tpopf\n"               	//; save changed EAX to EFLAGS
        "\tpushf\n"              	//; push EFLAGS to TOS
        "\tpop %%eax\n"             	//; store EFLAGS in EAX
        "\tcmp %%ebx, %%eax\n"        	//; see if bit 21 has changed
        "\tjz NO_ATHLON\n"        	//; if no change, no CPUID

        // Check for extended functions
        "\tmov $0x80000000, %%eax\n"  	//; query for extended functions
        "\tCPUID\n"               	//; get extended function limit
        "\tcmp $0x80000000, %%eax\n"  	//; is 8000_0001h supported?
        "\tjbe NO_ATHLON\n"       	//; if not, 3DNow! command set not supported

        "\tmov $0x80000001, %%eax\n"  	//; setup extended function 1
        "\tCPUID\n"               	//; call the function
        "\ttest $0x80000000, %%edx\n" 	//; test bit 31
        "\tjz NO_ATHLON\n"        	//; 3DNow! command set not supported
        "\tmov $1, %%eax\n"
        "\tjmp DONE\n"
"NO_ATHLON:\n"
        "\txor %%eax, %%eax\n"
"DONE:\n"
        "\tmov %%eax, %0\n"         //; store to bAthlon
        // optimizer hints
        : "=r" (bAthlon)            // let compiler choose output register
        :                           // no input selection for compiler
        : "eax", "ebx", "ecx", "edx"	// modifiys
    );

    if( bAthlon )
    {
        if( !check_cpu_vendor("AuthenticAMD") )
            bAthlon = 0;
    }

    return bAthlon;
}

/*=======================================================================
 * amd_adv_spec_cache_feature
 *
 * Test if this is an AMD Athlon processor that exposes a conflicting
 * cache attribute bug in the kernel.
 =======================================================================*/

#if ( (PAGE_ATTR_FIX == 1) || (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,19)) )
// not used
#else
/* Standard macro to see if a specific flag is changeable */
static inline int flag_is_changeable_p(u32 flag)
{
	u32 f1, f2;
	asm("pushfl\n\t"
	    "pushfl\n\t"
	    "popl %0\n\t"
	    "movl %0,%1\n\t"
	    "xorl %2,%0\n\t"
	    "pushl %0\n\t"
	    "popfl\n\t"
	    "pushfl\n\t"
	    "popl %0\n\t"
	    "popfl\n\t"
	    : "=&r" (f1), "=&r" (f2)
	    : "ir" (flag));

	return ((f1^f2) & flag) != 0;
}


/* Probe for the CPUID instruction */
static int __init have_cpuid_p(void)
{
	return flag_is_changeable_p(X86_EFLAGS_ID);
}
#endif


int ATI_API_CALL __ke_amd_adv_spec_cache_feature(void)
{
#if ( (PAGE_ATTR_FIX == 1) || (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,19)) )
/* the kernel already does provide a fix for the AMD Athlon
   big page attribute / cache flush data consistency system bug on its own.
   (AMD claimed that CPU cache behaviour for such pages is not specified.)
   - kernel 2.4.19 has the early fix which just removes some page attributes.
   - later kernels do have implementeed the full big page split code fix.
*/
#else /* PAGE_ATTR_FIX */
/* the kernel does not provide any fix for the AMD problem. */
        char vendor_id[16];
        int ident;
        int family, model;
 
        /* Must have CPUID */
        if(!have_cpuid_p())
                goto donthave;
        if(cpuid_eax(0)<1)
                goto donthave;
        
        /* Must be x86 architecture */
        cpuid(0, &ident,  
                (int *)&vendor_id[0],
                (int *)&vendor_id[8],
                (int *)&vendor_id[4]);

        if (memcmp(vendor_id, "AuthenticAMD", 12)) 
               goto donthave;

        ident = cpuid_eax(1);
        family = (ident >> 8) & 0xf;
        model  = (ident >> 4) & 0xf;
        if (((family == 6)  && (model >= 6)) || (family == 15)) {
                return 1;
        }

donthave:
#endif /* PAGE_ATTR_FIX */
        return 0;
}

int ATI_API_CALL __ke_has_PSE(void)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,71)
	if (test_bit(X86_FEATURE_PSE, &boot_cpu_data.x86_capability))
#else
	if (cpu_has_pse)
#endif
    {
		return 1;
	}
	return 0;
}

#endif /* !__x86_64__ */

#endif /* __KERNEL__ */
