/*********************************************************************
*
*		Filename:	b2status.cpp
*
*		Description:	
*						
*		History:		
*						
*
*		Copyright (c) 2005 B2C2, Incorporated
*		
*
*********************************************************************/
// Copyright (c) 1998-2005 B2C2, Incorporated.  All Rights Reserved.
//
// THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF B2C2, INCORPORATED.
// The copyright notice above does not evidence any
// actual or intended publication of such source code.
//
// This file is proprietary source code of B2C2, Incorporated. and is released pursuant to and
// subject to the restrictions of the non-disclosure agreement and license contract entered
// into by the parties.

//
// File: b2status.cpp
//

#ifndef TRUE
#define TRUE 1
#endif

#ifndef FALSE
#define FALSE 0
#endif

#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

#define	USAGE_MSG "\
\n\
Usage: b2status <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\
\n\
Optional Arguments:\n\
-all   Show all information for selected adapter (default)\n\
-tc    Show tuner capabilities\n\
-ti    Show tuner information\n\
-ip    Show IP information\n\
-ts    Show TS information\n\
-av    Show A/V information\n\
-h     Help\n"

//
// Enumeration value description
//
typedef struct tagENUM_NAME
{
	long lValue;
	char * szName;
} ENUM_NAME, *PENUM_NAME;

// Definition of enums; last szName member value must be NULL

static ENUM_NAME sTunerTypes[] = 
{
	{ TUNER_SATELLITE,	"Satellite" },
	{ TUNER_CABLE,		"Cable" },
	{ TUNER_TERRESTRIAL,"Terrestrial DVB" },
	{ TUNER_ATSC,		"Terrestrial ATSC" },
	{ 0,				NULL } 		//Must be last line!
};

static ENUM_NAME sFecTypes[] = 
{
	{ FEC_1_2,	"1/2" },
	{ FEC_2_3,	"2/3" },
	{ FEC_3_4,	"3/4" },
	{ FEC_5_6,	"5/6" },
	{ FEC_7_8,	"7/8" },
	{ FEC_AUTO,	"AUTO" },
	{ 0,		NULL } 		//Must be last line!
};

static ENUM_NAME sBandwidthTypes[] = 
{
	{ BANDWIDTH_6_MHZ,	"6" },
	{ BANDWIDTH_7_MHZ,	"7" },
	{ BANDWIDTH_8_MHZ,	"8" },
	{ 0,					NULL } 				//Must be last line!
};

static ENUM_NAME sGuardIntervalTypes[] = 
{
	{ GUARD_INTERVAL_1_32,	"1/32" },
	{ GUARD_INTERVAL_1_16,	"1/16" },
	{ GUARD_INTERVAL_1_8,	"1/8" },
	{ GUARD_INTERVAL_1_4,	"1/4" },
	{ GUARD_INTERVAL_AUTO,	"AUTO" },
	{ 0,					NULL } 				//Must be last line!
};

static ENUM_NAME sPolarityTypes[] = 
{
	{ POLARITY_HORIZONTAL,	"HORIZONTAL" },
	{ POLARITY_VERTICAL,	"VERTICAL" },
 	//if no LNB power is needed
	{ POLARITY_LNB_NO_POWER,"NO_LNB_POWER" },
	{ 0,					NULL } 				//Must be last line!
};

static ENUM_NAME sLnbSelTypes[] = 
{
	{ LNB_SELECTION_0,		"0" },
	{ LNB_SELECTION_22,		"22" },
	{ LNB_SELECTION_33,		"33" },
	{ LNB_SELECTION_44,		"44" },
	{ 0,					NULL } 				//Must be last line!
};

static ENUM_NAME sDiseqcTypes[] = 
{
	{ DISEQC_NONE,			"NONE" },
	{ DISEQC_SIMPLE_A,		"SIMPLE_A" },
	{ DISEQC_SIMPLE_B,		"SIMPLE_B" },
	{ DISEQC_LEVEL_1_A_A,	"LEVEL_1_A_A" },
	{ DISEQC_LEVEL_1_B_A,	"LEVEL_1_B_A" },
	{ DISEQC_LEVEL_1_A_B,	"LEVEL_1_A_B" },
	{ DISEQC_LEVEL_1_B_B,	"LEVEL_1_B_B" },
	{ 0, 					NULL } 				//Must be last line!
};

static ENUM_NAME sModulationTypes[] = 
{
	{ QAM_4,	"QAM_4" },
	{ QAM_16,	"QAM_16" },
	{ QAM_32,	"QAM_32" },
	{ QAM_64,	"QAM_64" },
	{ QAM_128,	"QAM_128" },
	{ QAM_256,	"QAM_256" },
	{ 0,		NULL } 		//Must be last line!
};

