/*
 * Copyright (c) 2003 Linuxant inc.
 * Copyright (c) 2001-2003 Conexant Systems, Inc.
 *
 * NOTE: The use and distribution of this software is governed by the terms in
 * the file LICENSE, which is included in the package. You must read this and
 * agree to these terms before using or distributing this software.
 *
 */
#ifndef __OSCOMPAT_H
#define __OSCOMPAT_H

#ifdef __cplusplus
extern "C"
{
#endif

#include "osuniqredef.h"

#include <linux/version.h>
#include <linux/config.h>
#include <linux/sched.h>
#include <linux/tqueue.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/kdev_t.h>

#if LINUX_VERSION_CODE == KERNEL_VERSION(2,4,9)
/* get rid of non-standard min/max macros */
#undef min
#undef max
#endif

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,2)
#include <linux/circ_buf.h>
#else

struct circ_buf {
	char *buf;
	int head;
	int tail;
};

/* Return count in buffer.  */
#define CIRC_CNT(head,tail,size) (((head) - (tail)) & ((size)-1))

/* Return space available, 0..size-1.  We always leave one free char
   as a completely full buffer has head == tail, which is the same as
   empty.  */
#define CIRC_SPACE(head,tail,size) CIRC_CNT((tail),((head)+1),(size))

/* Return count up to the end of the buffer.  Carefully avoid
   accessing head and tail more than once, so they can change
   underneath us without returning inconsistent results.  */
#define CIRC_CNT_TO_END(head,tail,size) \
	({int end = (size) - (tail); \
	  int n = ((head) + end) & ((size)-1); \
	  n < end ? n : end;})

/* Return space available up to the end of the buffer.  */
#define CIRC_SPACE_TO_END(head,tail,size) \
	({int end = (size) - 1 - (head); \
	  int n = (end + (tail)) & ((size)-1); \
	  n <= end ? n : end+1;})

#endif

#ifdef CONFIG_KDB
#define KDB_ENTER() asm("\tint $129\n")
#else
#define KDB_ENTER()
#endif

#if defined(CONFIG_PREEMPT) && defined(PROJECT) && (PROJECT == PRJ_HSF)
#error Preemptible kernel option (CONFIG_PREEMPT) not yet supported. Reconfigure your kernel without it.
#endif

#ifndef minor
#define minor(d) MINOR(d)
#endif

#ifdef MODULE
#ifndef THIS_MODULE
#define THIS_MODULE (&__this_module)
#endif
#ifndef module_init
#define module_init(x)  int init_module(void) { return x(); }
#endif
#ifndef module_exit
#define module_exit(x)  void cleanup_module(void) { x(); }
#endif
#endif

#ifndef MODULE_LICENSE
#define MODULE_LICENSE(x)
#endif

#ifndef __exit
#define __exit
#endif

#ifndef DECLARE_MUTEX
#define DECLARE_MUTEX(name)                 struct semaphore name=MUTEX
#define init_MUTEX(x)                               *(x)=MUTEX
#endif

#ifndef DECLARE_WAITQUEUE
#define DECLARE_WAITQUEUE(wait, current) struct wait_queue wait = { current, NULL }
#define DECLARE_WAIT_QUEUE_HEAD(wait)       wait_queue_head_t wait
typedef struct wait_queue *wait_queue_head_t;
#define init_waitqueue_head(x)          *(x)=NULL
#endif

#ifndef set_current_state
#define __set_current_state(state_value)	do { current->state = state_value; } while (0)
#ifdef __SMP__
#define set_current_state(state_value)		do { __set_current_state(state_value); mb(); } while (0)
#else
#define set_current_state(state_value)		__set_current_state(state_value)
#endif
#endif // set_current_state

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
#define get_zeroed_page get_free_page
#define try_inc_mod_count(mod) ((mod && !(mod->flags & MOD_DELETED)) ? __MOD_INC_USE_COUNT(mod), 1 : 0)
#endif // LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)

#ifndef PREPARE_TQUEUE
/*
 * Emit code to initialise a tq_struct's routine and data pointers
 */
#define PREPARE_TQUEUE(_tq, _routine, _data)			\
	do {							\
		(_tq)->routine = _routine;			\
		(_tq)->data = _data;				\
	} while (0)
#endif

#ifndef INIT_TQUEUE
/*
 * Emit code to initialise all of a tq_struct
 */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
#define INIT_TQUEUE(_tq, _routine, _data)			\
	do {							\
		INIT_LIST_HEAD(&(_tq)->list);				\
		(_tq)->sync = 0;				\
		PREPARE_TQUEUE((_tq), (_routine), (_data));	\
	} while (0)
