/******************************************************************************
    AudioScience HPI driver
    HPI Operating System function implementation for Linux

    Copyright (C) 1997-2017  AudioScience Inc. <support@audioscience.com>

    This program is free software; you can redistribute it and/or modify
    it under the terms of version 2 of the GNU General Public License as
    published by the Free Software Foundation;

    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.

******************************************************************************/
#define SOURCEFILE_NAME "hpios.c"
#include "hpi_internal.h"
#include "hpidebug.h"
#include <linux/delay.h>
#include <linux/sched.h>

#ifndef HPI_BUILD_SANITISE
#ifndef  __KERNEL__
#error Using kernel source for userspace build
#endif
#endif

void HpiOs_DelayMicroSeconds(u32 dwNumMicroSec)
{
	if ((usecs_to_jiffies(dwNumMicroSec) > 1) && !in_interrupt()) {
		/* MUST NOT SCHEDULE IN INTERRUPT CONTEXT! */
		schedule_timeout_uninterruptible(usecs_to_jiffies
			(dwNumMicroSec));
	} else if (dwNumMicroSec <= 2000)
		udelay(dwNumMicroSec);
	else
		mdelay(dwNumMicroSec / 1000);

}

/** Allocate an area of locked memory for bus master DMA operations.

If allocation fails, return NULL, *pMemArea.size = 0, *pMemArea.vaddr = NULL
Otherwise *pMemArea is initialised. *pMemArea valid.
*/
void * HpiOs_LockedMem_Alloc(
	struct consistent_dma_area *pMemArea,
	u32 size,
	struct hpi_os_adapter *pa,
	uint32_t *pPhysicalAddress
)
{
	struct pci_dev *pdev = pa->pci_dev;

	pMemArea->vaddr = dma_alloc_coherent(&pdev->dev, size,
			&pMemArea->dma_handle, GFP_DMA32 | GFP_KERNEL);

	if (pMemArea->vaddr) {
		HPI_DEBUG_LOG3(DEBUG, "Allocated %d bytes, dma 0x%x vma %p\n",
			size,
			(unsigned int)pMemArea->dma_handle, pMemArea->vaddr);
		pMemArea->pdev = &pdev->dev;
		pMemArea->size = size;
	} else {
		HPI_DEBUG_LOG1(WARNING,
			"failed to allocate %d bytes locked memory\n",
			size);
		pMemArea->size = 0;
	}
	*pPhysicalAddress = pMemArea->dma_handle;
	return pMemArea->vaddr;
}

void HpiOs_LockedMem_Free(struct consistent_dma_area *pMemArea)
{
	if (pMemArea->size && pMemArea->vaddr) {
		dma_free_coherent(pMemArea->pdev, pMemArea->size,
			pMemArea->vaddr, pMemArea->dma_handle);
		HPI_DEBUG_LOG3(DEBUG, "Freed %lu bytes, dma 0x%x vma %p\n",
			(unsigned long)pMemArea->size,
			(unsigned int)pMemArea->dma_handle, pMemArea->vaddr);
		pMemArea->size = 0;
		pMemArea->vaddr = NULL;
	}
}


hpi_err_t HpiOs_Event_WaitWithTimeout(struct completion *c, uint32_t timeout_us)
{
	unsigned long j = usecs_to_jiffies(timeout_us);
	long r = wait_for_completion_interruptible_timeout(c, j);

	if (r > 0) // result is jiffies remaining, at least 1
		return 0;
	else if (r == 0)
		return HPI_ERROR_WAIT_TIMEOUT;
	else
		return HPI_ERROR_WAIT_INTERRUPTED;
}

/* Wait for event with default two second timeout */
hpi_err_t HpiOs_Event_Wait(struct completion *c)
{
	return HpiOs_Event_WaitWithTimeout(c, 2000000);
}