// Added by ALF
// copied from "../sky2pcavsrc/constants.h"; better to move constant to b2c2_defs.h
#define SKY2PC_E_OUTOFLOCK				0x90010115

#define B2C2_SDK_AVAIL_FEQUENCY			(1L << 5)
#define B2C2_SDK_AVAIL_SYMB_RATE		(1L << 6)
#define B2C2_SDK_AVAIL_POLARITY			(1L << 7)
#define	B2C2_SDK_AVAIL_MODULATION		(1L << 8)
#define B2C2_SDK_AVAIL_DISEQC			(1L << 9)
#define B2C2_SDK_AVAIL_LNB_FREQ			(1L << 10)
#define B2C2_SDK_AVAIL_LNB_SWITCH_FREQ	(1L << 11)
#define	B2C2_SDK_AVAIL_GUARD_INTERVAL	(1L << 12)
#define	B2C2_SDK_AVAIL_BANDWIDTH		(1L << 13)

#define MODULATION_SUPPORTED(v)			((v>=(1L << QAM_4))?TRUE:FALSE)

#define MAX_REASONABLE_SUPPORTED_PIDS	64

#define PID_LIST_COLUMN					3

#define PID_KEY_LIST_COLUMN				2

// Struct used for passing tuner parameters
typedef struct
{	
	char			szAdapterName[256];
	bool			bAdapterNameSet;
	bool			bShowTunerCap;
	bool			bShowTunerInfo;
	bool			bShowIpInfo;
	bool			bShowTsInfo;
	bool			bShowAvInfo;
	bool			bShowAllInfo;
} SParams;

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

void printMac (unsigned char* getMac)
{
	int i;

	for (i = 0; i < B2C2_SDK_MAC_ADDR_SIZE - 1; i++)
	{
		printf ("%02hX:", getMac[i]);
	}
	printf ("%02hX", getMac[B2C2_SDK_MAC_ADDR_SIZE - 1]);
}


// **********************************************************************
// *** ShowFlag ()
// **********************************************************************
void ShowFlag (char* szLabel, int bSupported, char* szFlag)
{
	printf ("%s: %s%s\n", szLabel, (bSupported) ? "" : "NOT ", szFlag);
}

// **********************************************************************
// *** ShowValue () - long value
// **********************************************************************
void ShowValue (char* szLabel, char* szUnit, int bSupported, long lValue)
{
	if (bSupported)
	{
		printf ("%s: %ld %s\n", szLabel, lValue, szUnit);
	}
	else
	{
		printf ("%s: n/s\n", szLabel);
	}
}

// **********************************************************************
// *** ShowValue () - long - long
// **********************************************************************
void ShowValue (char* szLabel, char* szUnit, int bSupported, long lFrom, long lTo)
{
	if (bSupported)
	{
		printf ("%s: %ld%s - %ld%s\n", szLabel, lFrom, szUnit, lTo, szUnit);
	}
	else
	{
		printf ("%s: n/s\n", szLabel);
	}
}

// **********************************************************************
// *** ShowValue () - float value
// **********************************************************************
void ShowValue (char* szLabel, char* szUnit, int bSupported, float fValue)
{
	if (bSupported)
	{
		printf ("%s: %f %s\n", szLabel, fValue, szUnit);
	}
	else
	{
		printf ("%s: n/s\n", szLabel);
	}
}

// **********************************************************************
// *** ShowValue () - enum value
// **********************************************************************
void ShowValue (char* szLabel, char* szUnit, int bSupported, long lValue, PENUM_NAME pEnumNames)
{
	if (bSupported)
	{
		int nPos;
		for (nPos = 0; pEnumNames[nPos].szName != NULL; nPos++)
		{
			if (pEnumNames[nPos].lValue == lValue)
			{
				printf ("%s: %s %s\n", szLabel, pEnumNames[nPos].szName, szUnit);
				break;
			}
		}

		if (pEnumNames[nPos].szName == NULL)
		{
			// Value not found in list
			printf ("%s: unknown value (%ld)\n", szLabel, lValue);
		}
	}
	else
	{
		printf ("%s: n/s\n", szLabel);
	}
}

// **********************************************************************
// *** ShowValue () - bool value
// **********************************************************************
void ShowValue (char *szLabel, char *szUnit, int bSupported, bool bValue, char *szTrue, char *szFalse)
{
	if (bSupported)
	{
		printf ("%s: %s %s\n", szLabel, bValue ? szTrue : szFalse, szUnit);
	}
	else
	{
		printf ("%s: n/s\n", szLabel);
	}
}

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

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

	return strTscBits;
}

// **********************************************************************
// *** ExitOnFailed ()
// **********************************************************************
void ExitOnFailed (HRESULT hr, char* pFuncName)
{
	if (FAILED (hr))
	{
	    fprintf (stderr,"b2status: Error: B2C2 Driver Data Ctrl. Interface %s() method failed, error: 0x%8.8x\n",
					   pFuncName, hr);
		exit (1);
    }
}

