/*
 **********************************************************************
 *     osutils.c - OS Services layer for emu10k1 driver
 *     Copyright 1999, 2000 Creative Labs, Inc.
 *
 **********************************************************************
 *
 *     Date                 Author          Summary of changes
 *     ----                 ------          ------------------
 *     October 20, 1999     Bertrand Lee    base code release
 *     November 2, 1999     Alan Cox        cleaned up
 *
 **********************************************************************
 *
 *     This program is free software; you can redistribute it and/or
 *     modify it under the terms of the GNU General Public License as
 *     published by the Free Software Foundation; either version 2 of
 *     the License, or (at your option) any later version.
 *
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 *
 *     You should have received a copy of the GNU General Public
 *     License along with this program; if not, write to the Free
 *     Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
 *     USA.
 *
 **********************************************************************
 */

/* FIXME! This file should not exist!
 * We do not need the crossplatform support anymore
 * This is a Linux-only driver
 */

#include "hwaccess.h"
#include "mycommon.h"

extern spinlock_t sblive_spinlock;

int osAllocMemPhysical(u32 size, u32 *phandle, void **linear, unsigned long *physical)
{
	u32 reqpage, order;
	PLMEMHANDLE mem;

	mem = kmalloc(sizeof(LMEMHANDLE), GFP_KERNEL);
	if (mem == NULL)
	  return CTSTATUS_NOMEMORY;

#ifdef MEMTEST
	DPD("kmalloc: [%p]\n", mem);
#endif

	order = 0;
	reqpage = size / PAGE_SIZE;
	
	if (size % PAGE_SIZE)
	  reqpage++;
	
	if (reqpage != 0) 
	{
		reqpage--;
		while (reqpage > 0) 
		{
			reqpage >>= 1;
			order++;
		}
	}
	
	*linear = (void *)__get_free_pages(GFP_KERNEL, order, 0);

	if (*linear == 0) 
	{
		kfree(mem);
		
#ifdef MEMTEST
		DPD("kfree: [%p]\n", mem);
#endif
		
		return CTSTATUS_NOMEMORY;
	}

#ifdef MEMTEST
	DPD("__get_free_pages: [%p]\n", *linear);
#endif
	
	/* in linux, we can directly access physical address, don't need to do
	 * phys_to_virt.
	 * In linux kernel 2.0.36, virt_to_bus does nothing, get_free_pages
	 * returns physical address. But in kernel 2.2.1 upwards,
	 * get_free_pages returns virtual address, we need to convert it
	 * to physical address. Then this physical address can be used to program
	 * hardware registers. */
	*physical = virt_to_bus(*linear);
	mem->physaddx = *physical;
	mem->virtaddx = *linear;
	mem->order    = order;
	*phandle = (u32)mem;
	
	return CTSTATUS_SUCCESS;
}


int osCopyPageTable(char *virtaddr, u32 startPage, u32 numpages, u32 *paddr)
{
	u32 i;

	for (i = 0; i < numpages; i++, paddr++) 
	{
		u32 physaddx;

		physaddx = (u32) virt_to_bus((void *) virtaddr);
		virtaddr = virtaddr + PAGE_SIZE;
		*paddr = physaddx;
	}

	return CTSTATUS_SUCCESS;
}


int osFreeMemPhysical(u32 handle)
{
	PLMEMHANDLE mem = (PLMEMHANDLE) handle;

	if (mem == NULL)
	  return CTSTATUS_ERROR;

	free_pages((unsigned long)mem->virtaddx, mem->order);
	kfree(mem);
	
#ifdef MEMTEST
	DPD("free_pages: [%p]\n", mem->virtaddx);
	DPD("kfree: [%p]\n", mem);
#endif
	
	return CTSTATUS_SUCCESS;
}


int osListAttach(struct sblive_list ** head, struct sblive_list * n)
{
	unsigned long flags;

	if (head == NULL)
	  return CTSTATUS_ERROR;

	spin_lock_irqsave(&sblive_spinlock, flags);

	n->next = NULL;

	if (*head != NULL) 
	{
		struct sblive_list *t = *head;

		while (t->next != NULL)
		  t = t->next;
		t->next = n;
	} else
	  *head = n;

	spin_unlock_irqrestore(&sblive_spinlock, flags);

	return CTSTATUS_SUCCESS;
}


int osListRemove(struct sblive_list ** head, struct sblive_list * dead)
{
	u32 status = CTSTATUS_SUCCESS;
	unsigned long flags;

	if (head == NULL)
	  return CTSTATUS_ERROR;

	spin_lock_irqsave(&sblive_spinlock, flags);

	if (*head != dead) 
	{
		struct sblive_list *t = *head;

		while (t != NULL && t->next != dead)
		  t = t->next;

		if (t != NULL) 
		{
			t->next = dead->next;
			dead->next = NULL;
		} else
		  status = CTSTATUS_ERROR;
	} else
	  *head = dead->next;

	spin_unlock_irqrestore(&sblive_spinlock, flags);

	return status;
}


struct sblive_list *osListGetNext(struct sblive_list * head, struct sblive_list * curr)
{
	if (curr == NULL)
	  return head;

	return curr->next;
}

/* DPC Handling */

typedef struct 
{
	struct tq_struct *task;
	CALLBACKFN callback;
	u32 refdata;
	u32 param;
	int is_active;
} callback_handle;

void task_queue_callback(void *data)
{
	callback_handle *cbk = data;

	cbk->callback(cbk->refdata, cbk->param, 0);
	kfree(cbk->task);
	kfree(cbk);
}

int osScheduleDPC(PCTDPC dpc)
{
	callback_handle *cbk;
	struct tq_struct *task;

	task = kmalloc(sizeof(struct tq_struct), GFP_ATOMIC);
	cbk = kmalloc(sizeof(cbk), GFP_ATOMIC);

	task->next = NULL;
	task->sync = 0;
	task->routine = task_queue_callback;
	task->data = cbk;

	cbk->task = task;
	cbk->callback = dpc->DPCCallBackFn;
	cbk->refdata = dpc->refdata;
	cbk->param = dpc->param;

	queue_task(task, &tq_immediate);
	mark_bh(IMMEDIATE_BH);

	return CTSTATUS_SUCCESS;
}

