/*---------------------------------------------------------------------------------------*/
/*
	Copyright (c) 1998-2004 B2C2, Incorporated.  All Rights Reserved.

	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF B2C2, INC.

	The copyright notice above does not evidence any actual or intended publication of 
	such source code.

	This file is proprietary source code of B2C2, Inc. and is released pursuant to and
	subject to the restrictions of any non-disclosure agreement and license contract 
	entered into by the parties.

  
	FILE:	b2settuner.cpp

	HISTORY:		

	12/29/03  Add Bandwidth option to support new DVB-T hardware with Samsung NIM.
	12/29/03  Allowed frequency in MHz to be specified with decimal point.
	09/21/05  Add support for the DiSEqC 1.2 SDK interface. [ARS]

*/
/*---------------------------------------------------------------------------------------*/


#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif

typedef unsigned long DWORD;

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#if defined __linux__
	#include <unistd.h>
	#include <sys/types.h>
	#include <linux_windefs.h>
#endif //defined __linux__

#if defined WIN32
	#include <Dshow.h>
#endif //defined WIN32

#include "b2c2_defs.h"
#include "b2c2mpeg2adapter.h"
#include "ib2c2mpeg2tunerctrl.h"
#include "ib2c2mpeg2datactrl.h"
#include "ib2c2mpeg2avctrl.h"

// Command line usage message

static char * USAGE_MSG = "\
\n\
Usage: b2settuner <required args> [optional args]\n\
\n\
Note: Arguments must be separated from values with blanks\n\
\n\
Required Arguments:\n\
-a    Adapter name; e.g. eth1 (Linux only)\n\
-i    Tuner type; valid values are:\n\
      c (cable), s (satellite), t (terrestrial DVB),\n\
      a (terrestrial ATSC),\n\
      One of the following must be specified for (c, s, t, a):\n\
-f    Frequency (Transponder) in MHz (c, s, t, a)\n\
-fk   Frequency (Transponder) in kHz (c, s, t, a)\n\
-s    Symbol Rate in Ks/s (c, s); e.g. 6111\n\
-m    Modulation in QAM (c); valid values are:\n\
      4, 16, 32, 64, 128, 256\n\
-l    LNB Frequency in MHz (s); note: must be less than Transponder\n\
      Frequency specified with -f\n\
-e    FEC (s); valid values are:\n\
      1/2, 2/3, 3/4, 5/6, 7/8, auto\n\
-o    Polarity (s); valid values are:\n\
      h (horizontal), v (vertical), n(none)\n\
-k    LNB Selection in KHz(s); valid values are: 0, 22, 33, 44\n\
-d    Diseqc (s); valid values are:\n\
      n (none), a, b, a/a, a/b, b/a, b/b, x <n> 0xXX ...\n\
-g    Guard interval (t); valid values are: 1/4, 1/8, 1/16, 1/32\n\
-b    Bandwidth in Mhz (t); valid values are: 6, 7, 8 \n\
-pd   IP PID as integer or hex. (c, s); e.g. 164, 0xa4 or 0XA4.\n\
      For each IP PID, specify a separate -pd option;\n\
      e.g. -pd 17 -pd 18, etc.  Maximum # of PIDs supported: 32\n\
\n""\
Optional Arguments:\n\
-st   Skip Tuning\n\
-ma   MAC address as 6 pairs of hex digits preceded with 0x;\n\
      e.g. 0x01005e010500\n\
-t    Time duration in seconds to pause while data is received\n\
-r    Times to repeat test if not in lock\n\
-pia  Add IP PID (same as -pd)\n\
-pid  Delete IP PID given as next argument in decimal or hex(0x)\n\
-pil  List IP PIDs\n\
-pta  Add TS PID given as next argument in decimal or hex (0x)\n\
-ptd  Delete TS PID given as next argument in decimal or hex(0x)\n\
-ptl  List TS PIDs\n\
-paa  Add Audio PID given as next argument in decimal or hex(0x)\n\
-pad  Delete Audio PID; PID must not be given as argument\n\
-pva  Add Video PID given as next argument in decimal or hex(0x)\n\
-pvd  Delete Video PID; PID must not be given as argument\n\
-pal  List Audio/Video PIDs\n\
-ums  Set unicast MAC address\n\
-uml  Lists unicast MAC address\n\
-umr  Restores unicast MAC address from device\n\
-mms  Set multicast MAC address\n\
-mml  Lists multicast MAC address\n\
-mmd  Deletes multicast MAC address\n\
-stid Set datagram packet table ID; valid values are: 3E, 3F\n\
-gtid Get datagram packet table ID\n\
-kpa  Add key for given PID and TSC field values;\n\
        PID can be given as decimal or hex (0x) value;\n\
	    Possible TSC values are: 01, 10 (even), 11 (odd);\n\
        Key value is given as 8 pairs of hex digits;\n\
	  e.g. 0x1234 10 1122334455667788\n\
-kpd  Delete key for given PID and TSC values;\n\
        PID can be given as decimal or hex (0x) value;\n\
	    Valid TSC values are: 01, 10 (even), 11 (odd);\n\
	  e.g. 0x1234 11\n\
-kas  Add key for given PID and ANY TSC field value;\n\
        PID can be given as decimal or hex (0x) value;\n\
	    Key value is given as 8 pairs of hex digits;\n\
	  e.g. 0x1234 1122334455667788\n\
-kad  Delete key for PID and ANY TSC values\n\
-kgs  Set global key as 8 pairs of hex digits; \n\
      e.g. 1122334455667788\n\
-kgd  Delete global key\n\
-kp   Purge all existing keys\n\
-kl   Lists all existing keys\n\
-h    Help\n";

// Tuner types


#define MAX_IP_PIDS			34
#define MAX_TS_PIDS			39

#define PID_MAX 			0x1fff

// copied from "../sky2pcavsrc/constants.h"; better move b2c2_defs.h
#define SKY2PC_E_OUTOFLOCK						0x90010115

#define MAX_PID_KEYS		7

// Struct used for passing parameters

typedef struct
{	
	tTunerModulation	eTunerType;
	bool			bTunerTypeSet;
	char			szAdapterName[256];
	bool			bAdapterNameSet;
	long			lFrequencyKhz;
	bool			bFrequencySet;
	long			lSymbolRateKs;
	bool			bSymbolRateSet;
	eModulation		teModulation;
	bool			bModulationSet;
	long			lLNB;
	bool			bLNBSet;
	eFEC			teFEC;
	bool			bFECSet;
	ePolarity		tePolarity;
	bool			bPolaritySet;
	eLNBSelection	teLNBSelection;
	bool			bLNBSelectionSet;
	eDiseqc			teDiseqc;
	bool			bDiseqcSet;
	long			lGuardInterval;
	bool			bGuardIntervalSet;
	long			lBandwidth;
	bool			bBandwidthSet;
	long			lIpPIDArraySize;
	long			lIpPIDArray[MAX_IP_PIDS];
	long			lSysIpPIDSize;
	long			lSysTsPIDSize;
	long			lSleepLength;
	bool			bTimeOutSet;
	//
	tMacAddressList	stMcMacAddressesSet;
	tMacAddressList	stMcMacAddressesDel;
	bool			bMcMacAddrList;
	unsigned char   abtUcMacAddress[B2C2_SDK_MAC_ADDR_SIZE];
	bool			bUcMacAddrSet;
	bool			bUcMacAddrRestore;
	bool			bUcMacAddrList;
	//
	bool			bSkipTuning;
	int				nRepeatLockTest;
	//
	long			lTsPidAddArraySize;
	long			lTsPidAddArray[MAX_TS_PIDS];
	long			lTsPidDelArraySize;
	long			lTsPidDelArray[MAX_TS_PIDS];
	bool			bShowTsPids;
	//
	bool			bAvPidAddSet;
	long			lAudioPidAdd;
	long			lVideoPidAdd;
	//
	long			bAudioPidDel;
	long			bVideoPidDel;
	//
	bool			bShowAvPids;
	//
	long			lIpPidDelArraySize;
	long			lIpPidDelArray[MAX_IP_PIDS];
	bool			bShowIpPids;
	//
	bool			bGetTableId;
	bool			bSetTableId;
	long			lTableId;
	//
	unsigned char   abtGlobalKey[B2C2_SDK_FIXED_KEY_SIZE];
	bool			bSetGlobalKey;
	bool			bDeleteGlobalKey;
	//
	bool			bKeyList;
	bool			bKeyPurge;
	//
	long			lPidKeysAdd;
	long			alKpaPid[MAX_PID_KEYS];
	long			alKpaTsc[MAX_PID_KEYS];
	BYTE			aabtKpaKey[MAX_PID_KEYS][B2C2_SDK_FIXED_KEY_SIZE];
	//
	long			lPidKeysDelete;
	long			alKpdPid[MAX_PID_KEYS];
	long			alKpdTsc[MAX_PID_KEYS];
	//
	long			bAddAnyTscKey;
	long			lKaaPid;
	BYTE			abtKaaKey[B2C2_SDK_FIXED_KEY_SIZE];
	//
	long			bDeleteAnyTscKey;
	long			lKadPid;

	int				nDiSEqCLength;
	BYTE			abtDiSEqCCommand[16];
} SParams;

int CheckCommandLineArgs (int argc, char * argv[], SParams * psParams);

#if defined WIN32
static inline unsigned int sleep (unsigned int seconds)	
	{
		Sleep (seconds * 1000);
		return 0;
	}
#endif //defined WIN32


void B2SetTunerCheckCapabilities (IB2C2MPEG2TunerCtrl4 *pB2C2FilterTunerCtrl, SParams *psParams)
{
	tTunerCapabilities 	tunerCaps;
	long  				lTunerCapsSize = sizeof (tunerCaps);
	memset (&tunerCaps, 0xFF, lTunerCapsSize);
	long 				hr;

	//
	// Get Tuner Capabilities
	//	
	hr = pB2C2FilterTunerCtrl->GetTunerCapabilities (&tunerCaps, &lTunerCapsSize);

	if (FAILED (hr))
	{
		fprintf (stderr,"b2status: Error: B2C2 Driver Data Ctrl. Interface GetTunerCapabilities method failed, error: 0x%8.8lX\n", hr);
	}

	// Compare the user-specified tuner type with the capabilities tuner type
	if (tunerCaps.eModulation != psParams->eTunerType)
	{
		printf ("Warning: specified tuner type does not match actual tuner found.\n");
	}
	
	// Compare the frequency specified with the range specified in the capabilities.
	if (   ((DWORD) psParams->lFrequencyKhz > (tunerCaps.dwMaxTransponderFreqInKHz)) 
		|| ((DWORD) psParams->lFrequencyKhz < (tunerCaps.dwMinTransponderFreqInKHz)))
	{
		printf ("Warning: specified tuner frequency %ld is outside the tuner's range of %ld-%ld kHz\n",
					psParams->lFrequencyKhz, (tunerCaps.dwMinTransponderFreqInKHz), (tunerCaps.dwMaxTransponderFreqInKHz));
	}

	// Compare the symbol rate, if specified, with the range specified in the capabilities.
	if (psParams->bSymbolRateSet)
	{
		if (   ((DWORD) (psParams->lSymbolRateKs * 1000) > tunerCaps.dwMaxSymbolRateInBaud) 
			|| ((DWORD) (psParams->lSymbolRateKs * 1000) < tunerCaps.dwMinSymbolRateInBaud))
		{
			printf ("Warning: specified symbol rate %ld is outside the tuner's range of %ld-%ld kS/s\n",
						psParams->lSymbolRateKs, (tunerCaps.dwMinSymbolRateInBaud / 1000), (tunerCaps.dwMaxSymbolRateInBaud / 1000));
		}
	}
}