// **********************************************************************
// *** main ()
// **********************************************************************
int main (int argc, char * argv[])
{
	IB2C2MPEG2TunerCtrl3	*pB2C2FilterTunerCtrl;
	IB2C2MPEG2DataCtrl4		*pB2C2FilterDataCtrl;
	IB2C2MPEG2AVCtrl2		*pB2C2FilterAvCtrl;
	HRESULT					hr;
	SParams					sParams;

#if defined __linux__
	// Need to be root to run; to test use effective user ID
	if (geteuid () != (uid_t) 0)
	{
		fprintf (stderr, "\nb2status: Error: Must be root to run this command\n");
		return 1;	// *** FUNCTION EXIT POINT
	}
#endif //defined __linux__

	// Initializations.
	sParams.bAdapterNameSet = FALSE;
	strcpy (sParams.szAdapterName, "");

	// Defaults.
	sParams.bShowAllInfo = FALSE;
	sParams.bShowTunerCap = FALSE;
	sParams.bShowTunerInfo = FALSE;
	sParams.bShowTsInfo = FALSE;
	sParams.bShowIpInfo = FALSE;
	sParams.bShowAvInfo = FALSE;

	// 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, "\nb2status: Error: Adapter name has not been specified\n");
		fprintf (stderr, USAGE_MSG);
		return 1;	// *** FUNCTION EXIT POINT
	}