#else
#define INIT_TQUEUE(_tq, _routine, _data)			\
	do {							\
		(_tq)->next = NULL;				\
		(_tq)->sync = 0;				\
		PREPARE_TQUEUE((_tq), (_routine), (_data));	\
	} while (0)
#endif
#endif

#ifndef DECLARE_TASKLET
#define tasklet_struct tq_struct
#define tasklet_init(tlet, func, parm) INIT_TQUEUE(tlet, (void (*)(void *))(func), (void *)(parm))
#define tasklet_schedule(tlet) queue_task(tlet, &tq_immediate); mark_bh(IMMEDIATE_BH)
static inline void tasklet_kill(struct tasklet_struct *t)
{
	unsigned long flags;
	struct tq_struct **pp;

	spin_lock_irqsave(&tqueue_lock, flags);

	if(t->sync) {
		for(pp = &tq_immediate; *pp; pp = &(*pp)->next) {
			if(*pp == t) {
				*pp = t->next;
				t->sync = 0;
				break;
			}
		}
	}

	spin_unlock_irqrestore(&tqueue_lock, flags);
}
#endif

#if defined(EXPORT_SYMTAB)

#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)) && defined(memcpy)
/* accomodate references to memcpy caused by gcc */
void * memcpy_for_gcc(void *dest, const void *src, __kernel_size_t n)
{
	return memcpy(dest, src, n);
}
#undef memcpy
void * memcpy(void *dest, const void *src, __kernel_size_t n) __attribute__ ((weak, alias ("memcpy_for_gcc")));
#endif

#if defined(PROJECT) && (PROJECT == PRJ_HSF)
void OsModuleUseCountInc(void)
{
	MOD_INC_USE_COUNT;
}

void OsModuleUseCountDec(void)
{
	MOD_DEC_USE_COUNT;
}
#endif

#endif // EXPORT_SYMTAB


#if defined(DEBUG_CHECK_STACK) && (defined(EXPORT_SYMTAB) || defined(CNXTSERIAL_INCLUDE_CORE))

/* inspired by Benjamin LaHaise's CONFIG_X86_STACK_CHECK patch */

static void __attribute__ ((unused, used))
_OSUNIQDEF(stack_overflow)(void *esp, void *eip)
{
	printk(KERN_ERR "%s: from %p.  esp: %p\n", __FUNCTION__, eip, esp);
	KDB_ENTER();
}

void mcount(void) __attribute__ ((alias (_OSUNIQDEF_STR(mcount))));

static int __attribute__ ((unused)) stack_danger_zone = 
		sizeof(struct task_struct) + 512;

asm(
"\n"
"THREAD_SIZE       = 8192\n" /* 2 * PAGE_SIZE */
"\n"
".data\n"
"\n"
"stack_overflowed:\n"
"	.long   0\n"
"\n"
".text\n"
"\n"
"	.align 16\n"
"\n"
"	.type    "_OSUNIQDEF_STR(mcount)",@function\n"
"	.globl "_OSUNIQDEF_STR(mcount)"\n"
"\n"
_OSUNIQDEF_STR(mcount)":\n"
"	push %eax\n"
"	movl $(THREAD_SIZE - 1),%eax\n"
"	andl %esp,%eax\n"
"	cmpl stack_danger_zone, %eax\n"
"	jle 1f\n"
"2:\n"
"	popl %eax\n"
"	ret\n"
"\n"
"1:\n"
"	movl	%esp,%eax\n"
"\n"
"	lock; btsl $0,stack_overflowed\n" /* prevent re-entry */
"	jc 2b\n"
"\n"
"	pushf\n"
"	cli\n"
"\n"
	/* push eip then esp of error for stack_overflow */
"	pushl	4(%eax)\n"
"	pushl	%eax\n"
"	call	"_OSUNIQDEF_STR(stack_overflow)"\n"
"	addl	$8,%esp\n"
"\n"
"	popf\n"
"\n"
"	popl	%eax\n"
"	movl    $0,stack_overflowed\n"
"	ret\n"
);

#warning stack check enabled

#endif

#if defined(PROJECT) && (PROJECT == PRJ_HSF)
void OsObtainedLock(void);
void OsReleasedLock(void);
int OsLocksHeld(void);

#define OSContextAllowsSleeping() ((!in_interrupt()) && (!OsLocksHeld()))
#endif

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,20)
struct usb_ctrlrequest {
	__u8 bRequestType;
	__u8 bRequest;
	__u16 wValue;
	__u16 wIndex;
	__u16 wLength;
} __attribute__ ((packed));
#endif

#ifdef __cplusplus
}
#endif

#endif /* __OSCOMPAT_H */