#if defined WIN32
	#define MAX_ADDR_FORMAT_STR		"%02X-%02X-%02X-%02X-%02X-%02X"
#else // __linux__
	#define MAX_ADDR_FORMAT_STR		"%02X:%02X:%02X:%02X:%02X:%02X"
#endif //defined WIN32

// Note: szPrefix and szExtention can be NULL
void ShowMacAddr (char* szPrefix, BYTE *pMacAddr, char *szExtention)
{
	printf ("%s" MAX_ADDR_FORMAT_STR "%s",
			szPrefix == NULL ? "" : szPrefix,
			pMacAddr[0], pMacAddr[1], pMacAddr[2],
			pMacAddr[3], pMacAddr[4], pMacAddr[5],
			szExtention == NULL ? "" : szExtention);
}

bool Str2Mac (unsigned char * pMacAddr, char * szMacAddress)
{
	if (strlen (szMacAddress) < 12)
	{
		return FALSE;
	}

	// walk backwards, converting pairs of hex character strings to decimal.
	// original string is clobbered.

	pMacAddr[5] = (BYTE) strtoul (szMacAddress + 10, NULL, 16);

	szMacAddress[10] = (char) NULL;
	pMacAddr[4] = (BYTE) strtoul (szMacAddress + 8, NULL, 16);

	szMacAddress[8] = (char) NULL;
	pMacAddr[3] = (BYTE) strtoul (szMacAddress + 6, NULL, 16);

	szMacAddress[6] = (char) NULL;
	pMacAddr[2] = (BYTE) strtoul (szMacAddress + 4, NULL, 16);

	szMacAddress[4] = (char) NULL;
	pMacAddr[1] = (BYTE) strtoul (szMacAddress + 2, NULL, 16);

	szMacAddress[2] = (char) NULL;
	pMacAddr[0] = (BYTE) strtoul (szMacAddress, NULL, 16);

	return TRUE;
}

bool Str2MultiBytes (const char * szStr, unsigned char * pBytes, int nBytes)
{
	if (   nBytes == 0
		|| strlen (szStr) < (unsigned) (nBytes * 2))
	{
		return FALSE;
	}

	char * pszCurStr = (char*) szStr;

	for (int iCnt = 0; iCnt < nBytes; iCnt++)
	{
		unsigned long dwCurByte;
		sscanf( pszCurStr, "%2lx", &dwCurByte);
		pBytes[iCnt] = (unsigned char) dwCurByte;
		pszCurStr += 2;
	}

	return TRUE;
}

void Str2Pid (const char * szPid, long * plPid)
{
	if (   strlen (szPid) > 2 
		&& (   strncmp (szPid, "0x", 2) == 0 
		    || strncmp (szPid, "0X", 2) == 0))
	{
		// convert from hex
		*plPid = strtoul ((szPid + 2), NULL, 16);
	}
	else
	{
		// assume decimal
		*plPid = atoi (szPid);
	}
}

bool TscStr2Type (const char * szTsc, long * plType)
{
	if (strlen (szTsc) != 2)
	{
		return FALSE;
	}

	// Compare against possible TSC values
	if (strcmp( szTsc, "01") == 0)
	{
		*plType = B2C2_KEY_PID_TSC_01;
	} else
	if (strcmp( szTsc, "10") == 0)
	{
		*plType = B2C2_KEY_PID_TSC_10;
	} else
	if (strcmp( szTsc, "11") == 0)
	{
		*plType = B2C2_KEY_PID_TSC_11;
	} else
	{
		return FALSE; // Invalid value
	}
	return TRUE;
}

char* Tsc2Bits(BYTE btTsc)
{
	static char strTscBits [] = "00";

	strTscBits[0] = (btTsc & 0x02) ? '1' : '0';
	strTscBits[1] = (btTsc & 0x01) ? '1' : '0';

	return strTscBits;
}