#endif //__linux__

	// Set show all in case of no optional argument was defined
	if (   !sParams.bShowAllInfo
		&& !sParams.bShowTunerCap
		&& !sParams.bShowTunerInfo
		&& !sParams.bShowIpInfo
		&& !sParams.bShowTsInfo
		&& !sParams.bShowAvInfo)
	{
		sParams.bShowAllInfo = TRUE;
	}
		
	// Emulate instantiating of DirectShow filter.

	B2C2MPEG2Adapter b2c2Adapter (sParams.szAdapterName);

	// Emulate querying for COM interfaces.
	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
	}

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

	/* Not necessary for query-only application
	hr = pB2C2FilterDataCtrl->Initialize ();

	if (FAILED (hr))
	{
		fprintf (stderr,"b2status: Error: B2C2 Driver Data Ctrl. Interface Initialize method failed, error: 0x%8.8x\n", hr);
		return hr;	// *** FUNCTION EXIT POINT
	}
	*/

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

	//
	// Tuner Type
	//
	tTunerCapabilities 	tunerCaps;
	long  				lTunerCapsSize = sizeof (tunerCaps);
	memset (&tunerCaps, 0xFF, lTunerCapsSize);

	bool 				bLock = FALSE;
	long 				lSupportedSetting = 0;
	long				lTotalKeys = -1;
	long 				lPidTscKeys = -1;
	long				lPidKey = -1;
	long 				lGlobalKey = -1;

	//
	// Get Tuner Capabilities
	//	
	if (   sParams.bShowTunerCap
		|| sParams.bShowTunerInfo
		|| sParams.bShowIpInfo		// In order to use GetReceivedDataIp ()
		|| sParams.bShowAllInfo)
	{
		hr = pB2C2FilterTunerCtrl->GetTunerCapabilities (&tunerCaps, &lTunerCapsSize);

		if (FAILED (hr))
		{
			fprintf (stderr,"b2status: Error: B2C2 Driver Data Ctrl. Interface GetTunerCapabilities method failed, error: 0x%8.8x\n", hr);
			return 1;	// *** APPLICATION EXIT POINT
		}

//		hr = pB2C2FilterDataCtrl->GetKeyCount (&lTotalKeys, &lPidTscKeys, &lPidKey, &lGlobalKey);
//
//		ExitOnFailed (hr, "GetKeyCount");				// *** APPLICATION EXIT POINT

		//
		// Check Lock
		//
		bLock = !FAILED (pB2C2FilterTunerCtrl->CheckLock());

		switch (tunerCaps.eModulation)
		{	
		case TUNER_SATELLITE :
			lSupportedSetting =   B2C2_SDK_AVAIL_FEQUENCY
								| B2C2_SDK_AVAIL_SYMB_RATE
								| B2C2_SDK_AVAIL_POLARITY
								| B2C2_SDK_AVAIL_DISEQC
								| B2C2_SDK_AVAIL_LNB_FREQ
								| B2C2_SDK_AVAIL_LNB_SWITCH_FREQ;
			break;
		case TUNER_CABLE :
			lSupportedSetting =   B2C2_SDK_AVAIL_FEQUENCY
								| B2C2_SDK_AVAIL_SYMB_RATE
								| B2C2_SDK_AVAIL_MODULATION;
			break;
		case TUNER_TERRESTRIAL :
			lSupportedSetting =   B2C2_SDK_AVAIL_FEQUENCY
								| B2C2_SDK_AVAIL_GUARD_INTERVAL
								| B2C2_SDK_AVAIL_BANDWIDTH;
			break;
		case TUNER_ATSC :
			lSupportedSetting =   B2C2_SDK_AVAIL_FEQUENCY;
			break;
		default : break;
		};
	}

	//
	// Show the tuner capabilities
	//
	if (sParams.bShowTunerCap || sParams.bShowAllInfo)
	{
		printf ("**************** Tuner capabilities: ********************\n");

		ShowValue ("Tuner type                  ", "", TRUE, tunerCaps.eModulation, sTunerTypes);;
		ShowValue ("Transponder frequency range ", " kHz", TRUE,
							tunerCaps.dwMinTransponderFreqInKHz, tunerCaps.dwMaxTransponderFreqInKHz);
		ShowValue ("Tuner frequency range       ", " kHz", TRUE,
							tunerCaps.dwMinTunerFreqInKHz, tunerCaps.dwMaxTunerFreqInKHz);
		ShowValue ("Symbol rate range           ", " kS/s", TRUE,
							tunerCaps.dwMinSymbolRateInBaud / 1000, tunerCaps.dwMaxSymbolRateInBaud / 1000);

		ShowFlag  ("Auto Symbol Rate            ", tunerCaps.bAutoSymbolRate, "SUPPORTED");
		unsigned long dwSupp = tunerCaps.dwPerformanceMonitoring;	// Temp variable for readability below

		ShowFlag  ("BER                         ", dwSupp & BER_SUPPORTED, "SUPPORTED");
		ShowFlag  ("SNR                         ", dwSupp & SNR_SUPPORTED, "SUPPORTED");
		ShowFlag  ("Signal Strength             ", dwSupp & SIGNAL_STRENGTH_SUPPORTED, "SUPPORTED");
		ShowFlag  ("Signal Quality              ", dwSupp & SIGNAL_QUALITY_SUPPORTED, "SUPPORTED");
		ShowFlag  ("Total Block Count           ", dwSupp & BLOCK_COUNT_SUPPORTED, "SUPPORTED");
		ShowFlag  ("Corrected Block Count       ", dwSupp & CORRECTED_BLOCK_COUNT_SUPPORTED, "SUPPORTED");
		ShowFlag  ("Uncorrected Block Count     ", dwSupp & UNCORRECTED_BLOCK_COUNT_SUPPORTED, "SUPPORTED");
		ShowFlag  ("Global Key                  ", lGlobalKey, "SUPPORTED");
		ShowFlag  ("PID-only Key                ", lPidKey, "SUPPORTED");
		ShowValue ("PID-TSC Keys Max            ", "", lPidTscKeys > 0, lPidTscKeys);
	}

	//
	// Show the tuner information
	//
	if 	(sParams.bShowTunerInfo || sParams.bShowAllInfo)
	{
		printf ("************* Current Tuner information: ****************\n");

		long	lFrequency = -1;
		long 	lSymbolRate = -1;
		long 	lModulation = -1;
		long 	lPolarity = -1;
		long    lDiseqc = -1;
		long 	lLnbFreq = -1;
		long 	lLnbKhz = -1;
		long 	lFec = -1;
		float 	fSNR = -1.0;
		float 	fBER = 0.0;		// If the tuner is not ready, or no data are received,
								// than GetPreErrorCorrectionBER () will not change
								// the value at all!
		long 	lSignalStrength = -1;
		long 	lSignalQuality = -1;
		//float	fSignalLevel = -1.0;
		long 	lTotalBlocks = -1;
		long 	lCorrectedBlocks = -1;
		long 	lUncorrectedBlocks = -1;
		long 	lGuardInterv = -1;
		long 	lBandwidth = -1;

		hr = pB2C2FilterTunerCtrl->GetFrequency (&lFrequency);

		ExitOnFailed (hr, "GetFrequency");				// *** APPLICATION EXIT POINT

		pB2C2FilterTunerCtrl->GetSymbolRate (&lSymbolRate);

		ExitOnFailed (hr, "GetSymbolRate");				// *** APPLICATION EXIT POINT

		pB2C2FilterTunerCtrl->GetFec (&lFec);

		ExitOnFailed (hr, "GetFec");					// *** APPLICATION EXIT POINT

		pB2C2FilterTunerCtrl->GetModulation (&lModulation);

		ExitOnFailed (hr, "GetModulation");				// *** APPLICATION EXIT POINT

		pB2C2FilterTunerCtrl->GetPolarity (&lPolarity);

		ExitOnFailed (hr, "GetPolarity");				// *** APPLICATION EXIT POINT

		pB2C2FilterTunerCtrl->GetDiseqc (&lDiseqc);

		ExitOnFailed (hr, "GetGetDiseqc");				// *** APPLICATION EXIT POINT

		pB2C2FilterTunerCtrl->GetLnbFrequency (&lLnbFreq);

		ExitOnFailed (hr, "GetLnbFrequency");			// *** APPLICATION EXIT POINT

		pB2C2FilterTunerCtrl->GetLnbKHz (&lLnbKhz);

		ExitOnFailed (hr, "GetLnbKHz");					// *** APPLICATION EXIT POINT

		hr = pB2C2FilterTunerCtrl->GetSignalStrength (&lSignalStrength);

		ExitOnFailed (hr, "GetSignalStrength");			// *** APPLICATION EXIT POINT

		hr = pB2C2FilterTunerCtrl->GetSignalQuality (&lSignalQuality);

		ExitOnFailed (hr, "GetSignalQuality");			// *** APPLICATION EXIT POINT

		/* Will be deleted later - returns error not supported
		hr = pB2C2FilterTunerCtrl->GetSignalLevel (&fSignalLevel);

		ExitOnFailed (hr, "GetSignalLevel");			// *** APPLICATION EXIT POINT
        */

		hr = pB2C2FilterTunerCtrl->GetSNR (&fSNR);

		ExitOnFailed (hr, "GetSNR");					// *** APPLICATION EXIT POINT

		hr = pB2C2FilterTunerCtrl->GetPreErrorCorrectionBER (&fBER, TRUE);

		ExitOnFailed (hr, "GetPreErrorCorrectionBER");	// *** APPLICATION EXIT POINT

		hr = pB2C2FilterTunerCtrl->GetTotalBlocks (&lTotalBlocks);

		ExitOnFailed (hr, "GetTotalBlocks");			// *** APPLICATION EXIT POINT

		hr = pB2C2FilterTunerCtrl->GetCorrectedBlocks (&lCorrectedBlocks);

		ExitOnFailed (hr, "GetCorrectedBlocks");		// *** APPLICATION EXIT POINT

		hr = pB2C2FilterTunerCtrl->GetUncorrectedBlocks (&lUncorrectedBlocks);

		ExitOnFailed (hr, "GetUncorrectedBlocks");		// *** APPLICATION EXIT POINT

		hr = pB2C2FilterTunerCtrl->GetGuardInterval (&lGuardInterv);

		ExitOnFailed (hr, "GetGuardInterval");			// *** APPLICATION EXIT POINT

		hr = pB2C2FilterTunerCtrl->GetBandwidth (&lBandwidth);

		ExitOnFailed (hr, "GetBandwidth");			// *** APPLICATION EXIT POINT

		if (!(sParams.bShowTunerCap || sParams.bShowAllInfo) )
		{
			ShowValue ("Tuner type                    ", "", TRUE, tunerCaps.eModulation, sTunerTypes);;
		}
		ShowValue ("Status   In Lock            ", "", TRUE, bLock, "YES", "NO");
		ShowValue ("         Signal Strength    ", "%",tunerCaps.dwPerformanceMonitoring & SIGNAL_STRENGTH_SUPPORTED, lSignalStrength);
		ShowValue ("         Signal Quality     ", "%",tunerCaps.dwPerformanceMonitoring & SIGNAL_QUALITY_SUPPORTED, lSignalQuality);
		ShowValue ("         SNR                ", "", tunerCaps.dwPerformanceMonitoring & SNR_SUPPORTED, fSNR);
		ShowValue ("         BER                ", "", tunerCaps.dwPerformanceMonitoring & BER_SUPPORTED, fBER);
		ShowValue ("         Total Blocks       ", "", tunerCaps.dwPerformanceMonitoring & BLOCK_COUNT_SUPPORTED, lTotalBlocks);
		ShowValue ("         Corrected Blocks   ", "", tunerCaps.dwPerformanceMonitoring & CORRECTED_BLOCK_COUNT_SUPPORTED, lCorrectedBlocks);
		ShowValue ("         Uncorrected Blocks ", "", tunerCaps.dwPerformanceMonitoring & UNCORRECTED_BLOCK_COUNT_SUPPORTED, lUncorrectedBlocks);
		ShowValue ("Settings Frequency          ", "MHz", lSupportedSetting & B2C2_SDK_AVAIL_FEQUENCY, lFrequency);
		ShowValue ("         Symbol Rate        ", "kS/s", lSupportedSetting & B2C2_SDK_AVAIL_SYMB_RATE, lSymbolRate);
		ShowValue ("         FEC                ", "", tunerCaps.dwFECSupported, lFec, sFecTypes);
		ShowValue ("         Polarity           ", "", lSupportedSetting & B2C2_SDK_AVAIL_POLARITY, lPolarity, sPolarityTypes);
		ShowValue ("         Modulation         ", "", MODULATION_SUPPORTED (tunerCaps.dwConstellationSupported), lModulation, sModulationTypes);
		ShowValue ("         Diseqc             ", "", lSupportedSetting & B2C2_SDK_AVAIL_DISEQC, lDiseqc, sDiseqcTypes);
		ShowValue ("         LNB Frequ.         ", "MHz", lSupportedSetting & B2C2_SDK_AVAIL_LNB_FREQ , lLnbFreq);
		ShowValue ("         LNB Select.        ", "kHz", lSupportedSetting & B2C2_SDK_AVAIL_LNB_SWITCH_FREQ, lLnbKhz, sLnbSelTypes);
		ShowValue ("         Guard Interv.      ", "", lSupportedSetting & B2C2_SDK_AVAIL_GUARD_INTERVAL, lGuardInterv, sGuardIntervalTypes);
		ShowValue ("         Bandwidth          ", "Mhz", lSupportedSetting & B2C2_SDK_AVAIL_BANDWIDTH, lBandwidth, sBandwidthTypes);

		if (lTotalKeys > 0)
		{
			long lKeys = lTotalKeys;
			long *palPids = new long[lTotalKeys];
			long *palType = new long[lTotalKeys];

			hr = pB2C2FilterDataCtrl->GetKeysInUse (&lKeys, palType, palPids);

			if (SUCCEEDED(hr))
			{
				/*
				 *	Show current PIDs for Key
				 */
				printf ("         Keys               : Max %ld, Current %ld", lTotalKeys, lKeys);

				if (lKeys > 0)
				{
					printf (" :");
					for (long lCnt = 0; lCnt < lKeys; lCnt++)
					{
						if (lCnt % PID_KEY_LIST_COLUMN == 0)
						{
							printf ("\n");
						}
						switch (palType[lCnt])
						{
						// PID-TSC Keys 
						case B2C2_KEY_PID_TSC_01 : 
						case B2C2_KEY_PID_TSC_10 :
						case B2C2_KEY_PID_TSC_11 :
							printf ("\t%2ld:0x%04lX(%ld),%s", lCnt+1, palPids[lCnt], palPids[lCnt], Tsc2Bits ((BYTE)palType[lCnt]));
							break;
						// PID-only Key
						case B2C2_KEY_PID_TSC_ANY :
							printf ("\t%2ld:0x%04lX(%ld),%s", lCnt+1, palPids[lCnt], palPids[lCnt], "ANY");
							break;
						// Global Key 
						case B2C2_KEY_GLOBAL :
							printf ("\t%2ld:%s", lCnt+1, "GLOBAL");
							break;
						default :
							printf ("\t%2ld:0x%04lX(%ld),Unknown key type:%02lX", lCnt+1, palPids[lCnt], palPids[lCnt], palType[lCnt]);
							break;
						}
					}
				}
				printf ("\n");
			}

			delete [] palPids;
			delete [] palType;

			ExitOnFailed (hr, "GetPidKeysInUse");			// *** APPLICATION EXIT POINT
		}
	}

	/*
	 * Show IP information
	 */
	if 	(sParams.bShowIpInfo || sParams.bShowAllInfo)
	{
		printf ("****************** IP statistics: ***********************\n");

		long lMaxIpPids;
		long lNumCurrentPids;
		long lStreamsOpen, lStreamsRunning;

		hr = pB2C2FilterDataCtrl->GetMaxIpPIDCount (&lMaxIpPids);

		ExitOnFailed (hr, "GetMaxIpPIDCount");			// *** APPLICATION EXIT POINT

		if (   lMaxIpPids > MAX_REASONABLE_SUPPORTED_PIDS
			|| lMaxIpPids <= 0)
		{
			fprintf (stderr,"b2status: Error: B2C2 Driver Data Ctrl. Interface GetMaxIpPIDCount returned invalid value: %lu\n", lMaxIpPids);
		}

		lNumCurrentPids = lMaxIpPids;	// size of array, in long's.

		long *palPids = new long[lMaxIpPids];
		
		hr = pB2C2FilterDataCtrl->GetIpState (&lStreamsOpen, &lStreamsRunning,
											  &lNumCurrentPids, palPids);

		if (FAILED (hr) && palPids)
		{
			// delete array before return
			delete [] palPids;
			palPids = NULL;
		}

		ExitOnFailed (hr, "GetIpState");				// *** APPLICATION EXIT POINT

		/*
		 *	Show current IP PIDs, streams
		 */
		printf ("IP Streams           : %ld open, %ld running\n", lStreamsOpen, lStreamsRunning);
		printf ("IP PIDs              : Max %ld, Current %ld", lMaxIpPids, lNumCurrentPids);

		if (lNumCurrentPids > 0)
		{
			printf (" :");
			for (int iCnt = 0; iCnt < lNumCurrentPids; iCnt++)
			{
				if (iCnt % PID_LIST_COLUMN == 0)
				{
					printf ("\n");
				}
				printf ("\t%2d:0x%04lX(%ld)", iCnt+1, palPids[iCnt], palPids[iCnt]);
			}
		}
		printf ("\n");

		// Delete PID array
		if (palPids)
		{
			delete [] palPids;
			palPids = NULL;
		}

		/*
		 *  Show datagram table ID
		 */
		long lOutTableId;

		HRESULT hr = pB2C2FilterDataCtrl->GetTableId (&lOutTableId); 
  
		if (FAILED (hr))
		{
			// error handling
			printf ("Error: failed to get datagram table ID. Error %X\n", hr);
		}
		else
		{
			char strTableId[50];

			// Use lOutTableId, holding current datagram table ID
			switch (lOutTableId)
			{
			case TABLE_ID_3E : // DVB Standard
				sprintf( strTableId, "3E");
				break;
			case TABLE_ID_3F : // ATSC Standard
				sprintf( strTableId, "3F");
				break;
			case TABLE_ID_AUTO : // Non-ATSC device
				sprintf( strTableId, "AUTO");
				break;
			default :
				sprintf( strTableId, "unknown (%lu)", lOutTableId);
				break;
			}

			printf ("Datagram Table ID    : %s\n", strTableId);
		}

		/*
		 *  Show hardware MAC address
		 */
		unsigned char getHwMac[B2C2_SDK_MAC_ADDR_SIZE];

		hr = pB2C2FilterDataCtrl->GetHardwareMacAddress (getHwMac);

		if (FAILED (hr))
		{
			// error handling
			printf ("Error: failed to get hardware MAC address. Error %X\n", hr);
		}
		else
		{
			// use getHwMac
			printf ("Hardware MAC Address : ");
			printMac (getHwMac);
			printf ("\n");
		}

		/*
		 *  Show current MAC address filters
		 */
		unsigned char getMac[B2C2_SDK_MAC_ADDR_SIZE];
		hr = pB2C2FilterDataCtrl->GetUnicastMacAddress (getMac);
		
		if (FAILED (hr))
		{
			printf ("Error: failed to get unicast MAC address. Error %X\n", hr);
		}
		else
		{
			printf ("Unicast MAC Address  : ");
			printMac (getMac);
			printf ("\n");
		}

		tMacAddressList getMacAddrList;
		memset (&getMacAddrList, 0x00, sizeof (getMacAddrList));
		getMacAddrList.lCount = B2C2_SDK_MAC_ADDR_LIST_MAX;    // Note: Don't forget this line!!!
			
		hr = pB2C2FilterDataCtrl->GetMulticastMacAddressList (&getMacAddrList);
		
		if (FAILED (hr))
		{
			printf ("Error: failed to get multicast MAC address list. Error %X\n", hr);
		}
		else
		{
			int i;
			printf ("Multicast MAC Filters: Currently %ld of %d maximum set%s",
					getMacAddrList.lCount, B2C2_SDK_MAC_ADDR_LIST_MAX,
					getMacAddrList.lCount > 0 ? ":" : "");	
			for (i=0; i < getMacAddrList.lCount; i++)
			{
				if (i % 2 == 0)
				{
					printf ("\n");
				}
				printf ("\t%2d : ", i + 1);
				printMac (getMacAddrList.aabtMacAddr [i]);
			}
			printf ("\n");
		}
					
		/*
		 *  Show data statistics
		 */
		__int64 llDataRcv;
		__int64 llIpRcv;

		hr = pB2C2FilterDataCtrl->GetReceivedDataIp (&llDataRcv, &llIpRcv);

		ExitOnFailed (hr, "GetReceivedDataIp");			// *** APPLICATION EXIT POINT

		/*
		 *	Special handling of LONG LONG datatype printf specifiers here: linux and windows recognize 
		 *	different format specifiers.
		 */
#ifdef __linux__
		printf ("Total received       : %llu bytes in %llu IP packets.\n", llDataRcv, llIpRcv);
#else
		printf ("Total received       : %I64u bytes in %I64u IP packets.\n", llDataRcv, llIpRcv);
#endif
	}

	//
	// Show TS Info
	//
	if 	(sParams.bShowTsInfo || sParams.bShowAllInfo)
	{
		printf ("****************** TS statistics: ***********************\n");

		long lMaxTsPids;
		long lNumCurrentPids;
		long lStreamsOpen, lStreamsRunning;

		// Note: this is actually the total number of supported PIDs. Currently, only 4 simulteneous TS pids are supported.
		hr = pB2C2FilterDataCtrl->GetMaxPIDCount (&lMaxTsPids);

		ExitOnFailed (hr, "GetMaxPIDCount");	// *** APPLICATION EXIT POINT

		if (   lMaxTsPids > MAX_REASONABLE_SUPPORTED_PIDS
			|| lMaxTsPids <= 0)
		{
			fprintf (stderr,"b2status: Error: B2C2 Driver Data Ctrl. Interface GetMaxPIDCount returned invalid value: %ld\n", lMaxTsPids);
		}

		lNumCurrentPids = lMaxTsPids;	// size of array, in longs.

		long *palPids = new long[lMaxTsPids];

		hr = pB2C2FilterDataCtrl->GetTsState (&lStreamsOpen, &lStreamsRunning,
											  &lNumCurrentPids, palPids);

		if (FAILED (hr) && palPids)
		{
			// delete array before return
			delete [] palPids;
			palPids = NULL;
		}

		ExitOnFailed (hr, "GetTsState");				// *** APPLICATION EXIT POINT

		printf ("TS Streams : %ld open, %ld running.\n", lStreamsOpen, lStreamsRunning);
		printf ("TS PIDs    : Max. %ld, Current %ld", lMaxTsPids, lNumCurrentPids);

		if (lNumCurrentPids > 0)
		{
			printf (" :");
			for (int iCnt = 0; iCnt < lNumCurrentPids; iCnt++)
			{
				if (iCnt % PID_LIST_COLUMN == 0)
				{
					printf ("\n");
				}
				printf ("\t%2d:0x%04lX(%ld)", iCnt+1, palPids[iCnt], palPids[iCnt]);
			}
		}
		printf ("\n");

		// Delete PID array
		if (palPids)
		{
			delete [] palPids;
			palPids = NULL;
		}
	}

	//
	// Show A/V Info
	//
	if 	(sParams.bShowAvInfo || sParams.bShowAllInfo)
	{
		printf ("**************** AV/TS information: *********************\n");

		// Note: At the moment is only one Audion and one Video PID supported
		long lAudioStreamsOpen, lAudioStreamsRunning;
		long lVideoStreamsOpen, lVideoStreamsRunning;
		long lAudioPid, lVideoPid;
		
		hr = pB2C2FilterAvCtrl->GetAudioVideoState (&lAudioStreamsOpen, &lVideoStreamsOpen,
													&lAudioStreamsRunning, &lVideoStreamsRunning,
													&lAudioPid, &lVideoPid);

		ExitOnFailed (hr, "GetAudioVideoState");		// *** APPLICATION EXIT POINT

		printf ("Audio Streams : %ld open, %ld running\n", lAudioStreamsOpen, lAudioStreamsRunning);
		printf ("Audio PID     : ");
		if (lAudioPid > 0)	
		{
			printf ("0x%04lX(%ld)\n", lAudioPid, lAudioPid);
		}
		else
		{
			printf ("not set\n");
		}

		printf ("Video Streams : %ld open, %ld running\n", lVideoStreamsOpen, lVideoStreamsRunning);
		printf ("Video PID     : ");
		if (lVideoPid > 0)	
		{
			printf ("0x%04lX(%ld)\n", lVideoPid, lVideoPid);
		}
		else
		{
			printf ("not set\n");
		}
	}

	printf ("*********************************************************\n");

	return hr;
}

