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

    AudioScience HPI driver
    HPI Operating System Specific macros for Linux Kernel driver

    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.

******************************************************************************/
#ifndef _HPIOS_H_
#define _HPIOS_H_

#undef HPI_OS_LINUX_KERNEL
#define HPI_OS_LINUX_KERNEL

#define HPI_OS_DEFINED
#define HPI_BUILD_KERNEL_MODE

#ifdef HPI_OS_LINUX
#error Using kernel header for userspace build!
#endif

#ifndef HPI_BUILD_INCLUDE_INTERNAL
#define HPI_BUILD_INCLUDE_INTERNAL
#endif

#ifndef HPI_BUILD_SANITISE
#define HPI_BUILD_REASSIGN_DUPLICATE_ADAPTER_IDX

#ifdef MODVERSIONS
#include <linux/modversions.h>
#endif

#include <linux/version.h>
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 5))
#include <asm/system.h>
#endif

#define HPI_MIN_KERNEL_VERSION KERNEL_VERSION(3, 2, 0)

#if LINUX_VERSION_CODE < HPI_MIN_KERNEL_VERSION
#warning "Minimum supported kernel version is 3.2.0"
#endif

#endif

#include <linux/io.h>
#include <linux/ioctl.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/device.h>
#include <linux/firmware.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/msi.h>
#include <linux/mutex.h>

#ifndef HPI_BUILD_SANITISE
#define C99(x) x

#define INLINE inline

#if __GNUC__ >= 3
#define HPI_DEPRECATED __attribute__((deprecated))
#endif

#define __pack__ __attribute__ ((packed))

#ifndef __user
#define __user
#endif
#ifndef __iomem
#define __iomem
#endif
#endif

#define HPI_NO_OS_FILE_OPS

#ifndef HPI_BUILD_SANITISE
#ifdef CONFIG_64BIT
#define HPI_BUILD_64BIT
#endif
#endif

/** Details of a memory area allocated with  pci_alloc_consistent
Need all info for parameters to pci_free_consistent
*/
struct consistent_dma_area {
	struct device *pdev;
	/* looks like dma-mapping dma_devres ?!*/
	size_t size;
	void *vaddr;
	dma_addr_t dma_handle;
};


#ifndef HPI_BUILD_SANITISE

typedef struct consistent_dma_area HpiOs_LockedMem_Handle;

#define DRIVER_DATA_TYPE kernel_ulong_t

/* Memory read/write */
#define HPIOS_MEMWRITE32(a, d) iowrite32((d), (a))
#define HPIOS_MEMREAD32(a) ioread32((a))
#define HPIOS_MEMWRITEBLK32(to_io, from_buf, nwords) iowrite32_rep(to_io, from_buf, nwords)
#define HPIOS_MEMREADBLK32(from_io, to_buf, nwords)  ioread32_rep(from_io, to_buf, nwords)

static inline void *HpiOs_MemAlloc(u32 dwSize)
{
	return kmalloc(dwSize, GFP_KERNEL);
}

static inline void *HpiOs_MemAllocZero(u32 dwSize)
{
	return kzalloc(dwSize, GFP_KERNEL);
}

static inline void *HpiOs_MemAllocZeroArray(size_t n, size_t size)
{
	return kcalloc(n, size, GFP_KERNEL);
}

static inline void HpiOs_MemFree(void *ptr)
{
	kfree(ptr);
}

#define HPIOS_DEBUG_PRINTF printk

#if defined(ALSA_BUILD) || defined(KERNEL_ALSA_BUILD)
#define HPI_BUILD_ALSA
#define HPI_BUILD_DEBUG
#define HPI_BUILD_INCLUDE_HPI6000
#define HPI_BUILD_INCLUDE_HPI6205
#define HPI_BUILD_INCLUDE_HPI6700
#endif
#define HPI_BUILD_NO_STREAM_WAIT

#define WMB wmb
#define RMB rmb

