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

  Control tuners on ASI87xx, ASI8821 and ASI892x cards

  Usage: asihpitune --help

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 )

******************************************************************************/
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#ifdef HPI_OS_LINUX
#include <unistd.h>
#endif
#include <getopt.h>
#ifdef __APPLE__
#include <hpi/hpi.h>
#include <hpi/hpirds.h>
#include <hpi/hpidebug.h>
#else
#include <hpi.h>
#include <hpidebug.h>
#endif

#define verbose_printf if (opt_verbose) printf

#define nanosec 1000000

// local protos
static void HandleError(hpi_err_t err);
static void HandleErrorComment(const char *comment, hpi_err_t err);
static void HandleErrorCommentContinue(const char *comment, hpi_err_t err);
static int getch(void);
static void asi_sleep(unsigned int milliseconds);

/* NOTE: indices must match HPI defines for each band */
static char *band_names[HPI_TUNER_BAND_LAST + 1] = {
	"invalid",
	"AM",
	"FM",
	"NTSC_M",
	"FM2",
	"Aux",
	"PAL_BG",
	"PAL_I",
	"PAL_DK",
	"Secam_L",
	"DAB"
};

static int keep_looping = 1;

/* Option variables */
static uint16_t opt_adapter_index = 0;
static uint16_t opt_tuner_index = 0;
static uint16_t opt_band = 0;
static uint32_t opt_tuner_frequency = 0;
static uint32_t opt_dab_poll = 0;
static short opt_rss_mode = -1;
static short opt_rds_mode = -1;
static int opt_query_tuner_caps = 0;
static int opt_query_tuner_settings = 0;
static int opt_verbose = 0;
static int opt_rds_poll_ms = 1000;
static int opt_rds_poll_loops = 0;
static short opt_mux_index = -1;
static uint32_t opt_serv = 0;
static uint16_t opt_comp = 0;

static struct option long_options[] = {
	{"adapter", required_argument, 0, 'a'},
	{"band", required_argument, 0, 'b'},
	{"query-caps", no_argument, 0, 'c'},
	{"frequency", required_argument, 0, 'f'},
	{"rss-mode", required_argument, 0, 'm'},
	{"rds-mode", required_argument, 0, 'n'},
	{"rds-poll-ms", required_argument, 0, 'p'},
	{"rds", optional_argument, 0, 'r'},
	{"settings", no_argument, 0, 's'},
	{"tuner", required_argument, 0, 't'},
	{"verbose", no_argument, 0, 'v'},
	{"mux", required_argument, 0, 'x'},
	{"audio-service", no_argument, 0, 'y'},
	{"help", no_argument, 0, 'h'},
	{0, 0, 0, 0}
};

static const char *short_options = "a:b:cf:m:n:p:r::t:st:vx:y:h";

static const char *option_help[] = {
	"<adapter number> to test, default is 0.",
	"<band> see below for valid options",
	"Query tuner capabilities.",
	"<freq in kHz> tuner frequency",
	"<mode> Set tuner RSS mode.",
	"<mode> Set tuner RDS mode.",
	"<milliseconds> Set RDS polling interval. Default 1000",
	"[optional loop count] Reads RDS data from the tuner. Default 100 loops",
	"Read back tuner settings.",
	"<tuner index> to query or set, default is 0.",
	"Enable verbose output.",
	"<n> Set audio out mux to tuner",
	"set DAB or HD audio service channel #",
	"Show this text."
};
static void help(
	void
)
{
	int i = 0;
	printf("\nUsage - asihpitune [options]\n");
	while (long_options[i].name != 0) {
		printf("--%s -%c %s\n",
			long_options[i].name,
			(char)(long_options[i].val), option_help[i]);
		i++;
	}
	printf("Valid values for -b option are ");
	for (i = 1; i <= HPI_TUNER_BAND_LAST; i++)
		printf("%s, ", band_names[i]);
	printf("\n");
	exit(0);
}