// **********************************************************************
// *** Check command line arguments
// **********************************************************************

int CheckCommandLineArgs (int argc, char * argv[], SParams * psParams)
{
	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;

			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], "-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], "-all") == 0)
			{
		        psParams->bShowAllInfo = TRUE;
				lArgValueIndex = 0;
			}
			else if (strcmp (argv[ii], "-tc") == 0)
			{
				// OPTIONAL: Show tuner info
		        psParams->bShowTunerCap = TRUE;
				lArgValueIndex = 0;
			}
			else if (strcmp (argv[ii], "-ti") == 0)
			{
				// OPTIONAL: Show tuner info
		        psParams->bShowTunerInfo = TRUE;
				lArgValueIndex = 0;
			}
			else if (strcmp (argv[ii], "-ip") == 0)
			{
				// OPTIONAL: IP info
		        psParams->bShowIpInfo = TRUE;
				lArgValueIndex = 0;
			}
			else if (strcmp (argv[ii], "-ts") == 0)
			{
				// OPTIONAL: TS info
		        psParams->bShowTsInfo = TRUE;
				lArgValueIndex = 0;
			}
			else if (strcmp (argv[ii], "-av") == 0)
			{
				// OPTIONAL: AV info
		        psParams->bShowAvInfo = TRUE;
				lArgValueIndex = 0;
			}
			else
			{
				// Catch-all error

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

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

	return 0;
}