int main (int argc, char * argv[])
{
	SParams					sParams;

	IB2C2MPEG2TunerCtrl4	*pB2C2FilterTunerCtrl;
	IB2C2MPEG2DataCtrl4		*pB2C2FilterDataCtrl;
	IB2C2MPEG2AVCtrl2		*pB2C2FilterAvCtrl;

	HRESULT					hr;

	// Need to be root to run.

#if defined __linux__
	// Need to be root to run; to test use effective user ID
	if (geteuid () != (uid_t) 0)
	{
		fprintf (stderr, "\nb2settuner: Error: Must be root to run this command\n");

		return 1;	// *** FUNCTION EXIT POINT
	}
#endif //defined __linux__

	// Initializations.

	memset (&sParams.szAdapterName, 0x00, sizeof (sParams.szAdapterName));
	sParams.bTunerTypeSet = FALSE;
	sParams.bAdapterNameSet = FALSE;
	sParams.bFrequencySet = FALSE;
	sParams.bSymbolRateSet = FALSE;
	sParams.bLNBSet = FALSE;
	sParams.bFECSet = FALSE;
	sParams.bPolaritySet = FALSE;
	sParams.bLNBSelectionSet = FALSE;
	sParams.bDiseqcSet = FALSE;
	sParams.bGuardIntervalSet = FALSE;
	sParams.bBandwidthSet = FALSE;
	sParams.bModulationSet = FALSE;
	memset (&sParams.stMcMacAddressesSet, 0x00, sizeof (sParams.stMcMacAddressesSet));
	memset (&sParams.stMcMacAddressesDel, 0x00, sizeof (sParams.stMcMacAddressesDel));
	sParams.bMcMacAddrList = FALSE;
	sParams.bUcMacAddrSet = FALSE;
	sParams.bUcMacAddrRestore = FALSE;
	sParams.bUcMacAddrList = FALSE;
	sParams.bSkipTuning = FALSE;
	sParams.bTimeOutSet = FALSE;
	sParams.bAvPidAddSet = FALSE;
	sParams.bAudioPidDel = FALSE;
	sParams.bVideoPidDel = FALSE;
	sParams.lIpPIDArraySize = 0;
	sParams.nRepeatLockTest = -1;
	sParams.lTsPidAddArraySize = 0;
	sParams.lTsPidDelArraySize = 0;
	sParams.bShowTsPids = FALSE;
	sParams.lAudioPidAdd = 0;
	sParams.lVideoPidAdd = 0;
	sParams.bShowAvPids = FALSE;
	sParams.lIpPidDelArraySize = 0;
	sParams.bShowIpPids = FALSE;
	sParams.bGetTableId = FALSE;
	sParams.bSetTableId = FALSE;
	sParams.lTableId = 0;
	memset (&sParams.abtGlobalKey, 0x00, sizeof (sParams.abtGlobalKey));
	sParams.bSetGlobalKey = FALSE;
	sParams.bDeleteGlobalKey = FALSE;
	sParams.bKeyList = FALSE;
	sParams.bKeyPurge = FALSE;
	//
	sParams.lPidKeysAdd = 0;
	memset (&sParams.alKpaPid, 0x00, sizeof (sParams.alKpaPid));
	memset (&sParams.alKpaTsc, 0x00, sizeof (sParams.alKpaTsc));
	memset (&sParams.aabtKpaKey, 0x00, sizeof (sParams.aabtKpaKey));
	//
	sParams.lPidKeysDelete = 0;
	memset (&sParams.alKpdPid, 0x00, sizeof (sParams.alKpdPid));
	memset (&sParams.alKpdTsc, 0x00, sizeof (sParams.alKpdTsc));
	//
	sParams.bAddAnyTscKey = FALSE;
	memset (&sParams.lKaaPid, 0x00, sizeof (sParams.lKaaPid));
	memset (&sParams.abtKaaKey, 0x00, sizeof (sParams.abtKaaKey));
	//
	sParams.bDeleteAnyTscKey = FALSE;
	memset (&sParams.lKadPid, 0x00, sizeof (sParams.lKadPid));

	// Defaults.

	sParams.lSysIpPIDSize = MAX_IP_PIDS;
	sParams.lSysTsPIDSize = MAX_TS_PIDS;
	sParams.lSleepLength = 10;

	// Check command line arguments.

	if (CheckCommandLineArgs (argc, argv, &sParams) < 0)
	{
		return 1;	// *** FUNCTION EXIT POINT
	}

#ifdef __linux__
	// Make sure an adapter name has been specified.
	if (sParams.bAdapterNameSet == FALSE)
	{
		fprintf (stderr, "\nb2settuner: Error: Adapter name has not been specified\n");
		fprintf (stderr, USAGE_MSG);
	
		return 1;	// *** FUNCTION EXIT POINT
	}
#endif

	// Make sure a tuner type has been specified unless tuning is being skipped.

	if (sParams.bSkipTuning == FALSE && sParams.bTunerTypeSet == FALSE)
	{
		fprintf (stderr, "\nb2settuner: Error: Tuner type has not been specified\n");
		fprintf (stderr, USAGE_MSG);
	
		return 1;	// *** FUNCTION EXIT POINT
	}

	// Create B2C2 Adapter class

	B2C2MPEG2Adapter b2c2Adapter (sParams.szAdapterName);

	// Emulate querying for COM interfaces.

	// Initialize; Check if B2C2 Adapter is accessible
	// and if device is B2C2 Adapter

	hr = b2c2Adapter.Initialize ();

	switch (hr)
	{
	case S_OK : // Successful
		break;
	default :
	case E_FAIL:
		fprintf (stderr, "\nb2settuner: Error: Failed to open \'%s\' or the device is no B2C2 Adapter\n",
						 sParams.szAdapterName);

		DWORD dwErr = b2c2Adapter.GetLastError();
		if( dwErr)
		{ 
			fprintf (stderr, "                   (%s failed; #%08X)\n\n",
							 b2c2Adapter.GetLastErrorText(), (int) dwErr);
		}
	
		return 1;	// *** FUNCTION EXIT POINT
	}

	// NOTE: Initialize () checks if pointer != NULL	

	pB2C2FilterTunerCtrl = b2c2Adapter.GetTunerControl ();
	pB2C2FilterDataCtrl = b2c2Adapter.GetDataControl ();
	pB2C2FilterAvCtrl = b2c2Adapter.GetAvControl ();

	// From this point on, calls to the B2C2 SDK methods are identical to those on Windows.

	// **********************************************************************
	// *** CONFIGURE TUNER
	// **********************************************************************

	// Skip tuning if so required.

	if (sParams.bSkipTuning == FALSE)
	{
		// **********************************************************************
		// *** CHECK CAPABILITIES: warn user of any problems here
		// **********************************************************************
		B2SetTunerCheckCapabilities (pB2C2FilterTunerCtrl, &sParams);

		switch (sParams.eTunerType)
		{
		case TUNER_CABLE:
			
			// **********************************************************************
			// *** CABLE
			// **********************************************************************

			// Make sure all flags have been set

		    if (sParams.bFrequencySet == FALSE ||
			    sParams.bSymbolRateSet == FALSE ||
			    sParams.bModulationSet == FALSE)
		    {
			    fprintf (stderr, "\nb2settuner: Error: One or more tuner-specific flags have not been specified\n");
			    fprintf (stderr, USAGE_MSG);
	    
			    return 1;	// *** FUNCTION EXIT POINT
		    }
	    
		    hr = pB2C2FilterTunerCtrl->SetFrequencyKHz(sParams.lFrequencyKhz);
			    
		    if (FAILED (hr))
		    {
			    fprintf (stderr,"b2settuner: Error: B2C2 Driver Tuner Ctrl. Interface SetFrequencyKHz method failed, error: 0x%8.8x\n", hr);
	
			    return hr;	// *** FUNCTION EXIT POINT
		    }
	    
		    hr = pB2C2FilterTunerCtrl->SetSymbolRate(sParams.lSymbolRateKs);
			    
		    if (FAILED (hr))
		    {
			    fprintf (stderr,"b2settuner: Error: B2C2 Driver Tuner Ctrl. Interface SetSymbolRate method failed, error: 0x%8.8x\n", hr);

			    return hr;	// *** FUNCTION EXIT POINT
		    }
	    
		    hr = pB2C2FilterTunerCtrl->SetModulation(sParams.teModulation);
			    
		    if (FAILED (hr))
		    {
			    fprintf (stderr,"b2settuner: Error: B2C2 Driver Tuner Ctrl. Interface SetModulation method failed, error: 0x%8.8x\n", hr);

			    return hr;	// *** FUNCTION EXIT POINT
		    }

			break;
    
	    case TUNER_SATELLITE:

	        // **********************************************************************
	        // *** SATELLITE
	        // **********************************************************************

	        // Make sure all flags have been set
        
	        if (sParams.bFrequencySet == FALSE ||
		        sParams.bSymbolRateSet == FALSE ||
		        sParams.bLNBSet == FALSE ||
		        sParams.bFECSet == FALSE ||
		        sParams.bPolaritySet == FALSE ||
		        sParams.bLNBSelectionSet == FALSE ||
		        sParams.bDiseqcSet == FALSE)
	        {
		        fprintf (stderr, "\nb2settuner: Error: One or more tuner-specific flags have not been specified\n");
		        fprintf (stderr, USAGE_MSG);
        
		        return 1;	// *** FUNCTION EXIT POINT
	        }
			
	        if (sParams.lLNB >= sParams.lFrequencyKhz)
	        {
		        fprintf (stderr, "\nb2settuner: Error: LNB Frequency (-l) must be less than Transponder frequency (-f)\n");
		        fprintf (stderr, USAGE_MSG);
        
		        return 1;	// *** FUNCTION EXIT POINT
	        }
        
	        hr = pB2C2FilterTunerCtrl->SetFrequencyKHz(sParams.lFrequencyKhz);
		        
	        if (FAILED (hr))
	        {
		        fprintf (stderr,"b2settuner: Error: B2C2 Driver Tuner Ctrl. Interface SetFrequency method failed, error: 0x%8.8x\n", hr);

		        return hr;	// *** FUNCTION EXIT POINT
	        }

	        hr = pB2C2FilterTunerCtrl->SetSymbolRate(sParams.lSymbolRateKs);
		        
	        if (FAILED (hr))
	        {
		        fprintf (stderr,"b2settuner: Error: B2C2 Driver Tuner Ctrl. Interface SetSymbolRate method failed, error: 0x%8.8x\n", hr);

		        return hr;	// *** FUNCTION EXIT POINT
	        }
        
	        hr = pB2C2FilterTunerCtrl->SetLnbFrequency(sParams.lLNB);
        
	        if (FAILED (hr))
	        {
		        fprintf (stderr,"b2settuner: Error: B2C2 Driver Tuner Ctrl. Interface SetLnbFrequency method failed, error: 0x%8.8x\n", hr);

		        return hr;	// *** FUNCTION EXIT POINT
	        }
        
	        hr = pB2C2FilterTunerCtrl->SetFec(sParams.teFEC);
        
	        if (FAILED (hr))
	        {
		        fprintf (stderr,"b2settuner: Error: B2C2 Driver Tuner Ctrl. Interface SetFec method failed, error: 0x%8.8x\n", hr);

		        return hr;	// *** FUNCTION EXIT POINT
	        }
        
	        hr = pB2C2FilterTunerCtrl->SetPolarity(sParams.tePolarity);

	        if (FAILED (hr))
	        {
		        fprintf (stderr,"b2settuner: Error: B2C2 Driver Tuner Ctrl. Interface SetPolarity method failed, error: 0x%8.8x\n", hr);

		        return hr;	// *** FUNCTION EXIT POINT
	        }
        
	        hr = pB2C2FilterTunerCtrl->SetLnbKHz(sParams.teLNBSelection);
        
	        if (FAILED (hr))
	        {
		        fprintf (stderr,"b2settuner: Error: B2C2 Driver Tuner Ctrl. Interface SetLnbKHz method failed, error: 0x%8.8x\n", hr);

		        return hr;	// *** FUNCTION EXIT POINT
	        }
        
	        hr = pB2C2FilterTunerCtrl->SetDiseqc(sParams.teDiseqc);
        
	        if (FAILED (hr))
	        {
		        fprintf (stderr,"b2settuner: Error: B2C2 Driver Tuner Ctrl. Interface SetDiseqc method failed, error: 0x%8.8x\n", hr);

		        return hr;	// *** FUNCTION EXIT POINT
	        }

			// Now that the stream parameters are set,
			// If sending a formatted DiSEqC command,
			// just do it and exit.
			// The stream parameters are necessary if the lnb tome is to be reset after the message.
			if (sParams.nDiSEqCLength != 0)
			{
		        hr = pB2C2FilterTunerCtrl->SendDiSEqCCommand(sParams.nDiSEqCLength, sParams.abtDiSEqCCommand);
				if (FAILED (hr))
				{
					fprintf (stderr,"b2settuner: Error: B2C2 Driver Tuner Ctrl. Interface SendDiSEqCCommand method failed, error: 0x%8.8x\n", hr);
//
//					return hr;	// *** FUNCTION EXIT POINT
				}
				return hr;
			}
        
			break;

		case TUNER_TERRESTRIAL:

	        // **********************************************************************
	        // *** TERRESTRIAL
	        // **********************************************************************

	        // Make sure all flags have been set (NOTE: Guard Interval is required if using 
			// r0.0 of the DVB-T card)

	        if (sParams.bFrequencySet == FALSE || sParams.bBandwidthSet == FALSE )
	        {
		        fprintf (stderr, "\nb2settuner: Error: One or more tuner-specific flags have not been specified\n");
		        fprintf (stderr, USAGE_MSG);
        
		        return 1;	// *** FUNCTION EXIT POINT
	        }

		    hr = pB2C2FilterTunerCtrl->SetFrequencyKHz(sParams.lFrequencyKhz);
			    
		    if (FAILED (hr))
		    {
			    fprintf (stderr,"b2settuner: Error: B2C2 Driver Tuner Ctrl. Interface SetFrequencyKHz method failed, error: 0x%8.8x\n", hr);
	
			    return hr;	// *** FUNCTION EXIT POINT
		    }

		    hr = pB2C2FilterTunerCtrl->SetGuardInterval (sParams.lGuardInterval);
			    
		    if (FAILED (hr))
		    {
			    fprintf (stderr,"b2settuner: Error: B2C2 Driver Tuner Ctrl. Interface SetGuardInterval method failed, error: 0x%8.8x\n", hr);
	
			    return hr;	// *** FUNCTION EXIT POINT
		    }

			// Set Channel Bandwidth (NOTE: Temporarily use polarity function to avoid having to 
			// change SDK interface for SetBandwidth)
		    hr = pB2C2FilterTunerCtrl->SetPolarity (sParams.lBandwidth);
			    
		    if (FAILED (hr))
		    {
			    fprintf (stderr,"b2settuner: Error: B2C2 Driver Tuner Ctrl. Interface SetBandwidth method failed, error: 0x%8.8x\n", hr);
	
			    return hr;	// *** FUNCTION EXIT POINT
		    }

		case TUNER_ATSC:

	        // **********************************************************************
	        // *** ATSC: requires only frequency to be set
	        // **********************************************************************

	        // Make sure all flags have been set
			// 
	        if (sParams.bFrequencySet == FALSE)
	        {
		        fprintf (stderr, "\nb2settuner: Error: One or more tuner-specific flags have not been specified\n");
		        fprintf (stderr, USAGE_MSG);
        
		        return 1;	// *** FUNCTION EXIT POINT
	        }

		    hr = pB2C2FilterTunerCtrl->SetFrequencyKHz(sParams.lFrequencyKhz);
			    
		    if (FAILED (hr))
		    {
			    fprintf (stderr,"b2settuner: Error: B2C2 Driver Tuner Ctrl. Interface SetFrequencyKHz method failed, error: 0x%8.8x\n", hr);
	
			    return hr;	// *** FUNCTION EXIT POINT
		    }
			break;

		default:
			printf ("Error: invalid tuner type specified\n");
			break;

		}	// switch (sParams.eTunerType)

		// **********************************************************************
		// *** SET TUNER AND CHECK LOCK
		// **********************************************************************
		    
		// Changed by ALF - use SetTunerStatusEx() if '-r' argument is defined,
		// to block the application only <-r> seconds if out of lock

		if (sParams.nRepeatLockTest > (-1))
		{
			hr = pB2C2FilterTunerCtrl->SetTunerStatusEx (sParams.nRepeatLockTest);
		}
		else
		{
			hr = pB2C2FilterTunerCtrl->SetTunerStatus ();
		}

		if (hr == (HRESULT) SKY2PC_E_OUTOFLOCK)	
		{
	    	fprintf (stderr,"b2settuner: Error: Cannot lock\n");
		
	    	return hr;	// *** FUNCTION EXIT POINT
		}
		else if (FAILED (hr))
    	{
			fprintf (stderr,"b2settuner: Error: B2C2 Driver Tuner Ctrl. Interface SetTunerStatus%s method failed, error: 0x%8.8x\n",
 						(sParams.nRepeatLockTest > (-1)) ? "Ex" : "", hr);
		
		    return hr;	// *** FUNCTION EXIT POINT
    	}
	}
	else
	{
		// Since no tuning requested, we need to make sure that at least one PID,
		// OR a MAC address has been specified.

		if (   sParams.lIpPIDArraySize == 0
			&& sParams.lIpPidDelArraySize == 0
			&& sParams.lTsPidAddArraySize == 0
			&& sParams.lTsPidDelArraySize == 0
			&& sParams.bShowTsPids == FALSE
			&& sParams.bShowAvPids == FALSE
			&& sParams.bAvPidAddSet == FALSE
			&& sParams.bAudioPidDel == FALSE
			&& sParams.bVideoPidDel == FALSE
			&& sParams.bShowIpPids == FALSE
			&& sParams.stMcMacAddressesSet.lCount == 0
			&& sParams.stMcMacAddressesDel.lCount == 0
			&& sParams.bUcMacAddrList == FALSE
			&& sParams.bUcMacAddrSet == FALSE
			&& sParams.bUcMacAddrRestore == FALSE
			&& sParams.bMcMacAddrList == FALSE
			&& sParams.bGetTableId == FALSE
			&& sParams.bSetTableId == FALSE
			&& sParams.bSetGlobalKey == FALSE
			&& sParams.bDeleteGlobalKey == FALSE
			&& sParams.bKeyList == FALSE
			&& sParams.bKeyPurge == FALSE
			&& sParams.lPidKeysAdd == 0
			&& sParams.lPidKeysDelete == 0
			&& sParams.bAddAnyTscKey == FALSE
			&& sParams.bDeleteAnyTscKey == FALSE)
		{
	    	fprintf (stderr,"\nb2settuner: Error: At least one PID or a MAC address must be specified when tuning is skipped\n");
		    fprintf (stderr, USAGE_MSG);
	    
	    	return hr;	// *** FUNCTION EXIT POINT
		}
	}	// 	if (sParams.bSkipTuning == FALSE)...

	// **********************************************************************
	// *** INTIALIZE DATA CTRL INTERFACE
	// **********************************************************************

	/*
	// Not more required
	hr = pB2C2FilterDataCtrl->Initialize ();

	if (FAILED (hr))
	{
		fprintf (stderr,"b2settuner: Error: B2C2 Driver Data Ctrl. Interface Initialize method failed, error: 0x%8.8x\n", hr);

		return hr;	// *** FUNCTION EXIT POINT
	}
	*/

	// **********************************************************************
	// *** Set / Delete / List AV PIDS
	// **********************************************************************

	if (sParams.bAvPidAddSet)
	{
		hr = pB2C2FilterAvCtrl->SetAudioVideoPIDs (sParams.lAudioPidAdd, sParams.lVideoPidAdd);

		if (FAILED (hr))
		{
			fprintf (stderr,"b2settuner: Error: B2C2 Driver Data Ctrl. Interface SetAudioVideoPIDs method failed, error: 0x%8.8x\n", hr);

			return hr;	// *** FUNCTION EXIT POINT
		}
	}

	if (sParams.bAudioPidDel || sParams.bVideoPidDel)
	{
		hr = pB2C2FilterAvCtrl->DeleteAudioVideoPIDs (sParams.bAudioPidDel, sParams.bVideoPidDel);

		if (FAILED (hr))

		{
			fprintf (stderr,"b2settuner: Error: B2C2 Driver Data Ctrl. Interface DeleteAudioVideoPIDs method failed, error: 0x%8.8x\n", hr);

			return hr;	// *** FUNCTION EXIT POINT
		}
	}

	if (sParams.bShowAvPids)
	{
		long lAudioOpen = 0;
		long lAudioRunning = 0;
		long lVideoOpen = 0;
		long lVideoRunning = 0;
		long lAudioPid = 0;
		long lVideoPid = 0;

		hr = pB2C2FilterAvCtrl->GetAudioVideoState (&lAudioOpen, &lVideoOpen,
													&lAudioRunning, &lVideoRunning,
													&lAudioPid, &lVideoPid);

		if (FAILED (hr))

		{
			fprintf (stderr,"b2settuner: Error: B2C2 Driver Data Ctrl. Interface GetAudioVideoState method failed, error: 0x%8.8x\n", hr);

			return hr;	// *** FUNCTION EXIT POINT
		}

		printf ("Audio :\n");
		printf ("  Open/Running : %ld/%ld\n", lAudioOpen, lAudioRunning);
		printf ("  PID          : ");
		if (lAudioPid > 0 && lAudioPid < PID_MAX)
		{
			printf ("%ld(0x%lX)\n", lAudioPid, lAudioPid);
		}
		else
		{
			printf ("not set\n");
		}

		printf ("Video : \n");
		printf ("  Open/Running : %ld/%ld\n", lVideoOpen, lVideoRunning);

		printf ("  PID          : ");
		if (lVideoPid > 0 && lVideoPid < PID_MAX)
		{
			printf ("%ld(0x%lX)\n", lVideoPid, lVideoPid);
		}
		else
		{
			printf ("not set\n");
		}
	} // if (sParams.bShowAvPids)

	// **********************************************************************
	// *** SET MAC ADDRESS (OPTIONAL)
	// **********************************************************************

#if defined __linux__
	if (sParams.stMcMacAddressesSet.lCount > 0)
	{

		//hr = pB2C2FilterDataCtrl->SetMacAddress (sParams.szMacAddress);
		hr = pB2C2FilterDataCtrl->AddMulticastMacAddress (&sParams.stMcMacAddressesSet);

		if (FAILED (hr))
		{
			fprintf (stderr,"b2settuner: Error: B2C2 Driver Data Ctrl. Interface AddMulticastMacAddress method failed, error: 0x%8.8x\n", hr);

			return hr;	// *** FUNCTION EXIT POINT
		}
	}

	if (sParams.stMcMacAddressesDel.lCount > 0)
	{

		//hr = pB2C2FilterDataCtrl->SetMacAddress (sParams.szMacAddress);
		hr = pB2C2FilterDataCtrl->DeleteMulticastMacAddress (&sParams.stMcMacAddressesDel);

		if (FAILED (hr))
		{
			fprintf (stderr,"b2settuner: Error: B2C2 Driver Data Ctrl. Interface DeleteMulticastMacAddress method failed, error: 0x%8.8x\n", hr);

			return hr;	// *** FUNCTION EXIT POINT
		}
	}
#endif //defined __linux__

	if (sParams.bUcMacAddrSet)
	{
		hr = pB2C2FilterDataCtrl->SetUnicastMacAddress (sParams.abtUcMacAddress);

		if (FAILED (hr))
		{
			fprintf (stderr,"b2settuner: Error: B2C2 Driver Data Ctrl. Interface SetUnicastMacAddress method failed, error: 0x%8.8x\n", hr);

			return hr;	// *** FUNCTION EXIT POINT
		}
	}

	if (sParams.bUcMacAddrRestore)
	{
		hr = pB2C2FilterDataCtrl->RestoreUnicastMacAddress ();

		if (FAILED (hr))
		{
			fprintf (stderr,"b2settuner: Error: B2C2 Driver Data Ctrl. Interface RestoreUnicastMacAddress method failed, error: 0x%8.8x\n", hr);

			return hr;	// *** FUNCTION EXIT POINT
		}
	}

	// *** Get Unicast MAC address

	if (sParams.bUcMacAddrList)
	{
		BYTE abtMacAddr[B2C2_SDK_MAC_ADDR_SIZE];
		memset (abtMacAddr, 0x00, sizeof (abtMacAddr));

		hr = pB2C2FilterDataCtrl->GetUnicastMacAddress( abtMacAddr);
		
		if (FAILED (hr))
		{
			fprintf (stderr,"b2settuner: Error: B2C2 Driver Data Ctrl. Interface GetUnicastMacAddress method failed, error: 0x%8.8x\n", hr);

			return hr;	// *** FUNCTION EXIT POINT
		}
		
		ShowMacAddr( "Unicast MAC Address: ", abtMacAddr, "\n");
	}

	// *** Get Multicast MAC address

	if (sParams.bMcMacAddrList)
	{
		tMacAddressList outMacAddrList;

		// Preset the structure
		memset (&outMacAddrList, 0x00, sizeof (outMacAddrList));
		outMacAddrList.lCount = B2C2_SDK_MAC_ADDR_LIST_MAX;

		hr = pB2C2FilterDataCtrl->GetMulticastMacAddressList( &outMacAddrList);
		
		if (FAILED (hr))
		{
			fprintf (stderr,"b2settuner: Error: B2C2 Driver Data Ctrl. Interface GetMulticastMacAddressList method failed, error: 0x%8.8x\n", hr);

			return hr;	// *** FUNCTION EXIT POINT
		}
		
		printf ("Multicast MAC Addresses:");

		int iCnt;
		for (iCnt = 0; iCnt < outMacAddrList.lCount; iCnt++)
		{
			if (iCnt % 2 == 0)
			{
				printf ("\n");
			}
			printf ("\t%2d - ", iCnt + 1);
			ShowMacAddr( NULL, outMacAddrList.aabtMacAddr[iCnt], NULL);
		}

		if (iCnt > 0)
		{
			printf ("\n");
		}
		else
		{
			printf (" no addresses set\n");
		}
	}

	// **********************************************************************
	// *** SET PIDS, PAUSE, DELETE IP PIDS (OPTIONAL)
	// **********************************************************************

	if (sParams.lIpPIDArraySize > 0)
	{
		hr = pB2C2FilterDataCtrl->AddIpPIDs (sParams.lIpPIDArraySize, sParams.lIpPIDArray);

		if (FAILED (hr))
		{
			fprintf (stderr,"b2settuner: Error: B2C2 Driver Data Ctrl. Interface AddIpPIDs method failed, error: 0x%8.8x\n", hr);

			return hr;	// *** FUNCTION EXIT POINT
		}

		// If timeout has been explicitly set, then only sleep and delete PIDs if timeout is non-zero.
	
		if (sParams.bTimeOutSet == TRUE)
		{
			if (sParams.lSleepLength != 0)
			{
				sleep (sParams.lSleepLength);
		
				// Delete Data PIDs.
		
				hr = pB2C2FilterDataCtrl->DeleteIpPIDs(sParams.lIpPIDArraySize, sParams.lIpPIDArray);
		
				if (FAILED (hr))
				{
					fprintf (stderr,"b2settuner: Error: B2C2 Driver Data Ctrl. Interface DeleteIpPIDs method failed, error: 0x%8.8x\n", hr);
		
					return hr;	// *** FUNCTION EXIT POINT
				}
			}
		}
	}

	// **********************************************************************
	// *** Delete / List IP PIDS
	// **********************************************************************

	if (sParams.lIpPidDelArraySize > 0)
	{
		hr = pB2C2FilterDataCtrl->DeleteIpPIDs (sParams.lIpPidDelArraySize, sParams.lIpPidDelArray);

		if (FAILED (hr))
		{
			fprintf (stderr,"b2settuner: Error: B2C2 Driver Data Ctrl. Interface DeleteIpPIDs method failed, error: 0x%8.8x\n", hr);

			return hr;	// *** FUNCTION EXIT POINT
		}
	}

	if (sParams.bShowIpPids)
	{
		long lOpen = 0;
		long lRunning = 0;
		long lMaxPids = 0;

		pB2C2FilterDataCtrl->GetMaxIpPIDCount (&lMaxPids);

		long *palPids = new long[lMaxPids];

		memset (palPids, 0x00, sizeof (long) * lMaxPids);
	
		hr = pB2C2FilterDataCtrl->GetIpState (&lOpen, &lRunning, &lMaxPids, palPids);

		if (FAILED (hr))
		{
			fprintf (stderr,"b2gettuner: Error: B2C2 Driver Data Ctrl. Interface GetIpState method failed, error: 0x%8.8x\n", hr);

			// Delete the PIDs array
			delete [] palPids;
			palPids = NULL;

			return hr;	// *** FUNCTION EXIT POINT
		}

		printf ("IP Open    : %ld\n", lOpen);

		printf ("IP Running : %ld\n", lRunning);

		int iCnt;

		// show PIDs
		for (iCnt = 0; (iCnt < lMaxPids) && (palPids[iCnt] != 0); iCnt++)
		{
			if (iCnt == 0)
			{
				printf ("IP PIDs    :");
			}
			if (iCnt % 3 == 0)
			{
				printf ("\n");
			}
			printf ("\t%2d: %ld(0x%lX)", iCnt+1, palPids[iCnt], palPids[iCnt]);
		}

		// Delete the PIDs array
		delete [] palPids;
		palPids = NULL;

		if (iCnt == 0)
		{
			printf ("No IP PIDs set!");
		}

		printf ("\n");
	} // if (sParam.bShowIpPids)

	// **********************************************************************
	// *** Add / Delete / List TS PIDS
	// **********************************************************************

	if (sParams.lTsPidAddArraySize > 0)
	{
		hr = pB2C2FilterDataCtrl->AddTsPIDs (sParams.lTsPidAddArraySize, sParams.lTsPidAddArray);

		if (FAILED (hr))
		{
			fprintf(stderr,"b2settuner: Error: B2C2 Driver Data Ctrl. Interface AddTsPIDs method failed, error: 0x%8.8x\n", hr);

			return hr;	// *** FUNCTION EXIT POINT
		}
	}

	if (sParams.lTsPidDelArraySize > 0)
	{
		hr = pB2C2FilterDataCtrl->DeleteTsPIDs (sParams.lTsPidDelArraySize, sParams.lTsPidDelArray);

		if (FAILED (hr))
		{
			fprintf(stderr,"b2settuner: Error: B2C2 Driver Data Ctrl. Interface DeleteTsPIDs method failed, error: 0x%8.8x\n", hr);

			return hr;	// *** FUNCTION EXIT POINT
		}
	}

	if (sParams.bShowTsPids)
	{
		long lOpen = 0;
		long lRunning = 0;
		long lMaxPids = 0;

		pB2C2FilterDataCtrl->GetMaxPIDCount (&lMaxPids);

		long *palPids = new long[lMaxPids];

		memset (palPids, 0x00, sizeof (long) * lMaxPids);
	
		hr = pB2C2FilterDataCtrl->GetTsState (&lOpen, &lRunning, &lMaxPids, palPids);

		if (FAILED (hr))
		{
			fprintf(stderr,"b2gettuner: Error: B2C2 Driver Data Ctrl. Interface GetTsState method failed, error: 0x%8.8x\n", hr);

			// Delete the PIDs array
			delete [] palPids;
			palPids = NULL;

			return hr;	// *** FUNCTION EXIT POINT
		}

		printf ("TS Open   : %ld\n", lOpen);

		printf ("TS Running: %ld\n", lRunning);

		int iCnt;

		// show PIDs
		// NOTE: TS PID can also be 0x0
		for (iCnt = 0; iCnt < lMaxPids; iCnt++)
		{
			if (iCnt == 0)
			{
				printf ("TS PIDs   :");
			}
			if (iCnt % 3 == 0)
			{
				printf ("\n");
			}
			printf ("\t%2d: %ld(0x%lX)", iCnt+1, palPids[iCnt], palPids[iCnt]);
		}

		// Delete the PIDs array
		delete [] palPids;
		palPids = NULL;

		if (iCnt == 0)
		{
			printf ("No TS PIDs set!");
		}

		printf ("\n");
	} //  (sParam.bShowTsPids)

	// **********************************************************************
	// *** Set / Get datagram table ID
	// **********************************************************************

	if (sParams.bSetTableId)
	{
		hr = pB2C2FilterDataCtrl->SetTableId (sParams.lTableId);

		if (FAILED (hr))
		{
			fprintf(stderr,"b2settuner: Error: B2C2 Driver Data Ctrl. Interface SetTableId method failed, error: 0x%8.8x\n", hr);

			return hr;	// *** FUNCTION EXIT POINT
		}
	}

	if (sParams.bGetTableId)
	{
		long lOutTableId;

		hr = pB2C2FilterDataCtrl->GetTableId (&lOutTableId);

		if (FAILED (hr))
		{
			fprintf(stderr,"b2settuner: Error: B2C2 Driver Data Ctrl. Interface GetTableId method failed, error: 0x%8.8x\n", hr);

			return hr;	// *** FUNCTION EXIT POINT
		}

		char strTableId[50];

		switch (lOutTableId)
		{
		case TABLE_ID_3E :		// DVB Standard; used at ATSC
			sprintf( strTableId, "3E");
			break;
		case TABLE_ID_3F :		// ATSC Standard
			sprintf( strTableId, "3F");
			break;
		case TABLE_ID_AUTO :	// Automatic or non ATSC device
			sprintf( strTableId, "AUTO");
			break;
		default :
			sprintf( strTableId, "unknown (%lu)", lOutTableId);
			break;
		}
		printf ("Datagram Table ID : %s\n", strTableId);
	}

	// **********************************************************************
	// *** Global Key Handling
	// **********************************************************************

	if (sParams.bSetGlobalKey)
	{
		hr = pB2C2FilterDataCtrl->AddKey (B2C2_KEY_GLOBAL,				// Indicate Set Global Key
										  0,							// PID argument is not used
										  sParams.abtGlobalKey, 
										  sizeof (sParams.abtGlobalKey));

		if (FAILED (hr))
		{
			fprintf(stderr,"b2settuner: Error: B2C2 Driver Data Ctrl. Interface SetGlobalKey method failed, error: 0x%8.8x\n", hr);

			return hr;	// *** FUNCTION EXIT POINT
		}
	}

	if (sParams.bDeleteGlobalKey)
	{
		hr = pB2C2FilterDataCtrl->DeleteKey (B2C2_KEY_GLOBAL,				// Indicate Set Global Key
											 0);							// PID argument is not used

		if (FAILED (hr))
		{
			fprintf(stderr,"b2settuner: Error: B2C2 Driver Data Ctrl. Interface DeleteKey (global) method failed, error: 0x%8.8x\n", hr);

			return hr;	// *** FUNCTION EXIT POINT
		}
	}

	// **********************************************************************
	// *** PID TSC Key Functions
	// **********************************************************************

	if (sParams.bKeyPurge)
	{
		hr = pB2C2FilterDataCtrl->PurgeKeys ();

		if (FAILED (hr))
		{
			fprintf(stderr,"b2settuner: Error: B2C2 Driver Data Ctrl. Interface PurgeKeys method failed, error: 0x%8.8x\n", hr);

			return hr;	// *** FUNCTION EXIT POINT
		}
	}

	if (sParams.lPidKeysAdd > 0)
	{
		for (int iCnt = 0; iCnt < sParams.lPidKeysAdd; iCnt++)
		{
			hr = pB2C2FilterDataCtrl->AddKey (sParams.alKpaTsc[iCnt],	// = Type
											  sParams.alKpaPid[iCnt],
											  sParams.aabtKpaKey[iCnt],
											  B2C2_SDK_FIXED_KEY_SIZE);
			if (FAILED (hr))
			{
				fprintf(stderr,"b2settuner: Error: B2C2 Driver Data Ctrl. Interface AddPidKey (0x%04lX,%02lX) method failed, error: 0x%8.8x\n", 
								sParams.alKpaPid[iCnt], sParams.alKpaTsc[iCnt], hr);

				return hr;	// *** FUNCTION EXIT POINT
			}
		}
	}

	if (sParams.lPidKeysDelete > 0)
	{
		for (int iCnt = 0; iCnt < sParams.lPidKeysDelete; iCnt++)
		{
			hr = pB2C2FilterDataCtrl->DeleteKey (sParams.alKpdTsc[iCnt],	// = Type
												 sParams.alKpdPid[iCnt]);

			if (FAILED (hr))
			{
				fprintf(stderr,"b2settuner: Error: B2C2 Driver Data Ctrl. Interface DeletePidKey (0x%04lX,%02lX) method failed, error: 0x%8.8X\n", 
								sParams.alKpdPid[iCnt], sParams.alKpdTsc[iCnt], hr);

				return hr;	// *** FUNCTION EXIT POINT
			}
		}
	}

	// **********************************************************************
	// *** PID ANY TSC Key Functions
	// **********************************************************************

	if (sParams.bAddAnyTscKey)
	{
		hr = pB2C2FilterDataCtrl->AddKey (B2C2_KEY_PID_TSC_ANY,	
										  sParams.lKaaPid,
										  sParams.abtKaaKey,
										  B2C2_SDK_FIXED_KEY_SIZE);
		if (FAILED (hr))
		{
			fprintf(stderr,"b2settuner: Error: B2C2 Driver Data Ctrl. Interface AddKey (ANY TSC,0x%04lX) method failed, error: 0x%8.8X\n", 
							sParams.lKaaPid, hr);

			return hr;	// *** FUNCTION EXIT POINT
		}
	}

	if (sParams.bDeleteAnyTscKey > 0)
	{
		hr = pB2C2FilterDataCtrl->DeleteKey (B2C2_KEY_PID_TSC_ANY,
											 0);					// PID argument is not used

		if (FAILED (hr))
		{
			fprintf(stderr,"b2settuner: Error: B2C2 Driver Data Ctrl. Interface DeleteKey (ANY TSC,0x%04lX) method failed, error: 0x%8.8X\n", 
							sParams.lKadPid, hr);
			return hr;	// *** FUNCTION EXIT POINT
		}
	}

	// **********************************************************************
	// *** List Keys
	// **********************************************************************

	if (sParams.bKeyList)
	{
		long lMaxKeys;

		hr = pB2C2FilterDataCtrl->GetKeyCount (&lMaxKeys, NULL, NULL, NULL);

		if (FAILED (hr))
		{
			fprintf(stderr,"b2settuner: Error: B2C2 Driver Data Ctrl. Interface GetKeyCount method failed, error: 0x%8.8x\n", hr);

			return hr;	// *** FUNCTION EXIT POINT
		}

		if (lMaxKeys <= 0)
		{
			printf ("Keys not supported!\n");
		}
		else
		{
			long lKeys = lMaxKeys;
			long *palType = new long[lKeys];
			long *palPid = new long[lKeys];

			hr = pB2C2FilterDataCtrl->GetKeysInUse (&lKeys, palType, palPid);
			
			if (SUCCEEDED(hr))
			{
				/*
				 *	Show current key information
				 */
				printf ("Keys : Max %ld, Current %ld", lMaxKeys, lKeys);

				if (lKeys > 0)
				{
					printf (" :");
					for (int iCnt = 0; iCnt < lKeys; iCnt++)
					{
						if (iCnt % 2 == 0)
						{
							printf ("\n");
						}
						switch (palType[iCnt])
						{
						// PID-TSC Keys					// up to 7 Keys possible; highest priority 
						case B2C2_KEY_PID_TSC_01 :
						case B2C2_KEY_PID_TSC_10 :
						case B2C2_KEY_PID_TSC_11 :
							printf ("\t%2d:0x%04lX(%ld),%s", 
									iCnt+1, palPid[iCnt], palPid[iCnt], Tsc2Bits((BYTE) palType[iCnt]));
							break;
						// PID-only Key					// 1 key only; if no Global Key is set 
						case B2C2_KEY_PID_TSC_ANY :
							printf ("\t%2d:0x%04lX(%ld),%s", 
									iCnt+1, palPid[iCnt], palPid[iCnt], "ANY");
							break;
						// Global Key					// 1 key only; if no PID-only Key is set 
						case B2C2_KEY_GLOBAL :
							printf ("\t%2d:%s", iCnt+1, "GLOBAL");
							break;
						// Unsupported type
						default:
							printf ("\t%2d:0x%04lX(%ld),0x%lX", 
									iCnt+1, palPid[iCnt], palPid[iCnt], palType[iCnt]);
							break;
						}
					}
				}
				printf ("\n");
			}

			delete [] palPid;
			delete [] palType;

			if (FAILED (hr))
			{
				fprintf(stderr,"b2settuner: Error: B2C2 Driver Data Ctrl. Interface GetKeysInUse method failed, error: 0x%8.8x\n", hr);

				return hr;	// *** FUNCTION EXIT POINT
			}
		}
	}

	return 0;
}