static void parse_options(
	int argc,
	char *argv[]
)
{
	int c;
    /*********** Parse the command line options ***************/
	while (1) {
		// int this_option_optind = optind ? optind : 1;
		int option_index = 0;

		c = getopt_long(argc, argv, short_options,
			long_options, &option_index);
		if (c == -1)
			break;

		switch (c) {
		case 0:
			printf("option %s", long_options[option_index].name);
			if (optarg)
				printf(" with arg %s", optarg);
			printf("\n");
			break;
		case 'a':
			opt_adapter_index = atoi(optarg);
			break;
		case 'b':
			{
				int i;
				for (i = 1; i <= HPI_TUNER_BAND_LAST; i++)
					if (strcmp(band_names[i], optarg) ==
						0)
						opt_band = i;
				if (!opt_band) {
					printf("\n****** Invalid band: %s ******\n", optarg);
					help();
				}

				opt_query_tuner_settings = 1;
			}
			break;
		case 'c':
			opt_query_tuner_caps = 1;
			break;
		case 'f':
			opt_tuner_frequency = atoi(optarg);
			opt_query_tuner_settings = 1;
			break;
		case 'm':
			opt_rss_mode = atoi(optarg);
			opt_query_tuner_settings = 1;
			break;
		case 'n':
			opt_rds_mode = atoi(optarg);
			opt_query_tuner_settings = 1;
			break;
		case 'p':
			opt_rds_poll_ms = atoi(optarg);
			if (!opt_rds_poll_loops)
				opt_rds_poll_loops = 100;
			break;
		case 'r':
			if (optarg)
				opt_rds_poll_loops = atoi(optarg);
			else
				opt_rds_poll_loops = 100;
			break;
		case 's':
			opt_query_tuner_settings = 1;
			break;
		case 't':
			opt_tuner_index = atoi(optarg);
			break;
		case 'v':
			opt_verbose = 1;
			break;
		case 'x':
			opt_mux_index = atoi(optarg);
			break;
		case 'y':
			opt_dab_poll = atoi(optarg);
			break;
		case '?':
		case 'h':
			help();
			break;

		default:
			printf("?? getopt returned character code 0%o ??\n",
				c);
		}
	}

	if (optind < argc) {
		char *endptr;

		// printf ("non-option ARGV-elements: \n");
		while ((optind < argc))  {
			opt_serv = strtoll(argv[optind], &endptr, 0);
			opt_comp = strtol(endptr+1, NULL, 0);
			//printf("argument %d : %s -> 0x%X:0x%X\n",
			//	optind, argv[optind], opt_serv, opt_comp);
			optind++;
		}
	}

}

static hpi_err_t Tuner_GetDabComponentInfo(
	hpi_handle_t hTuner,
	uint32_t service_id,
	uint16_t component_id,
	struct hpi_dab_component_info *component_info
)
{
	int i;
	int t = 20;
	hpi_err_t err;

	for (i = 0; i < 20; i++) {
		err = HPI_Tuner_GetDabComponentInfo(hTuner,
			service_id, component_id, component_info);

			if (err == HPI_ERROR_CONTROL_NOT_READY) {
				// printf("Iteration %d wait %d\n", i, t);
				asi_sleep(t);
				t += 20;
			} else {
				break;
			}
	}

	return err;
}


static hpi_err_t Tuner_SetDabService(
	hpi_handle_t hTuner,
	uint32_t service_id,
	uint16_t component_id,
	int start
)
{
	int i;
	int t = 20;
	hpi_err_t err;

	err = HPI_Tuner_SetDabService(hTuner,
		service_id, component_id, start);

	if (err != HPI_ERROR_CONTROL_NOT_READY)
		return err;

	asi_sleep(t);

	for (i = 0; i < 10; i++) {
		err = HPI_Tuner_SetDabService(hTuner,
			service_id, component_id, start);

			if (err == HPI_ERROR_CONTROL_NOT_READY) {
				//printf("Iteration %d wait %d\n", i, t);
				asi_sleep(t);
				t += 20;
			} else
				break;
	}

	return err;
}