/* hp is a struct hpi_pci * */
#define HPIOS_PCI_SUBSYS_DEVICE(hp) (hp)->os.pci_dev->subsystem_device

#endif

static inline void* HpiOs_LockedMem_GetVirtAddr(
	struct consistent_dma_area *LockedMemHandle
)
{
	return LockedMemHandle->vaddr;
}

/* Check that vaddr is within the given memory area */
static inline u16 HpiOs_LockedMem_ValidAddr(
	struct consistent_dma_area *LockedMemHandle,
	void  *vaddr
)
{
    return LockedMemHandle &&
	   LockedMemHandle->vaddr &&
	    ((u8 *)vaddr - (u8 *)(LockedMemHandle->vaddr)
	    < (LockedMemHandle->size - 4));
}

struct hpi_ioctl_linux {
	void __user *phm;
	void __user *phr;
};

/* Conflict?: H is already used by a number of drivers hid, bluetooth hci,
   and some sound drivers sb16, hdsp, emu10k. AFAIK 0xFC is ununsed command
*/
#define HPI_IOCTL_LINUX _IOWR('H', 0xFC, struct hpi_ioctl_linux)

#define HPI_DEBUG_FLAG_ERROR   KERN_ERR
#define HPI_DEBUG_FLAG_WARNING KERN_WARNING
#define HPI_DEBUG_FLAG_NOTICE  KERN_NOTICE
#define HPI_DEBUG_FLAG_INFO    KERN_INFO
#define HPI_DEBUG_FLAG_DEBUG   KERN_DEBUG
#define HPI_DEBUG_FLAG_VERBOSE KERN_DEBUG	/* kernel has no verbose */

#if !defined HPI_BUILD_DEBUG && defined CONFIG_SND_DEBUG
#define HPI_BUILD_DEBUG
#endif

#define HPI_LOCKING

#ifndef HPI_BUILD_SANITISE

#define HpiOs_Mutex struct mutex

#define HpiOs_Mutex_Init(m) mutex_init(m)
#define HpiOs_Mutex_Lock(m) mutex_lock(m)
#define HpiOs_Mutex_Unlock(m) mutex_unlock(m)

#endif

#ifdef HPI_BUILD_ALSA

struct hpios_spinlock {
	spinlock_t lock;
	unsigned long flags;
};

#define HpiOs_SpinLock struct hpios_spinlock

#define HpiOs_SpinLock_Init(obj) spin_lock_init(&(obj)->lock)

static inline void HpiOs_SpinLock_Lock(HpiOs_SpinLock *l)
{
	spin_lock_irqsave(&((l)->lock), (l)->flags);
}

static inline void HpiOs_SpinLock_Unlock(HpiOs_SpinLock *l)
{
	spin_unlock_irqrestore(&((l)->lock), (l)->flags);
}

#else

#define HpiOs_SpinLock HpiOs_Mutex
#define HpiOs_SpinLock_Init HpiOs_Mutex_Init
#define HpiOs_SpinLock_Lock HpiOs_Mutex_Lock
#define HpiOs_SpinLock_Unlock HpiOs_Mutex_Unlock

#endif

#ifndef HPI_BUILD_SANITISE

#define hpios_event struct completion
#define HpiOs_Event_Init(c) init_completion(c)
#define HpiOs_Event_Set(c) complete(c)
#define HpiOs_Event_Clear(c)

#endif

hpi_err_t HpiOs_Event_Wait(struct completion *c);
hpi_err_t HpiOs_Event_WaitWithTimeout(struct completion *c, uint32_t timeout_us);

struct snd_card;
struct hpi_adapter_obj;

/** Linux-specific part of adapter info */
struct hpi_os_adapter {
	struct pci_dev *pci_dev;
	unsigned int num_cards;
	struct snd_card *snd_card[32];

	int irq;
	void (*irq_thread)(struct hpi_adapter_obj *);

	/* mutex prevents contention for one card
	   between multiple user programs (via ioctl) */
	struct mutex mutex;
	char *p_buffer;
	size_t buffer_size;
};

#endif
