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

ASI Linux Hardware Programming Interface (HPI) Wrapper
This module contains the main HPI entry point HPI_Message which uses ioctl
to pass the message to the driver.

Copyright (C) 1997-2017 AudioScience, Inc. All rights reserved.

This software is provided 'as-is', without any express or implied warranty.
In no event will AudioScience Inc. be held liable for any damages arising
from the use of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:

1. The origin of this software must not be misrepresented; you must not
   claim that you wrote the original software. If you use this software
   in a product, an acknowledgment in the product documentation would be
   appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
   misrepresented as being the original software.
3. This copyright notice and list of conditions may not be altered or removed
   from any source distribution.

AudioScience, Inc. <support@audioscience.com>

( This license is GPL compatible see http://www.gnu.org/licenses/license-list.html#GPLCompatibleLicenses )

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

#define SOURCEFILE_NAME "hpiwrap.c"

#include <unistd.h>		/* for close */
#include <fcntl.h>		/* for open */
#include <sys/ioctl.h>		/* for ioctl */
#include <stdio.h>

#include "hpi_internal.h"
#include "hpi_version.h"
#include "hpidebug.h"
#include "hpimsginit.h"

#ifdef HPI_BUILD_HPIDUMP
#include "hpidump.c"
#else
static void hpi_dump_open(void) {};
static void hpi_dump_close(void) {};
static void hpi_dump_message(struct hpi_message *phm, struct hpi_response *phr) {};
static void hpi_dump_response(struct hpi_message *phm, struct hpi_response *phr) {};
#endif

static int fd = 0;

/** List of device file names to probe for HPI ioctl
Covers both normal HPI device, and ALSA hwdep devices.
Note that each ALSA HPI hwdep device is equivalent to the standard HPI device,
and gives access to all HPI cards, not just the ALSA card it is associated with.
*/
static char *devnames[] = {
	NULL, /* Will be replaced by LIBHPI_DEV env pointer */
	"/dev/asihpi", /* Normal HPI device */
	"/dev/snd/hwC0D0", /* ALSA driver hwdep, may support HPI ioctl */
	"/dev/snd/hwC1D0",
	"/dev/snd/hwC2D0",
	"/dev/snd/hwC3D0",
	"/dev/snd/hwC4D0",
};

#if defined HPI_BUILD_MULTIINTERFACE
	#if defined HPI_BUILD_SSX2
		#include "hpissx2.h"
		#define HPI_MESSAGE_LOCAL_ADAPTERS(m,r) HPI_MESSAGE_LOWER_LAYER(m,r,NULL)
	#else
		void HPI_MessageIoctl(HPI_MESSAGE *phm, HPI_RESPONSE *phr);
		#define HPI_MESSAGE_LOCAL_ADAPTERS(m,r) HPI_MessageIoctl(m,r)
	#endif
#else
	#define HPI_MESSAGE_LOCAL_ADAPTERS(m,r) HPI_Message(m,r)
#endif

#if defined HPI_BUILD_MULTIINTERFACE
uint16_t HPI_DriverOpenIoctl(void)
#else
uint16_t HPI_DriverOpen(void)
#endif
{
	char * env;
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	hpi_err_t err = 0;
	uint32_t version;
	int i;

	env = getenv("LIBHPI_DEBUG_LEVEL");
	if ((env) && (*env >= '0') && (*env <= '9'))
		hpiDebugLevel = *env - '0';

	devnames[0] = getenv("LIBHPI_DEV");

	/* Probe the list of device files */
	for (i = 0 ; i < ARRAY_SIZE(devnames); i++) {
		if (!devnames[i])
			continue;
		fd = open(devnames[i], O_RDWR);
		if (fd < 0) // file couldn't be opened
			continue;

		HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_SUBSYSTEM, HPI_SUBSYS_GET_VERSION);
		HPI_MESSAGE_LOCAL_ADAPTERS(&hm, &hr);
		version = hr.u.s.dwData;
		err = hr.wError;
		if (err) {
			close(fd);
			continue;
		}
		break;
	}

	if (err || fd < 0) {
		if (hpiDebugLevel)
			perror("HPI_DriverOpen error");
		return 0;
	}

	HPI_DEBUG_LOG3(INFO,
		"lib version 0x%05X opened driver version 0x%05X at %s\n",
		HPI_VER, version, devnames[i]);

	env = getenv("LIBHPI_DEBUG_DUMP");
	if (env)
		hpi_dump_open();

	return 1;  // Success! Note that this is not an error code
}
#if defined HPI_BUILD_MULTIINTERFACE
void HPI_MessageIoctl(
#else
void HPI_Message(
#endif
		 struct hpi_message *phm,
		 struct hpi_response *phr)
{
	int status;
	struct hpi_ioctl_linux hpi_ioctl_data;

	/* Check that device opened...  */
	if ((fd == 0 && !HPI_DriverOpen()) || ( fd < 0)) {
		phr->wError=HPI_ERROR_PROCESSING_MESSAGE; // HPI_ERROR_DRIVER_NOT_OPEN???
		return;
	}

	hpi_ioctl_data.phm = phm;
	hpi_ioctl_data.phr = phr;
	HPI_DEBUG_MESSAGE (DEBUG, phm);

	hpi_dump_message(phm, phr);
	status = ioctl(fd, HPI_IOCTL_LINUX,
		(unsigned long)&hpi_ioctl_data);
	if (status < 0) {
		if (hpiDebugLevel)
			perror("HPI_Message error");
		HPI_DEBUG_MESSAGE(ERROR, phm);
		phr->wError=HPI_ERROR_PROCESSING_MESSAGE;
	}
	hpi_dump_response(phm, phr);
	HPI_DEBUG_RESPONSE(phr);
}

#if defined HPI_BUILD_MULTIINTERFACE
void HPI_DriverCloseIoctl(void)
#else
void HPI_DriverClose(void)
#endif
{
	int status;

	/* Check that device opened...  */
	if (fd <= 0)
		return;

	status = close(fd);
	if (status < 0 && hpiDebugLevel)
		perror("HPI_DriverClose error");
	fd = -1;
	hpi_dump_close();
}