static hpi_err_t PrintDabServiceInfo(hpi_handle_t hTuner, int idx)
{
	struct hpi_dab_service_info dsi;
	int c;
	uint32_t sid;
	hpi_err_t err;
	char label[DAB_LABEL_LEN + 1];

	err = HPI_Tuner_GetDabServiceInfo(hTuner, idx, &dsi);
	if (err)
		return err;

	sid = dsi.id;
	memcpy(label, dsi.label, DAB_LABEL_LEN);
	label[DAB_LABEL_LEN] = 0;

	printf("Service ID 0x%08X, name %s\n", sid, label);

	for (c = 0; c < dsi.num_components; c++) {
		uint16_t cid;
		struct hpi_dab_component_info dci;

		cid = dsi.components[c].id;

		err = Tuner_GetDabComponentInfo(hTuner,
			sid, cid, &dci);
		if (err)
			break;

		memcpy(label, dci.label, DAB_LABEL_LEN);
		label[DAB_LABEL_LEN] = 0;
		printf("\tComponent ID 0x%04X, name '%s'\n", cid, label);
	}

	return err;
}

static void ProcessPacket(void* buffer, size_t data_len)
{
	struct hpi_service_data_packet_info *header = buffer;
	uint32_t sid, cid;
	uint16_t packet_size;

	sid = header->serv_id;
	cid = header->comp_id;
	packet_size = header->byte_count;

	printf("Received %4d byte packet from 0x%08X:0x%04X\n", packet_size, sid, cid);
}

static void poll_dab(hpi_handle_t hTuner)
{
	hpi_err_t err;
	char buffer[8192];
	size_t packet_len;
	int next_poll_ms;

	while (keep_looping && opt_dab_poll) {
		err = HPI_Tuner_GetDabDataPacket(hTuner,
			buffer, sizeof(buffer), &packet_len, &next_poll_ms);

		if (!err) {
			ProcessPacket(buffer, packet_len);
			continue;
		} else if (err != HPI_ERROR_CONTROL_NOT_READY) {
			break;
		}
		asi_sleep(next_poll_ms);
		opt_dab_poll--;
	}
}

static void query_tuner_caps(hpi_handle_t hMixer, hpi_handle_t hTuner, int opt_tuner_index)
{
	hpi_hsubsys_t *hSubSys = NULL;
	hpi_err_t err;
	uint32_t dwIndex;
	uint16_t wSetting;
	uint32_t dwStart, dwEnd, dwStep;

	printf("\nTuner %d supports\n", opt_tuner_index);
	for (dwIndex = 0; dwIndex < 100; dwIndex++) {
		err = HPI_Tuner_QueryBand(hSubSys, hTuner, dwIndex, &wSetting);
		if (err)
			break;
		if ((wSetting == 0) || (wSetting > HPI_TUNER_BAND_LAST)) {
			printf("Unknown band type %d\n", wSetting);
			continue;
		}

		printf("%i %s : ", dwIndex, band_names[wSetting]);

		err = HPI_Tuner_QueryFrequency
			(hSubSys, hTuner, 0, wSetting,
			&dwStart);
		err = HPI_Tuner_QueryFrequency
			(hSubSys, hTuner, 1, wSetting,
			&dwEnd);
		err = HPI_Tuner_QueryFrequency
			(hSubSys, hTuner, 2, wSetting,
			&dwStep);
		printf("from %ikHz to %ikHz step %ikHz\n", dwStart, dwEnd, dwStep);
	}
}

