#ifndef __EMU_WRAPPER_H
#define __EMU_WRAPPER_H

/* wrapper for 2.2 kernel */

#include <linux/wrapper.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <asm/page.h>
#include <linux/mm.h>
#include <asm/io.h>
#include <linux/ioport.h>
#include <linux/malloc.h>
#include <linux/tqueue.h>
#include <linux/version.h>

extern loff_t no_llseek(struct file *file, loff_t offset, int origin);

#define min_t(type,x,y) \
        ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; })

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)

typedef struct wait_queue *wait_queue_head_t;
#define DECLARE_WAIT_QUEUE_HEAD(wait)		wait_queue_head_t wait

#define init_waitqueue_head(x)			*(x)=NULL
#define init_MUTEX(x)				*(x)=MUTEX

static __inline__ void list_add_tail(struct list_head *new, struct list_head *head)
{
        __list_add(new, head->prev, head);
}

typedef u32 dma_addr_t;

#ifdef MODULE
/* Not sure what version aliases were introduced in, but certainly in 2.91.66.  */
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 91)
/* These macros create a dummy inline: gcc 2.9x does not count alias
 as usage, hence the `unused function' warning when __init functions
 are declared static. We use the dummy __*_module_inline functions
 both to kill the warning and check the type of the init/cleanup
 function. */
typedef int (*__init_module_func_t)(void);
typedef void (*__cleanup_module_func_t)(void);
#define module_init(x) \
        int init_module(void) __attribute__((alias(#x))); \
        extern inline __init_module_func_t __init_module_inline(void) \
        { return x; }
#define module_exit(x) \
        void cleanup_module(void) __attribute__((alias(#x))); \
        extern inline __cleanup_module_func_t __cleanup_module_inline(void) \
        { return x; }
#else
#define module_init(x)  int init_module(void) { return x(); }
#define module_exit(x)  void cleanup_module(void) { x(); }
#endif
#endif /* MODULE */

#define list_for_each(pos, head) \
        for (pos = (head)->next; pos != (head); pos = pos->next)

extern __inline__ int __compat_get_order(unsigned long size)
{
        int order;

        size = (size-1) >> (PAGE_SHIFT-1);
        order = -1;
        do {
                size >>= 1;
                order++;
        } while (size);
        return order;
}

#define get_order(size) __compat_get_order(size)

#define pci_resource_len pci_compat_get_size

unsigned long pci_compat_get_size (struct pci_dev *dev, int n_base);

#define BUG() do { \
        printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); \
        __asm__ __volatile__(".byte 0x0f,0x0b"); \
} while (0)

#endif  /*  LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18) */

#define AFMT_AC3	0x00000400	/* Dolby Digital AC3 */

#define vma_get_pgoff(v)	vma_get_offset(v)

#define UP_INODE_SEM(a)		up(a)
#define DOWN_INODE_SEM(a)	down(a)

#define GET_INODE_STRUCT()	struct inode *inode = file->f_dentry->d_inode

#define tasklet_hi_schedule(t)	queue_task((t), &tq_immediate); \
				mark_bh(IMMEDIATE_BH)

#define tasklet_init(t,f,d)	(t)->next = NULL; \
				(t)->sync = 0; \
				(t)->routine = (void (*)(void *))(f); \
				(t)->data = (void *)(d)

#define tasklet_struct		tq_struct 

#define tasklet_kill(t)		while (test_bit(0, &(t)->sync)) { }

#define __exit
#define __exitdata
#define __devinit
#define __devinitdata
#define __devexit
#define __devexitdata
#define __devexit_p

#define MODULE_DEVICE_TABLE(foo,bar)
#define MODULE_LICENSE(foo)

#define pci_set_dma_mask(dev, mask) 0

#define PCI_ANY_ID (~0)

#define pci_get_drvdata(pdev) pci_compat_get_driver_data((pdev))
#define pci_set_drvdata(pdev, data) pci_compat_set_driver_data((pdev),(data))

#define pci_enable_device pci_compat_enable_device
#define pci_register_driver pci_compat_register_driver
#define pci_unregister_driver pci_compat_unregister_driver

#define pci_for_each_dev(dev) \
        for(dev = pci_devices; dev; dev = dev->next)

#define pci_resource_start(dev,bar) \
(((dev)->base_address[(bar)] & PCI_BASE_ADDRESS_SPACE) ? \
 ((dev)->base_address[(bar)] & PCI_BASE_ADDRESS_IO_MASK) : \
 ((dev)->base_address[(bar)] & PCI_BASE_ADDRESS_MEM_MASK))

struct pci_device_id {
        unsigned int vendor, device;
        unsigned int subvendor, subdevice;
        unsigned int class, class_mask;
        unsigned long driver_data;
};

struct pci_driver {
        struct list_head node;
        struct pci_dev *dev;
        char *name;
        const struct pci_device_id *id_table;   /* NULL if wants all devices */
        int (*probe)(struct pci_dev *dev, const struct pci_device_id *id);      /* New device inserted */
        void (*remove)(struct pci_dev *dev);    /* Device removed (NULL if not a hot-plug capable driver) */
        void (*suspend)(struct pci_dev *dev);   /* Device suspended */
        void (*resume)(struct pci_dev *dev);    /* Device woken up */
};

const struct pci_device_id * pci_compat_match_device(const struct pci_device_id *ids, struct pci_dev *dev);
int pci_compat_register_driver(struct pci_driver *drv);
void pci_compat_unregister_driver(struct pci_driver *drv);
int pci_compat_enable_device(struct pci_dev *dev);
void *compat_request_region (unsigned long start, unsigned long n, const char *name);
void * pci_compat_get_driver_data (struct pci_dev *dev);
void pci_compat_set_driver_data (struct pci_dev *dev, void *driver_data);

extern __inline__ void *
pci_alloc_consistent(struct pci_dev *hwdev,
                     size_t size, dma_addr_t *dma_handle) {
        void *ret;
        int gfp = GFP_ATOMIC;

        if (hwdev == NULL)
                gfp |= GFP_DMA;
        ret = (void *)__get_free_pages(gfp, get_order(size));

        if (ret != NULL) {
                memset(ret, 0, size);
                *dma_handle = virt_to_bus(ret);
        }
        return ret;
}

extern __inline__ void
pci_free_consistent(struct pci_dev *hwdev, size_t size,
                    void *vaddr, dma_addr_t dma_handle)
{
        free_pages((unsigned long)vaddr, get_order(size));
}

static inline int pci_module_init(struct pci_driver *drv)
{
        int rc = pci_register_driver (drv);

        if (rc > 0)
                return 0;

        /* if we get here, we need to clean up pci driver instance
         * and return some sort of error */
        pci_unregister_driver (drv);
        
        return -ENODEV;
}

#ifndef DSP_CAP_MULTI
#       define DSP_CAP_MULTI            0x00004000      /* support multiple open */
#endif

#ifndef DSP_CAP_BIND
#	define DSP_CAP_BIND             0x00008000      /* channel binding to front/rear/cneter/lfe */
#endif

#endif