// **********************************************************************
// *** CHECK COMMAND LINE ARGUMENTS
// **********************************************************************

int CheckCommandLineArgs (int argc, char * argv[], SParams * psParams)
{
	long		lPid;
	long		lArgValueIndex;

	// Parse command line args, if any. Eliminate program name itself which
	// counts as one arg.  All options are required unless specified otherwise
	// (see Usage message and comments).

	if (--argc > 0)
	{
		// Process and set values for specified flags.

		for (int ii = 1; ii <= argc; ii += (1 + lArgValueIndex))
		{
			// Most arguments require a value, so we set lArgValueIndex as 1.
			// For unary arguments such as "-st", lArgValueIndex will explicitly be
			// set to 0.

			lArgValueIndex = 1;

			//
			// First put arguments which not require an aditional argument
			//
			if (strcmp (argv[ii], "-h") == 0 || strcmp (argv[ii], "-H") == 0 ||
				strcmp (argv[ii], "?") == 0)
			{
				fprintf (stderr, USAGE_MSG);

				return -1;	// *** FUNCTION EXIT POINT
			}
			else if (strcmp (argv[ii], "-st") == 0 || strcmp (argv[ii], "-ST") == 0)
			{
				// Optional Argument: Skip Tuning

				// *** UNARY ***

				psParams->bSkipTuning = TRUE;

				lArgValueIndex = 0;
			}
			else if (strcmp (argv[ii], "-ptl") == 0 || strcmp (argv[ii], "-PTL") == 0)
			{
				// OPTIONAL: Non-tuner specific option

		        psParams->bShowTsPids = TRUE;

				lArgValueIndex = 0;
			}
			else if (strcmp (argv[ii], "-pal") == 0 || strcmp (argv[ii], "-PAL") == 0)
			{
				// OPTIONAL: Non-tuner specific option

		        psParams->bShowAvPids = TRUE;

				lArgValueIndex = 0;
			}
			else if (strcmp (argv[ii], "-pil") == 0 || strcmp (argv[ii], "-PIL") == 0)
			{
				// OPTIONAL: Non-tuner specific option

		        psParams->bShowIpPids = TRUE;

				lArgValueIndex = 0;
			}
			else if (strcmp (argv[ii], "-pad") == 0 || strcmp (argv[ii], "-PAD") == 0)
			{
				// Non-tuner specific option

				// Delete Audio PIDs

				psParams->bAudioPidDel = TRUE;

				lArgValueIndex = 0;
			}
			else if (strcmp (argv[ii], "-pvd") == 0 || strcmp (argv[ii], "-PVD") == 0)
			{
				// Non-tuner specific option

				// Delete Video PIDs

				psParams->bVideoPidDel = TRUE;

				lArgValueIndex = 0;
			}
			else if (strcmp (argv[ii], "-umr") == 0 || strcmp (argv[ii], "-UMR") == 0)
			{
				// Non-tuner specific option

				// Restore Unicast MAC address

				psParams->bUcMacAddrRestore = TRUE;

				lArgValueIndex = 0;
			}
			else if (strcmp (argv[ii], "-uml") == 0 || strcmp (argv[ii], "-UML") == 0)
			{
				// Non-tuner specific option

				// List Unicast MAC address

				psParams->bUcMacAddrList = TRUE;

				lArgValueIndex = 0;
			}
			else if (strcmp (argv[ii], "-mml") == 0 || strcmp (argv[ii], "-MML") == 0)
			{
				// Non-tuner specific option

				// List Multicast MAC address

				psParams->bMcMacAddrList = TRUE;

				lArgValueIndex = 0;
			}
			else if (strcmp (argv[ii], "-gtid") == 0 || strcmp (argv[ii], "-GTID") == 0)
			{
				// Non-tuner specific option

				// Get datagram table ID

				psParams->bGetTableId = TRUE;

				lArgValueIndex = 0;
			}
			else if (strcmp (argv[ii], "-kl") == 0 || strcmp (argv[ii], "-KL") == 0)
			{
				// Optional Argument: Lists all PIDs and TSC field values keys are set for

				psParams->bKeyList = TRUE;

				lArgValueIndex = 0;
			}
			else if (strcmp (argv[ii], "-kp") == 0 || strcmp (argv[ii], "-KP") == 0)
			{
				// Optional Argument: Purge all keys set

				psParams->bKeyPurge = TRUE;

				lArgValueIndex = 0;
			}
			else if (strcmp (argv[ii], "-kgd") == 0 || strcmp (argv[ii], "-KGD") == 0)
			{
				// Optional Argument: Delete global key

				psParams->bDeleteGlobalKey = TRUE;

				lArgValueIndex = 0;
			}
			else if (strcmp (argv[ii], "-kad") == 0 || strcmp (argv[ii], "-KAD") == 0)
			{
				// Optional Argument: Delete key for given PID and ANY TSC field value;
	 			//                    e.g. 0x1234

				if (psParams->bDeleteAnyTscKey)
				{
					fprintf (stderr, "\nb2settuner: Error: At most 1 PID ANY TSC Keys to delete may be specified\n");

					fprintf (stderr, USAGE_MSG);

					return -1;	// *** FUNCTION EXIT POINT
				}

/*				// PID can be given as decimal or hex (0x) value

				Str2Pid (argv[ii + 1], &psParams->lKadPid);

				// No Error, indicate delete

*/				psParams->bDeleteAnyTscKey = TRUE;

				lArgValueIndex = 0;
			}
			//////////////////////////////////////////////////////////////////
			//
			//  From here all arguments that require an aditional argument,
			//  at first check if additional argument is defined
			//
			//////////////////////////////////////////////////////////////////
			else if (ii == argc)
			{
				fprintf (stderr, "\nb2settuner: Error: Incrorrect number of arguments\n");
				fprintf (stderr, USAGE_MSG);

				return -1;	// *** FUNCTION EXIT POINT
			}
            else if (strcmp (argv[ii], "-i") == 0 || strcmp (argv[ii], "-I") == 0)
			{
				// Required Option: Tuner Type.

				if ((strncmp (argv[ii + 1], "c", 1) == 0) ||
					(strncmp (argv[ii + 1], "C", 1) == 0))
				{
					psParams->eTunerType = TUNER_CABLE;
				}
				else if ((strncmp (argv[ii + 1], "s", 1) == 0) ||
						 (strncmp (argv[ii + 1], "S", 1) == 0))
				{
					psParams->eTunerType = TUNER_SATELLITE;
				}
				else if ((strncmp (argv[ii + 1], "t", 1) == 0) ||
						 (strncmp (argv[ii + 1], "T", 1) == 0))
				{
					psParams->eTunerType = TUNER_TERRESTRIAL;
				}
				else if ((strncmp (argv[ii + 1], "a", 1) == 0) ||
						 (strncmp (argv[ii + 1], "A", 1) == 0))
				{
					psParams->eTunerType = TUNER_ATSC;
				}
				else
				{
					fprintf (stderr, "\nb2settuner: Error: Incorrect tuner type value: %s\n", argv[ii + 1]);
					fprintf (stderr, USAGE_MSG);

					return -1;	// *** FUNCTION EXIT POINT
				}
				
				psParams->bTunerTypeSet = TRUE;
			}
			else if (strcmp (argv[ii], "-a") == 0 || strcmp (argv[ii], "-A") == 0)
			{
				// Required Option: Adapter Name for B2C2 MPEG2 card.

				strcpy (psParams->szAdapterName, argv[ii + 1]);
				psParams->bAdapterNameSet = TRUE;
			}
			else if (strcmp (argv[ii], "-f") == 0 || strcmp (argv[ii], "-F") == 0)
			{
				// Frequency in mhz: Applicable to both Cable and Satellite

				psParams->lFrequencyKhz = (int)(atof (argv[ii + 1]) * 1000);	// convert from mhz to khz
				psParams->bFrequencySet = TRUE;
			}
			else if (strcmp (argv[ii], "-fk") == 0 || strcmp (argv[ii], "-FK") == 0)
			{
				// Frequency in kHz: Applicable to both Cable and Satellite

				psParams->lFrequencyKhz = atoi (argv[ii + 1]);
				psParams->bFrequencySet = TRUE;
			}
			else if (strcmp (argv[ii], "-s") == 0 || strcmp (argv[ii], "-S") == 0)
			{
				// Symbol Rate: Applicable to both Cable and Satellite

				psParams->lSymbolRateKs = atoi (argv[ii + 1]);
				psParams->bSymbolRateSet = TRUE;
			}
			else if (strcmp (argv[ii], "-m") == 0 || strcmp (argv[ii], "-M") == 0)
			{
				// Modulation: Cable only

				long lModulationInput = atoi (argv[ii + 1]);

				switch (lModulationInput)
				{
				case 4:
					psParams->teModulation = QAM_4;
					break;

				case 16:
					psParams->teModulation = QAM_16;
					break;
				
				case 32:
					psParams->teModulation = QAM_32;
					break;
				
				case 64:
					psParams->teModulation = QAM_64;
					break;

				case 128:
					psParams->teModulation = QAM_128;
					break;
				
				case 256:
					psParams->teModulation = QAM_256;
					break;
				
				default:
					fprintf (stderr, "\nb2settuner: Error: Incorrect modulation value: %s\n", argv[ii + 1]);
					fprintf (stderr, USAGE_MSG);

					return -1;	// *** FUNCTION EXIT POINT
				}

				psParams->bModulationSet = TRUE;
			}
			else if (strcmp (argv[ii], "-l") == 0 || strcmp (argv[ii], "-L") == 0)
			{
				// LNB: Satellite only

				psParams->lLNB = atoi (argv[ii + 1]);

				psParams->bLNBSet = TRUE;
			}
			else if (strcmp (argv[ii], "-e") == 0 || strcmp (argv[ii], "-E") == 0)
			{
				// FEC: Satellite only

				if (strcmp (argv[ii + 1], "1/2") == 0)
				{
					psParams->teFEC = FEC_1_2;
				}
				else if (strcmp (argv[ii + 1], "2/3") == 0)
				{
					psParams->teFEC = FEC_2_3;
				}
				else if (strcmp (argv[ii + 1], "3/4") == 0)
				{
					psParams->teFEC = FEC_3_4;
				}
				else if (strcmp (argv[ii + 1], "5/6") == 0)
				{
					psParams->teFEC = FEC_5_6;
				}
				else if (strcmp (argv[ii + 1], "7/8") == 0)
				{
					psParams->teFEC = FEC_7_8;
				}
				else if ((strncmp (argv[ii + 1], "a", 1) == 0) ||
						 (strncmp (argv[ii + 1], "A", 1) == 0))
				{
					psParams->teFEC = FEC_AUTO;
				}
				else
				{
					fprintf (stderr, "\nb2settuner: Error: Incorrect value %s for option -e\n", argv[ii + 1]);
					fprintf (stderr, USAGE_MSG);

					return -1;	// *** FUNCTION EXIT POINT
				}

				psParams->bFECSet = TRUE;
			}
			else if (strcmp (argv[ii], "-g") == 0 || strcmp (argv[ii], "-G") == 0)
			{
				// Guard interval: DVB-T only

				if (strcmp (argv[ii + 1], "1/4") == 0)
				{
					psParams->lGuardInterval = GUARD_INTERVAL_1_4;
				}
				else if (strcmp (argv[ii + 1], "1/8") == 0)
				{
					psParams->lGuardInterval = GUARD_INTERVAL_1_8;
				}
				else if (strcmp (argv[ii + 1], "1/16") == 0)
				{
					psParams->lGuardInterval = GUARD_INTERVAL_1_16;
				}
				else if (strcmp (argv[ii + 1], "1/32") == 0)
				{
					psParams->lGuardInterval = GUARD_INTERVAL_1_32;
				}
				else
				{
					fprintf (stderr, "\nb2settuner: Error: Incorrect value %s for option -g\n", argv[ii + 1]);
					fprintf (stderr, USAGE_MSG);

					return -1;	// *** FUNCTION EXIT POINT
				}

				psParams->bGuardIntervalSet = TRUE;
			}
			else if (strcmp (argv[ii], "-b") == 0 || strcmp (argv[ii], "-B") == 0)
			{
				// Bandwidth: DVB-T only

				if (strcmp (argv[ii + 1], "6") == 0)
				{
					psParams->lBandwidth = 	BANDWIDTH_6_MHZ;
				}
				else if (strcmp (argv[ii + 1], "7") == 0)
				{
					psParams->lBandwidth = BANDWIDTH_7_MHZ;
				}
				else if (strcmp (argv[ii + 1], "8") == 0)
				{
					psParams->lBandwidth = BANDWIDTH_8_MHZ;
				}
				else
				{
					fprintf (stderr, "\nb2settuner: Error: Incorrect value %s for option -b\n", argv[ii + 1]);
					fprintf (stderr, USAGE_MSG);

					return -1;	// *** FUNCTION EXIT POINT
				}

				psParams->bBandwidthSet = TRUE;
			}
			else if (strcmp (argv[ii], "-o") == 0 || strcmp (argv[ii], "-O") == 0)
			{
				// Polarity: Satellite only

				if ((strncmp (argv[ii + 1], "h", 1) == 0) ||
					(strncmp (argv[ii + 1], "H", 1) == 0))
				{
					psParams->tePolarity = POLARITY_HORIZONTAL;
				}
				else if ((strncmp (argv[ii + 1], "v", 1) == 0) ||
						 (strncmp (argv[ii + 1], "V", 1) == 0))
				{
					psParams->tePolarity = POLARITY_VERTICAL;
				}
				else if ((strncmp (argv[ii + 1], "n", 1) == 0) ||
						 (strncmp (argv[ii + 1], "N", 1) == 0))
				{
					psParams->tePolarity = POLARITY_LNB_NO_POWER;
				}
				else
				{
					fprintf (stderr, "\nb2settuner: Error: Incorrect value %s for option -o\n", argv[ii + 1]);
					fprintf (stderr, USAGE_MSG);

					return -1;	// *** FUNCTION EXIT POINT
				}

				psParams->bPolaritySet = TRUE;
			}
			else if (strcmp (argv[ii], "-k") == 0 || strcmp (argv[ii], "-K") == 0)
			{
				// LNB Selection: Satellite only

				long lLSInput = atoi (argv[ii + 1]);

				if (lLSInput == 0)
				{
					psParams->teLNBSelection = LNB_SELECTION_0;
				}
				else if (lLSInput == 22)
				{
					psParams->teLNBSelection = LNB_SELECTION_22;
				}
				else if (lLSInput == 33)
				{
					psParams->teLNBSelection = LNB_SELECTION_33;
				}
				else if (lLSInput == 44)
				{
					psParams->teLNBSelection = LNB_SELECTION_44;
				}
				else
				{
					fprintf (stderr, "\nb2settuner: Error: Incorrect value %s for option -k\n", argv[ii + 1]);
					fprintf (stderr, USAGE_MSG);

					return -1;	// *** FUNCTION EXIT POINT
				}

				psParams->bLNBSelectionSet = TRUE;
			}
			else if (strcmp (argv[ii], "-d") == 0 || strcmp (argv[ii], "-D") == 0)
			{
				// Diseqc: Satellite only

				psParams->nDiSEqCLength = 0;

				if (strncmp (argv[ii + 1], "n", 1) == 0  ||
					strncmp (argv[ii + 1], "N", 1) == 0)
				{
					psParams->teDiseqc = DISEQC_NONE;
				}
				else if (strcmp (argv[ii + 1], "a") == 0 ||
						 strcmp (argv[ii + 1], "A") == 0)
				{
					psParams->teDiseqc = DISEQC_SIMPLE_A;
				}
				else if (strcmp (argv[ii + 1], "b") == 0 ||
						 strcmp (argv[ii + 1], "B") == 0)
				{
					psParams->teDiseqc = DISEQC_SIMPLE_B;
				}
				else if (strcmp (argv[ii + 1], "a/a") == 0 ||
						 strcmp (argv[ii + 1], "A/A") == 0)
				{
					psParams->teDiseqc = DISEQC_LEVEL_1_A_A;
				}
				else if (strcmp (argv[ii + 1], "b/a") == 0 ||
						 strcmp (argv[ii + 1], "B/A") == 0)
				{
					psParams->teDiseqc = DISEQC_LEVEL_1_B_A;
				}
				else if (strcmp (argv[ii + 1], "a/b") == 0 ||
						 strcmp (argv[ii + 1], "A/B") == 0)
				{
					psParams->teDiseqc = DISEQC_LEVEL_1_A_B;
				}
				else if (strcmp (argv[ii + 1], "b/b") == 0 ||
						 strcmp (argv[ii + 1], "B/B") == 0)
				{
					psParams->teDiseqc = DISEQC_LEVEL_1_B_B;
				}
				else if (strcmp (argv[ii + 1], "x") == 0 ||
						 strcmp (argv[ii + 1], "X") == 0)
				{
					psParams->teDiseqc = DISEQC_NONE;
					psParams->nDiSEqCLength = atoi (argv[ii + 2]);

					if (psParams->nDiSEqCLength < sizeof (psParams->abtDiSEqCCommand))
					{
						if (!Str2MultiBytes (argv[ii + 3], 
											psParams->abtDiSEqCCommand, psParams->nDiSEqCLength))
						{
							fprintf (stderr, "\nb2settuner: Error: DiSEqC length %d, DiSEqC Command \'%s\'\n",
											argv[ii + 2], argv[ii + 3]);

							fprintf (stderr, USAGE_MSG);

							return -1;	// *** FUNCTION EXIT POINT
						}
					}
					else
					{
						fprintf (stderr, "\nb2settuner: Length Error: DiSEqC length %d, DiSEqC Command \'%s\'\n",
										argv[ii + 2], argv[ii + 3]);

						fprintf (stderr, USAGE_MSG);

						return -1;	// *** FUNCTION EXIT POINT
					}

					// Indicate that three arguments used
					lArgValueIndex = 3;
				}
				else
				{
					fprintf (stderr, "\nb2settuner: Error: Incorrect value %s for option -d\n", argv[ii + 1]);
					fprintf (stderr, USAGE_MSG);

					return -1;	// *** FUNCTION EXIT POINT
				}

				psParams->bDiseqcSet = TRUE;
			}
			else if (strcmp (argv[ii], "-pd") == 0 || strcmp (argv[ii], "-PD") == 0 ||
					 strcmp (argv[ii], "-pia") == 0 || strcmp (argv[ii], "-PIA") == 0)
			{
				// Non-tuner specific option

				// IP PIDs

				if (psParams->lIpPIDArraySize == psParams->lSysIpPIDSize)
				{
					fprintf (stderr, "\nb2settuner: Error: At most %ld IP PIDs may be specified\n",
						psParams->lSysIpPIDSize);

					fprintf (stderr, USAGE_MSG);

					return -1;	// *** FUNCTION EXIT POINT
				}

				// convert from hex or decimal

				Str2Pid (argv[ii + 1], &lPid);

				// add to array

				psParams->lIpPIDArray[psParams->lIpPIDArraySize++] = lPid;
			}
			else if (strcmp (argv[ii], "-ma") == 0 || strcmp (argv[ii], "-MA") == 0 ||
					 strcmp (argv[ii], "-mma") == 0 || strcmp (argv[ii], "-MMA") == 0)
			{
				// Optional Argument: Add Multicast MAC Address

				// Check syntax only here.

				if( psParams->stMcMacAddressesSet.lCount >= B2C2_SDK_MAC_ADDR_LIST_MAX)
				{
					fprintf (stderr, "\nb2settuner: Error: To many MAC addresses set; %d max\n", B2C2_SDK_MAC_ADDR_LIST_MAX);

				} else if (strlen (argv[ii + 1]) != 14 ||
					(strncmp (argv[ii + 1], "0x", 2) != 0 && strncmp (argv[ii + 1], "0X", 2) != 0))
				{
					fprintf (stderr, "\nb2settuner: Error: Invalid Multiast MAC address syntax\n");

					fprintf (stderr, USAGE_MSG);

					return -1;	// *** FUNCTION EXIT POINT
				}
				else
				{
					// Length is correct.  Chop "0x" prefix.

					//strcpy (psParams->szMacAddress, argv[ii + 1] + 2);
					Str2Mac (psParams->stMcMacAddressesSet.aabtMacAddr[psParams->stMcMacAddressesSet.lCount++],
						     argv[ii + 1] + 2);
				}
			}
			else if (strcmp (argv[ii], "-mmd") == 0 || strcmp (argv[ii], "-MMD") == 0)
			{
				// Optional Argument: Add Multicast MAC Address

				// Check syntax only here.

				if( psParams->stMcMacAddressesDel.lCount >= B2C2_SDK_MAC_ADDR_LIST_MAX)
				{
					fprintf (stderr, "\nb2settuner: Error: To many MAC addresses set; %d max\n", B2C2_SDK_MAC_ADDR_LIST_MAX);

				} else if (strlen (argv[ii + 1]) != 14 ||
					(strncmp (argv[ii + 1], "0x", 2) != 0 && strncmp (argv[ii + 1], "0X", 2) != 0))
				{
					fprintf (stderr, "\nb2settuner: Error: Invalid Multiast MAC address syntax\n");

					fprintf (stderr, USAGE_MSG);

					return -1;	// *** FUNCTION EXIT POINT
				}
				else
				{
					// Length is correct.  Chop "0x" prefix.

					//strcpy (psParams->szMacAddress, argv[ii + 1] + 2);
					Str2Mac (psParams->stMcMacAddressesDel.aabtMacAddr[psParams->stMcMacAddressesDel.lCount++],
						     argv[ii + 1] + 2);
				}
			}
			else if (strcmp (argv[ii], "-ums") == 0 || strcmp (argv[ii], "-UMS") == 0)
			{
				// Optional Argument: Set Unicase MAC Address

				// Check syntax only here.

				if ( psParams->bUcMacAddrSet)
				{
					fprintf (stderr, "\nb2settuner: Error: Unicast MAC address already defined\n");

					fprintf (stderr, USAGE_MSG);
				}
				else if (strlen (argv[ii + 1]) != 14 ||
					(strncmp (argv[ii + 1], "0x", 2) != 0 && strncmp (argv[ii + 1], "0X", 2) != 0))
				{
					fprintf (stderr, "\nb2settuner: Error: Invalid Unicast MAC address syntax\n");

					fprintf (stderr, USAGE_MSG);

					return -1;	// *** FUNCTION EXIT POINT
				}
				else
				{
					// Length is correct.  Chop "0x" prefix.

					//strcpy (psParams->szMacAddress, argv[ii + 1] + 2);
					Str2Mac (psParams->abtUcMacAddress,
						     argv[ii + 1] + 2);
				}

				psParams->bUcMacAddrSet = TRUE;
			}
			else if (strcmp (argv[ii], "-t") == 0 || strcmp (argv[ii], "-T") == 0)
			{
				// Optional Argument: Duration (time in seconds) for dump to file.

				psParams->lSleepLength = atoi (argv[ii + 1]);

				psParams->bTimeOutSet = TRUE;
			}
			else if (strcmp (argv[ii], "-r") == 0 || strcmp (argv[ii], "-R") == 0)
			{
				// OPTIONAL: Non-tuner specific option

		        psParams->nRepeatLockTest = atoi (argv[ii + 1]);
			}
			else if (strcmp (argv[ii], "-pta") == 0 || strcmp (argv[ii], "-PTA") == 0)
			{
				// Non-tuner specific option

				// IP PIDs

				if (psParams->lTsPidAddArraySize == psParams->lSysTsPIDSize)
				{
					fprintf (stderr, "\nb2settuner: Error: At most %ld TS PIDs may be specified\n",
						psParams->lSysTsPIDSize);

					fprintf (stderr, USAGE_MSG);

					return -1;	// *** FUNCTION EXIT POINT
				}

				// convert from hex or decimal

				Str2Pid (argv[ii + 1], &lPid);

				// add to array

				psParams->lTsPidAddArray[psParams->lTsPidAddArraySize++] = lPid;
			}
			else if (strcmp (argv[ii], "-ptd") == 0 || strcmp (argv[ii], "-PTD") == 0)
			{
				// Non-tuner specific option

				// Delete TS PIDs

				if (psParams->lTsPidDelArraySize == psParams->lSysTsPIDSize)
				{
					fprintf (stderr, "\nb2settuner: Error: At most %ld TS PIDs to delete may be specified\n",
						psParams->lSysTsPIDSize);

					fprintf (stderr, USAGE_MSG);

					return -1;	// *** FUNCTION EXIT POINT
				}

				// convert from hex or decimal

				Str2Pid (argv[ii + 1], &lPid);

				// add to array

				psParams->lTsPidDelArray[psParams->lTsPidDelArraySize++] = lPid;
			}
			else if (strcmp (argv[ii], "-paa") == 0 || strcmp (argv[ii], "-PAA") == 0)
			{
				// Non-tuner specific option

				// Add Audio PIDs

				if (psParams->lAudioPidAdd > 0)
				{
					fprintf (stderr, "\nb2settuner: Error: Only one Audio PIDs to add may be specified\n");

					fprintf (stderr, USAGE_MSG);

					return -1;	// *** FUNCTION EXIT POINT
				}

				// convert from hex or decimal

				Str2Pid (argv[ii + 1], &lPid);

				// add to array

				psParams->lAudioPidAdd = lPid;
				psParams->bAvPidAddSet = TRUE;
			}
			else if (strcmp (argv[ii], "-pva") == 0 || strcmp (argv[ii], "-PVA") == 0)
			{
				// Non-tuner specific option

				// Add Video PIDs

				if (psParams->lVideoPidAdd > 0)
				{
					fprintf (stderr, "\nb2settuner: Error: Only one Video PIDs to add may be specified\n");

					fprintf (stderr, USAGE_MSG);

					return -1;	// *** FUNCTION EXIT POINT
				}

				// convert from hex or decimal

				Str2Pid (argv[ii + 1], &lPid);

				// add to array

				psParams->lVideoPidAdd = lPid;
				psParams->bAvPidAddSet = TRUE;
			}
			else if (strcmp (argv[ii], "-pid") == 0 || strcmp (argv[ii], "-PID") == 0)
			{
				// Non-tuner specific option

				// Delete IP PIDs

				if (psParams->lIpPidDelArraySize == psParams->lSysIpPIDSize)
				{
					fprintf (stderr, "\nb2settuner: Error: At most %ld IP PIDs to delete may be specified\n",
						psParams->lSysIpPIDSize);

					fprintf (stderr, USAGE_MSG);

					return -1;	// *** FUNCTION EXIT POINT
				}

				// convert from hex or decimal

				Str2Pid (argv[ii + 1], &lPid);

				// add to array

				psParams->lIpPidDelArray[psParams->lIpPidDelArraySize++] = lPid;
			}
			else if (strcmp (argv[ii], "-stid") == 0 || strcmp (argv[ii], "-STID") == 0)
			{
				// Non-tuner specific option

				// Set datagram table ID

				if (strcmp (argv[ii + 1], "3E") == 0 || strcmp (argv[ii + 1], "3e") == 0)
				{
					psParams->lTableId = TABLE_ID_3E;

					// Indicate to set table ID
					psParams->bSetTableId = TRUE;
				}
				else if (strcmp (argv[ii + 1], "3F") == 0 || strcmp (argv[ii + 1], "3f") == 0)
				{
					psParams->lTableId = TABLE_ID_3F;

					// Indicate to set table ID
					psParams->bSetTableId = TRUE;
				}
				else
				{
					fprintf (stderr, "\nb2settuner: Error: Unknown Table ID value \'%s\'\n",
									 argv[ii + 1]);

					fprintf (stderr, USAGE_MSG);

					return -1;	// *** FUNCTION EXIT POINT
				}
			}
			else if (strcmp (argv[ii], "-kgs") == 0 || strcmp (argv[ii], "-KGS") == 0)
			{
				// Optional Argument: Set Global Key

				// Check syntax only here.

				//strcpy (psParams->szMacAddress, argv[ii + 1] + 2);
				if (!Str2MultiBytes (argv[ii + 1], 
									 psParams->abtGlobalKey, sizeof (psParams->abtGlobalKey)))
				{
					fprintf (stderr, "\nb2settuner: Error: Global Key value \'%s\'\n",
									 argv[ii + 1]);

					fprintf (stderr, USAGE_MSG);

					return -1;	// *** FUNCTION EXIT POINT
				}
				
				psParams->bSetGlobalKey = TRUE;
			}
			//////////////////////////////////////////////////////////////////
			//
			//  From here all arguments that require TWO aditional argument,
			//  at first check if additional arguments are defined
			//
			//////////////////////////////////////////////////////////////////
			else if ((ii+1) >= argc)
			{
				fprintf (stderr, "\nb2settuner: Error: Incrorrect number of arguments\n");
				fprintf (stderr, USAGE_MSG);

				return -1;	// *** FUNCTION EXIT POINT
			}
			else if (strcmp (argv[ii], "-kpd") == 0 || strcmp (argv[ii], "-KPD") == 0)
			{
				// Optional Argument: Delete key for given PID and TSC field value;
	 			//                    e.g. 0x1234 10

				if (psParams->lPidKeysDelete >= MAX_PID_KEYS)
				{
					fprintf (stderr, "\nb2settuner: Error: At most %d PID Keys to delete may be specified\n",
									 MAX_PID_KEYS);

					fprintf (stderr, USAGE_MSG);

					return -1;	// *** FUNCTION EXIT POINT
				}

				// PID can be given as decimal or hex (0x) value

				Str2Pid (argv[ii + 1], &psParams->alKpdPid[psParams->lPidKeysDelete]);

	 			//   Valid TSC values are: 01, 10, 11

				if (!TscStr2Type( argv[ii + 2], &psParams->alKpdTsc[psParams->lPidKeysDelete]))
				{
					fprintf (stderr, "\nb2settuner: Error: Delete Key TSC value \'%s\'\n",
									 argv[ii + 2]);

					fprintf (stderr, USAGE_MSG);

					return -1;	// *** FUNCTION EXIT POINT
				}

				// No Error, increase counter

				psParams->lPidKeysDelete ++;

				// Indicate that two arguments used
				lArgValueIndex = 2;
			}
			else if (strcmp (argv[ii], "-kas") == 0 || strcmp (argv[ii], "-KAS") == 0)
			{
				// Optional Argument: Add key for given PID and ANY TSC field value
				// 	                  e.g. 0x1234 1122334455667788

				if (psParams->bAddAnyTscKey)
				{
					fprintf (stderr, "\nb2settuner: Error: At most 1 PID any TSC Keys to add may be specified\n");

					fprintf (stderr, USAGE_MSG);

					return -1;	// *** FUNCTION EXIT POINT
				}

     			// PID can be given as decimal or hex (0x) value

				Str2Pid (argv[ii + 1], &psParams->lKaaPid);

				// Key value is given as 8 pairs of hex digits

				if (!Str2MultiBytes (argv[ii + 2],
									 psParams->abtKaaKey, 
									 sizeof (psParams->abtKaaKey)))
				{
					fprintf (stderr, "\nb2settuner: Error: PID ANY TSC Key value \'%s\'\n",
									 argv[ii + 3]);

					fprintf (stderr, USAGE_MSG);

					return -1;	// *** FUNCTION EXIT POINT
				}

				// No Error, indicate Add

				psParams->bAddAnyTscKey = TRUE;

				// Indicate that two arguments used
				lArgValueIndex = 2;
			}
			//////////////////////////////////////////////////////////////////
			//
			//  From here all arguments that require THREE aditional argument,
			//  at first check if additional arguments aredefined
			//
			//////////////////////////////////////////////////////////////////
			else if ((ii+2) >= argc)
			{
				fprintf (stderr, "\nb2settuner: Error: Incrorrect number of arguments\n");
				fprintf (stderr, USAGE_MSG);

				return -1;	// *** FUNCTION EXIT POINT
			}
			else if (strcmp (argv[ii], "-kpa") == 0 || strcmp (argv[ii], "-KPA") == 0)
			{
				// Optional Argument: Add key for given PID and TSC field value
				// 	                  e.g. 0x1234 10 1122334455667788

				if (psParams->lPidKeysAdd >= MAX_PID_KEYS)
				{
					fprintf (stderr, "\nb2settuner: Error: At most %d PID Keys to add may be specified\n",
									 MAX_PID_KEYS);

					fprintf (stderr, USAGE_MSG);

					return -1;	// *** FUNCTION EXIT POINT
				}

     			// PID can be given as decimal or hex (0x) value

				Str2Pid (argv[ii + 1], &psParams->alKpaPid[psParams->lPidKeysAdd]);

				// Possible TSC values are: 01, 10, 11

				if (!TscStr2Type( argv[ii + 2], &psParams->alKpaTsc[psParams->lPidKeysAdd]))
				{
					fprintf (stderr, "\nb2settuner: Error: Add Key TSC value \'%s\'\n",
									 argv[ii + 2]);

					fprintf (stderr, USAGE_MSG);

					return -1;	// *** FUNCTION EXIT POINT
				}

				// Key value is given as 8 pairs of hex digits

				if (!Str2MultiBytes (argv[ii + 3],
									 psParams->aabtKpaKey[psParams->lPidKeysAdd], 
									 sizeof (psParams->aabtKpaKey[psParams->lPidKeysAdd])))
				{
					fprintf (stderr, "\nb2settuner: Error: PID Key value \'%s\'\n",
									 argv[ii + 3]);

					fprintf (stderr, USAGE_MSG);

					return -1;	// *** FUNCTION EXIT POINT
				}

				// No Error, increase counter

				psParams->lPidKeysAdd ++;
				
				// Indicate that three arguments used
				lArgValueIndex = 3;
			}
			else
			{
				// Catch-all error

				fprintf (stderr, "\nb2settuner: Error: Unrecognized option flag %s\n", argv[ii]);
				fprintf (stderr, USAGE_MSG);

				return -1;	// *** FUNCTION EXIT POINT
			}
		}
	}

	return 0;
}