static void query_tuner_settings(hpi_handle_t hMixer, hpi_handle_t hTuner, int opt_tuner_index)
{
	hpi_hsubsys_t *hSubSys = NULL;
	hpi_handle_t hMux;
	hpi_err_t err;
	uint16_t nTunerBand = 0;
	short int nRfLevel = 0;
	uint32_t opt_tuner_frequency = 0;
	uint32_t wMode = 0;
	uint16_t wMuxIndex = 0;
	uint16_t wStatus = 0;
	uint16_t wStatusMask = 0;
	uint32_t nAudioServices = 0;
	uint32_t nCurrentService = 0;
	uint32_t sid;
	uint16_t cid;
	int i = 0;

	err = HPI_MixerGetControl(hSubSys, hMixer, 0, 0,
		HPI_DESTNODE_LINEOUT, 0, HPI_CONTROL_MULTIPLEXER, &hMux);
	HandleErrorComment("Get Mux", err);

	err = HPI_Tuner_GetBand(hSubSys, hTuner, &nTunerBand);
	HandleErrorComment("HPI_Tuner_GetBand", err);

	err = HPI_Tuner_GetFrequency(hSubSys, hTuner, &opt_tuner_frequency);
	HandleErrorComment("HPI_Tuner_GetFrequency", err);

	err = HPI_Tuner_GetRFLevel(hSubSys, hTuner, &nRfLevel);
	HandleErrorComment("HPI_Tuner_GetRFLevel", err);

	err = HPI_Multiplexer_GetSource(hSubSys, hMux, 0, &wMuxIndex);
	HandleErrorComment("HPI_Multiplexer_GetSource", err);

	err = HPI_Tuner_GetStatus(hSubSys, hTuner, &wStatusMask, &wStatus);
	HandleErrorComment("HPI_Tuner_GetStatus", err);

	printf("Tuner %d:\nBand=%s band, frequency=%i. Mode=%d.  RF level=%3.2fdBuV. Mux=%d\n",
			opt_tuner_index, band_names[nTunerBand],
			opt_tuner_frequency, wMode, nRfLevel / 100.0, wMuxIndex);
	printf("Status mask=%08X, value=%08X\n", wStatusMask, wStatus);

	if (nTunerBand == HPI_TUNER_BAND_DAB) {
		char multiplex_name[20];
		char audio_info[80];
		printf("\nDAB info:\n");

		// wait up to 20s for service list
		for (i = 0; i < 20; i++) {
			err = HPI_Tuner_GetDabAudioServiceCount(hSubSys, hTuner,
					&nCurrentService, &nAudioServices);

			if (err)
				break;

			if (nAudioServices)
				break;

			asi_sleep(1000);
		}

		err = HPI_Tuner_GetDabMultiplexName(hSubSys, hTuner,
					multiplex_name, sizeof(multiplex_name));
		printf("Multiplex name: %s\n", multiplex_name );

		printf("Number of Services=%d\n", nAudioServices);

		if (opt_verbose)
			for (i = 0 ; i < nAudioServices ; i++)
				PrintDabServiceInfo(hTuner, i);

		err = HPI_Tuner_GetDabService(hTuner, &sid, &cid);

		err = HPI_Tuner_GetDabAudioInfo(hSubSys, hTuner, audio_info, sizeof(audio_info));
		printf("Current Audio Service=0x%08X:0x%04X - %s\n", sid, cid, audio_info);

		printf("\n");
	}

	err = HPI_Tuner_GetMode(hSubSys, hTuner, HPI_TUNER_MODE_RSS, &wMode);

	if (err == HPI_ERROR_INVALID_CONTROL_ATTRIBUTE) {
		verbose_printf("Tuner does not have MODEs.\n");
		wMode = 0;
	} else if (err ==  HPI_ERROR_INVALID_CONTROL_VALUE) {
		verbose_printf("TUNER_MODE_RSS not supported\n");
		wMode = 0;
	} else {
		HandleErrorComment("HPI_Tuner_GetMode", err);
		printf("TUNER_MODE_RSS %d\n", wMode);
	}

	if (err != HPI_ERROR_INVALID_CONTROL_ATTRIBUTE) {
		err = HPI_Tuner_GetMode(hSubSys, hTuner,
			HPI_TUNER_MODE_RDS, &wMode);
		if ( err ==  HPI_ERROR_INVALID_CONTROL_VALUE) {
			verbose_printf("TUNER_MODE_RDS not supported\n");
			wMode = 0;
		} else {
			HandleErrorComment("HPI_Tuner_GetMode", err);
			printf("TUNER_MODE_RDS %d\n", wMode);
		}
	}
}

static void poll_rds(hpi_handle_t hMixer, hpi_handle_t hTuner, int opt_tuner_index)
{
	hpi_hsubsys_t *hSubSys = NULL;
	hpi_err_t err;
	hpi_handle_t hPad;
	unsigned int nRDSloopcount = opt_rds_poll_loops;
	uint32_t dwPTY = 0, dwPI = 0;
	hpi_err_t ecn = 0, ec = 0, epi = 0;

	err = HPI_MixerGetControl(hSubSys, hMixer, HPI_SOURCENODE_TUNER,
		opt_tuner_index, 0, 0, HPI_CONTROL_TUNER, &hTuner);
	HandleErrorComment("Get Tuner Control", err);

	err = HPI_MixerGetControl(hSubSys, hMixer, HPI_SOURCENODE_TUNER,
		opt_tuner_index, 0, 0, HPI_CONTROL_PAD, &hPad);
	HandleErrorComment("HPI_MixerGetControl (HPI_CONTROL_PAD)", err);

	if (err)
		return;

	while (--nRDSloopcount) {
		char szPAD[HPI_PAD_COMMENT_LEN];

		printf("\nLoop - %d\n", nRDSloopcount);
		if (opt_verbose) {
			/* RDS data group of (4 blocks each of 16 bits)
			 * plus 4 chars of error information
			 */
			unsigned char aRDS[12];

			err = HPI_Tuner_GetRDS(hSubSys, hTuner, (char *)aRDS);
			if (!err) {
				int i;

				printf("RDS block data ");
				for (i = 0; i < 12; i++)
					printf("%u ", aRDS[i]);
				printf("\n");
				printf("PI from        ^---^ = 0x%X\n",
				       aRDS[1] << 8 | aRDS[0]);
			} else {
				HandleErrorCommentContinue("HPI_Tuner_GetRDS", err);
			}
		}
		/*
		   Note, depending on whether the tuner supports HDRadio PAD information
		   or FM RDS information, different attributes may be valid.
		 */
		if (!ecn) {
			ecn = HPI_PAD_GetChannelName(hSubSys, hPad,
				szPAD, sizeof(szPAD));
			HandleErrorCommentContinue
				("HPI_PAD_GetChannelName", ecn);
			if (!ecn)
				printf("ChannelName : %s\n", szPAD);
		}

		err = HPI_PAD_GetArtist(hSubSys, hPad,
			szPAD, sizeof(szPAD));
		HandleErrorComment("HPI_PAD_GetArtist", err);
		if (!err)
			printf("Artist/RT : %s\n", szPAD);

		err = HPI_PAD_GetTitle(hSubSys, hPad,
			szPAD, sizeof(szPAD));
		HandleErrorComment("HPI_PAD_GetTitle", err);
		if (!err)
			printf("Title/PS : %s\n", szPAD);

		if (!ec) {
			ec = HPI_PAD_GetComment(hSubSys, hPad,
				szPAD, sizeof(szPAD));
			HandleErrorCommentContinue
				("HPI_PAD_GetComment", ec);
			if (!ec)
				printf("Comment/PS : %s\n", szPAD);
		}

		err = HPI_PAD_GetProgramType(hSubSys, hPad, &dwPTY);
		HandleErrorComment("HPI_PAD_GetProgramType", err);
		if (!err) {
			err = HPI_PAD_GetProgramTypeString(hSubSys,
				hPad, HPI_RDS_DATATYPE_RBDS, dwPTY,
				szPAD, sizeof(szPAD));
			if (!err)
				printf("PTY : %d, %s\n", dwPTY,
					szPAD);
		}

		if (!epi) {
			epi = HPI_PAD_GetRdsPI(hSubSys, hPad, &dwPI);
			HandleErrorCommentContinue("HPI_PAD_GetRdsPI",
				epi);
			if (!epi)
				printf("PI : 0x%X\n", dwPI);
		}

		asi_sleep(opt_rds_poll_ms);
	}
}

static void sigHandler(int sig)
{
	if (keep_looping)
		keep_looping = 0;
	else
		exit(0);
}

/************************************** MAIN ***********************/
int main(
	int argc,
	char *argv[]
)
{
	hpi_hsubsys_t *hSubSys;
	hpi_err_t err = 0;		// HPI error
	uint32_t dwVersion = 0;
	int numAdapters = 0;
	uint16_t wVersion;
	uint32_t dwSerialNumber;
	uint16_t wType;
	uint16_t wNumOutStreams;
	uint16_t wNumInStreams;
	int i;

	hpi_handle_t hMixer = 0;
	hpi_handle_t hTuner = 0, hMux = 0;

	printf("service %lu, component %lu\n", sizeof(struct hpi_dab_service_info), sizeof(struct hpi_dab_component_info));
	parse_options(argc, argv);

	signal(SIGINT, sigHandler);

	/* */
	verbose_printf
		("********************************************************************\n");
	verbose_printf
		("** asihpitune **\n");
	verbose_printf("** HPI test code for tuners **\n");
	verbose_printf
		("********************************************************************\n");
	printf("HPI_SubSysCreate\n");
	hSubSys = HPI_SubSysCreate();
	if (hSubSys == NULL) {
		printf("hSubSys==NULL\n");
		exit(1);
	}

	err = HPI_SubSysGetVersionEx(hSubSys, &dwVersion);
	HandleError(err);
	verbose_printf("HPI_SubSysGetVersionEx=%d.%02d.%02d\n",
		dwVersion >> 16, (dwVersion >> 8) & 0xff, dwVersion & 0xff);

	err = HPI_SubSysGetNumAdapters(hSubSys, &numAdapters);
	HandleError(err);
	verbose_printf("HPI_SubSysGetNumAdapters NumberAdapters=%d\n", numAdapters);
	for (i = 0; i < numAdapters; i++) {
		uint32_t dwAdapterIndex;
		uint16_t wAdapterType;
		err =  HPI_SubSysGetAdapter(hSubSys,
				i, &dwAdapterIndex, &wAdapterType);
		verbose_printf("%d=ASI%4X\n", dwAdapterIndex, wAdapterType);
	}

	err = HPI_AdapterClose(hSubSys, opt_adapter_index);
	HandleError(err);
	verbose_printf("HPI_AdapterClose \n");

	// open 1st adapter
	err = HPI_AdapterOpen(hSubSys, opt_adapter_index);
	HandleError(err);
	verbose_printf("HPI_AdapterOpen \n");

	err = HPI_AdapterGetInfo(hSubSys, opt_adapter_index,
		&wNumOutStreams, &wNumInStreams, &wVersion, &dwSerialNumber, &wType);
	HandleError(err);
	verbose_printf("HPI_AdapterGetInfo\n");
	verbose_printf("Adapter=ASI%4X Index=%d NumOutStreams=%d NumInStreams=%d S/N=%d Hw Version %c%d ",
		wType, opt_adapter_index, wNumOutStreams, wNumInStreams,
		dwSerialNumber, ((wVersion >> 3) & 0xf) + 'A',	// Hw version major
		wVersion & 0x7	// Hw version minor
		);

	if (opt_verbose) {
		uint16_t major, minor;
		err = HPI_AdapterGetProperty(hSubSys, opt_adapter_index,
			HPI_ADAPTER_PROPERTY_SOFTWARE_VERSION,
			&major, &minor);
		HandleError(err);
		verbose_printf("DSP code version %d.%02d.%02d\n", major >> 8,
			major & 0xff, minor);
	}
	// open the mixer of this adapter
	err = HPI_MixerOpen(hSubSys, opt_adapter_index, &hMixer);
	verbose_printf("HPI_MixerOpen: handle=%X\n", hMixer);
	HandleError(err);
	printf("\n");

	if ((wType & 0xF000) != 0x8000) {
		printf("Only ASI87xx, ASI8821 and ASI89xx adapters are supported/n");
		goto close;
	}

	err = HPI_MixerGetControl(hSubSys, hMixer, HPI_SOURCENODE_TUNER,
		opt_tuner_index, 0, 0, HPI_CONTROL_TUNER, &hTuner);
	HandleErrorComment("Get Tuner Control", err);
	if (err)
		goto close;

	err = HPI_MixerGetControl(hSubSys, hMixer, 0, 0,
		HPI_DESTNODE_LINEOUT, 0, HPI_CONTROL_MULTIPLEXER, &hMux);
	HandleErrorComment("Get Mux", err);

	/******** Mode setting ******/
	if (opt_rss_mode >= 0) {
			err = HPI_Tuner_SetMode(hSubSys, hTuner,
				HPI_TUNER_MODE_RSS, opt_rss_mode);
			HandleErrorComment("SetMode RSS", err);
	}

	if (opt_rds_mode >= 0) {
			err = HPI_Tuner_SetMode(hSubSys, hTuner,
				HPI_TUNER_MODE_RDS, opt_rds_mode);
			HandleErrorComment("SetMode RDS", err);
	}

	/******** Mux setting ******/
	if (hMux && (opt_mux_index >= 0) && (opt_mux_index < 8)) {
		HandleErrorComment("Get mux ctrl", err);
		err = HPI_Multiplexer_SetSource(hSubSys, hMux,
			HPI_SOURCENODE_TUNER, opt_mux_index);
		HandleErrorComment("SetMuxSource", err);
	}

	/******** Band setting ******/
	if (opt_band) {
		err = HPI_Tuner_SetBand(hSubSys, hTuner, opt_band);
		HandleErrorComment("SetBand", err);
	}

	/******** Frequency setting ******/
	if (opt_tuner_frequency) {
		err = HPI_Tuner_SetFrequency(hSubSys, hTuner,
			opt_tuner_frequency);
		HandleErrorComment("SetFrequency", err);
	}
	asi_sleep(nanosec / 1000000);



	if (opt_query_tuner_caps)
		query_tuner_caps(hMixer, hTuner, opt_tuner_index);

	if (opt_query_tuner_settings)
		query_tuner_settings(hMixer, hTuner, opt_tuner_index);

	if (opt_rds_poll_loops)
		poll_rds(hMixer, hTuner, opt_tuner_index);

	if (opt_band == HPI_TUNER_BAND_DAB) {
		if (opt_serv && opt_comp) {
			printf("Start DAB service 0x%X:0x%X\n", opt_serv, opt_comp);
			err = Tuner_SetDabService(hTuner, opt_serv, opt_comp, HPI_SWITCH_ON);
			HandleError(err);
		}

		if (opt_dab_poll)
			poll_dab(hTuner);

		if (opt_serv && opt_comp) {
			printf("Stop DAB service 0x%X:0x%X\n", opt_serv, opt_comp);
			err = Tuner_SetDabService(hTuner, opt_serv, opt_comp, HPI_SWITCH_OFF);
			HandleError(err);
		}
	}

close:
	err = HPI_MixerClose(hSubSys, hMixer);
	verbose_printf("\nHPI_MixerClose\n");
	HandleError(err);

	err = HPI_AdapterClose(hSubSys, opt_adapter_index);
	verbose_printf("HPI_AdapterClose\n");
	HandleError(err);

	err = HPI_AdapterClose(hSubSys, opt_adapter_index);

	HPI_SubSysFree(hSubSys);
	printf("\nNormal exit\n");
	return 0;
}

/****************************** HandleError **********************/
static void HandleErrorCommentContinue(const char *comment, hpi_err_t err)
{
	char szError[256];
	if (err) {
		printf("\t\t%s\n", comment);
		HPI_GetErrorText(err, szError);
		printf("\t\tERROR %s\n", szError);
	}

}

static void HandleError(hpi_err_t err)
{
	char szError[256];
	char nK = 0;

	if (err) {
		HPI_GetErrorText(err, szError);
		printf("ERROR %d %s\n", err, szError);
		printf("\tpress Enter to continue, (q,Enter) to exit...\n");
		nK = getch();
		if (nK == 'q')
			exit(0);
	}
}

static void HandleErrorComment(const char *comment, hpi_err_t err)
{
	if (err)
		printf("%s ", comment);
	HandleError(err);
}

static int getch(void)
{
	return getchar();
}

/****************************** asi_sleep **********************/
static void asi_sleep(unsigned int milliseconds)
{
#if defined(HPI_OS_LINUX) || defined(__APPLE__)
	struct timespec req =
		{ milliseconds / 1000, (milliseconds % 1000) * 1000000 };
	nanosleep(&req, 0);
#else
	Sleep(milliseconds);
#endif
}
/* END_OF_CODE */
