blob: 2d27110322927e1b4e1ca2ce738201aad28547e0 [file] [log] [blame]
/*
Copyright (c), 2004-2005,2007-2010 Trident Microsystems, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of Trident Microsystems nor Hauppauge Computer Works
nor the names of its contributors may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file $Id: drx_driver.c,v 1.40 2010/01/12 01:24:56 lfeng Exp $
*
* \brief Generic DRX functionality, DRX driver core.
*
*/
/*------------------------------------------------------------------------------
INCLUDE FILES
------------------------------------------------------------------------------*/
#include "drx_driver.h"
#include "bsp_host.h"
#define VERSION_FIXED 0
#if VERSION_FIXED
#define VERSION_MAJOR 0
#define VERSION_MINOR 0
#define VERSION_PATCH 0
#else
#include "drx_driver_version.h"
#endif
/*------------------------------------------------------------------------------
DEFINES
------------------------------------------------------------------------------*/
/*============================================================================*/
/*=== MICROCODE RELATED DEFINES ==============================================*/
/*============================================================================*/
/** \brief Magic word for checking correct Endianess of microcode data. */
#ifndef DRX_UCODE_MAGIC_WORD
#define DRX_UCODE_MAGIC_WORD ((((u16_t)'H')<<8)+((u16_t)'L'))
#endif
/** \brief CRC flag in ucode header, flags field. */
#ifndef DRX_UCODE_CRC_FLAG
#define DRX_UCODE_CRC_FLAG (0x0001)
#endif
/** \brief Compression flag in ucode header, flags field. */
#ifndef DRX_UCODE_COMPRESSION_FLAG
#define DRX_UCODE_COMPRESSION_FLAG (0x0002)
#endif
/** \brief Maximum size of buffer used to verify the microcode.
Must be an even number. */
#ifndef DRX_UCODE_MAX_BUF_SIZE
#define DRX_UCODE_MAX_BUF_SIZE (DRXDAP_MAX_RCHUNKSIZE)
#endif
#if DRX_UCODE_MAX_BUF_SIZE & 1
#error DRX_UCODE_MAX_BUF_SIZE must be an even number
#endif
/*============================================================================*/
/*=== CHANNEL SCAN RELATED DEFINES ===========================================*/
/*============================================================================*/
/**
* \brief Maximum progress indication.
*
* Progress indication will run from 0 upto DRX_SCAN_MAX_PROGRESS during scan.
*
*/
#ifndef DRX_SCAN_MAX_PROGRESS
#define DRX_SCAN_MAX_PROGRESS 1000
#endif
/*============================================================================*/
/*=== MACROS =================================================================*/
/*============================================================================*/
#define DRX_ISPOWERDOWNMODE( mode ) ( ( mode == DRX_POWER_MODE_9 ) || \
( mode == DRX_POWER_MODE_10 ) || \
( mode == DRX_POWER_MODE_11 ) || \
( mode == DRX_POWER_MODE_12 ) || \
( mode == DRX_POWER_MODE_13 ) || \
( mode == DRX_POWER_MODE_14 ) || \
( mode == DRX_POWER_MODE_15 ) || \
( mode == DRX_POWER_MODE_16 ) || \
( mode == DRX_POWER_DOWN ) )
/*------------------------------------------------------------------------------
GLOBAL VARIABLES
------------------------------------------------------------------------------*/
/*------------------------------------------------------------------------------
STRUCTURES
------------------------------------------------------------------------------*/
/** \brief Structure of the microcode block headers */
typedef struct {
u32_t addr;
/**< Destination address of the data in this block */
u16_t size;
/**< Size of the block data following this header counted in
16 bits words */
u16_t flags;
/**< Flags for this data block:
- bit[0]= CRC on/off
- bit[1]= compression on/off
- bit[15..2]=reserved */
u16_t CRC;/**< CRC value of the data block, only valid if CRC flag is
set. */
} DRXUCodeBlockHdr_t, *pDRXUCodeBlockHdr_t;
/*------------------------------------------------------------------------------
FUNCTIONS
------------------------------------------------------------------------------*/
/*============================================================================*/
/*============================================================================*/
/*== Channel Scan Functions ==================================================*/
/*============================================================================*/
/*============================================================================*/
#ifndef DRX_EXCLUDE_SCAN
/* Prototype of default scanning function */
static DRXStatus_t
ScanFunctionDefault(void *scanContext,
DRXScanCommand_t scanCommand,
pDRXChannel_t scanChannel, pBool_t getNextChannel);
/**
* \brief Get pointer to scanning function.
* \param demod: Pointer to demodulator instance.
* \return DRXScanFunc_t.
*/
static DRXScanFunc_t GetScanFunction(pDRXDemodInstance_t demod)
{
pDRXCommonAttr_t commonAttr = (pDRXCommonAttr_t) (NULL);
DRXScanFunc_t scanFunc = (DRXScanFunc_t) (NULL);
/* get scan function from common attributes */
commonAttr = (pDRXCommonAttr_t) demod->myCommonAttr;
scanFunc = commonAttr->scanFunction;
if (scanFunc != NULL) {
/* return device-specific scan function if it's not NULL */
return scanFunc;
}
/* otherwise return default scan function in core driver */
return &ScanFunctionDefault;
}
/**
* \brief Get Context pointer.
* \param demod: Pointer to demodulator instance.
* \param scanContext: Context Pointer.
* \return DRXScanFunc_t.
*/
void *GetScanContext(pDRXDemodInstance_t demod, void *scanContext)
{
pDRXCommonAttr_t commonAttr = (pDRXCommonAttr_t) (NULL);
/* get scan function from common attributes */
commonAttr = (pDRXCommonAttr_t) demod->myCommonAttr;
scanContext = commonAttr->scanContext;
if (scanContext == NULL) {
scanContext = (void *)demod;
}
return scanContext;
}
/**
* \brief Wait for lock while scanning.
* \param demod: Pointer to demodulator instance.
* \param lockStat: Pointer to bool indicating if end result is lock or not.
* \return DRXStatus_t.
* \retval DRX_STS_OK: Success
* \retval DRX_STS_ERROR: I2C failure or bsp function failure.
*
* Wait until timeout, desired lock or NEVER_LOCK.
* Assume:
* - lock function returns : at least DRX_NOT_LOCKED and a lock state
* higher than DRX_NOT_LOCKED.
* - BSP has a clock function to retrieve a millisecond ticker value.
* - BSP has a sleep function to enable sleep of n millisecond.
*
* In case DRX_NEVER_LOCK is returned the poll-wait will be aborted.
*
*/
static DRXStatus_t ScanWaitForLock(pDRXDemodInstance_t demod, pBool_t isLocked)
{
Bool_t doneWaiting = FALSE;
DRXLockStatus_t lockState = DRX_NOT_LOCKED;
DRXLockStatus_t desiredLockState = DRX_NOT_LOCKED;
u32_t timeoutValue = 0;
u32_t startTimeLockStage = 0;
u32_t currentTime = 0;
u32_t timerValue = 0;
*isLocked = FALSE;
timeoutValue = (u32_t) demod->myCommonAttr->scanDemodLockTimeout;
desiredLockState = demod->myCommonAttr->scanDesiredLock;
startTimeLockStage = DRXBSP_HST_Clock();
/* Start polling loop, checking for lock & timeout */
while (doneWaiting == FALSE) {
if (DRX_Ctrl(demod, DRX_CTRL_LOCK_STATUS, &lockState) !=
DRX_STS_OK) {
return DRX_STS_ERROR;
}
currentTime = DRXBSP_HST_Clock();
timerValue = currentTime - startTimeLockStage;
if (lockState >= desiredLockState) {
*isLocked = TRUE;
doneWaiting = TRUE;
} /* if ( lockState >= desiredLockState ) .. */
else if (lockState == DRX_NEVER_LOCK) {
doneWaiting = TRUE;
} /* if ( lockState == DRX_NEVER_LOCK ) .. */
else if (timerValue > timeoutValue) {
/* lockState == DRX_NOT_LOCKED and timeout */
doneWaiting = TRUE;
} else {
if (DRXBSP_HST_Sleep(10) != DRX_STS_OK) {
return DRX_STS_ERROR;
}
} /* if ( timerValue > timeoutValue ) .. */
} /* while */
return DRX_STS_OK;
}
/*============================================================================*/
/**
* \brief Determine next frequency to scan.
* \param demod: Pointer to demodulator instance.
* \param skip : Minimum frequency step to take.
* \return DRXStatus_t.
* \retval DRX_STS_OK: Succes.
* \retval DRX_STS_INVALID_ARG: Invalid frequency plan.
*
* Helper function for CtrlScanNext() function.
* Compute next frequency & index in frequency plan.
* Check if scan is ready.
*
*/
static DRXStatus_t
ScanPrepareNextScan(pDRXDemodInstance_t demod, DRXFrequency_t skip)
{
pDRXCommonAttr_t commonAttr = (pDRXCommonAttr_t) (NULL);
u16_t tableIndex = 0;
u16_t frequencyPlanSize = 0;
pDRXFrequencyPlan_t frequencyPlan = (pDRXFrequencyPlan_t) (NULL);
DRXFrequency_t nextFrequency = 0;
DRXFrequency_t tunerMinFrequency = 0;
DRXFrequency_t tunerMaxFrequency = 0;
commonAttr = (pDRXCommonAttr_t) demod->myCommonAttr;
tableIndex = commonAttr->scanFreqPlanIndex;
frequencyPlan = commonAttr->scanParam->frequencyPlan;
nextFrequency = commonAttr->scanNextFrequency;
tunerMinFrequency = commonAttr->tunerMinFreqRF;
tunerMaxFrequency = commonAttr->tunerMaxFreqRF;
do {
/* Search next frequency to scan */
/* always take at least one step */
(commonAttr->scanChannelsScanned)++;
nextFrequency += frequencyPlan[tableIndex].step;
skip -= frequencyPlan[tableIndex].step;
/* and then as many steps necessary to exceed 'skip'
without exceeding end of the band */
while ((skip > 0) &&
(nextFrequency <= frequencyPlan[tableIndex].last)) {
(commonAttr->scanChannelsScanned)++;
nextFrequency += frequencyPlan[tableIndex].step;
skip -= frequencyPlan[tableIndex].step;
}
/* reset skip, in case we move to the next band later */
skip = 0;
if (nextFrequency > frequencyPlan[tableIndex].last) {
/* reached end of this band */
tableIndex++;
frequencyPlanSize =
commonAttr->scanParam->frequencyPlanSize;
if (tableIndex >= frequencyPlanSize) {
/* reached end of frequency plan */
commonAttr->scanReady = TRUE;
} else {
nextFrequency = frequencyPlan[tableIndex].first;
}
}
if (nextFrequency > (tunerMaxFrequency)) {
/* reached end of tuner range */
commonAttr->scanReady = TRUE;
}
} while ((nextFrequency < tunerMinFrequency) &&
(commonAttr->scanReady == FALSE));
/* Store new values */
commonAttr->scanFreqPlanIndex = tableIndex;
commonAttr->scanNextFrequency = nextFrequency;
return DRX_STS_OK;
}
/*============================================================================*/
/**
* \brief Default DTV scanning function.
*
* \param demod: Pointer to demodulator instance.
* \param scanCommand: Scanning command: INIT, NEXT or STOP.
* \param scanChannel: Channel to check: frequency and bandwidth, others AUTO
* \param getNextChannel: Return TRUE if next frequency is desired at next call
*
* \return DRXStatus_t.
* \retval DRX_STS_OK: Channel found, DRX_CTRL_GET_CHANNEL can be used
* to retrieve channel parameters.
* \retval DRX_STS_BUSY: Channel not found (yet).
* \retval DRX_STS_ERROR: Something went wrong.
*
* scanChannel and getNextChannel will be NULL for INIT and STOP.
*/
static DRXStatus_t
ScanFunctionDefault(void *scanContext,
DRXScanCommand_t scanCommand,
pDRXChannel_t scanChannel, pBool_t getNextChannel)
{
pDRXDemodInstance_t demod = NULL;
DRXStatus_t status = DRX_STS_ERROR;
Bool_t isLocked = FALSE;
demod = (pDRXDemodInstance_t) scanContext;
if (scanCommand != DRX_SCAN_COMMAND_NEXT) {
/* just return OK if not doing "scan next" */
return DRX_STS_OK;
}
*getNextChannel = FALSE;
status = DRX_Ctrl(demod, DRX_CTRL_SET_CHANNEL, scanChannel);
if (status != DRX_STS_OK) {
return (status);
}
status = ScanWaitForLock(demod, &isLocked);
if (status != DRX_STS_OK) {
return status;
}
/* done with this channel, move to next one */
*getNextChannel = TRUE;
if (isLocked == FALSE) {
/* no channel found */
return DRX_STS_BUSY;
}
/* channel found */
return DRX_STS_OK;
}
/*============================================================================*/
/**
* \brief Initialize for channel scan.
* \param demod: Pointer to demodulator instance.
* \param scanParam: Pointer to scan parameters.
* \return DRXStatus_t.
* \retval DRX_STS_OK: Initialized for scan.
* \retval DRX_STS_ERROR: No overlap between frequency plan and tuner
* range.
* \retval DRX_STS_INVALID_ARG: Wrong parameters.
*
* This function should be called before starting a complete channel scan.
* It will prepare everything for a complete channel scan.
* After calling this function the DRX_CTRL_SCAN_NEXT control function can be
* used to perform the actual scanning. Scanning will start at the first
* center frequency of the frequency plan that is within the tuner range.
*
*/
static DRXStatus_t
CtrlScanInit(pDRXDemodInstance_t demod, pDRXScanParam_t scanParam)
{
DRXStatus_t status = DRX_STS_ERROR;
pDRXCommonAttr_t commonAttr = (pDRXCommonAttr_t) (NULL);
DRXFrequency_t maxTunerFreq = 0;
DRXFrequency_t minTunerFreq = 0;
u16_t nrChannelsInPlan = 0;
u16_t i = 0;
void *scanContext = NULL;
commonAttr = (pDRXCommonAttr_t) demod->myCommonAttr;
commonAttr->scanActive = TRUE;
/* invalidate a previous SCAN_INIT */
commonAttr->scanParam = (pDRXScanParam_t) (NULL);
commonAttr->scanNextFrequency = 0;
/* Check parameters */
if (((demod->myTuner == NULL) &&
(scanParam->numTries != 1)) ||
(scanParam == NULL) ||
(scanParam->numTries == 0) ||
(scanParam->frequencyPlan == NULL) ||
(scanParam->frequencyPlanSize == 0)
) {
commonAttr->scanActive = FALSE;
return DRX_STS_INVALID_ARG;
}
/* Check frequency plan contents */
maxTunerFreq = commonAttr->tunerMaxFreqRF;
minTunerFreq = commonAttr->tunerMinFreqRF;
for (i = 0; i < (scanParam->frequencyPlanSize); i++) {
DRXFrequency_t width = 0;
DRXFrequency_t step = scanParam->frequencyPlan[i].step;
DRXFrequency_t firstFreq = scanParam->frequencyPlan[i].first;
DRXFrequency_t lastFreq = scanParam->frequencyPlan[i].last;
DRXFrequency_t minFreq = 0;
DRXFrequency_t maxFreq = 0;
if (step <= 0) {
/* Step must be positive and non-zero */
commonAttr->scanActive = FALSE;
return DRX_STS_INVALID_ARG;
}
if (firstFreq > lastFreq) {
/* First center frequency is higher than last center frequency */
commonAttr->scanActive = FALSE;
return DRX_STS_INVALID_ARG;
}
width = lastFreq - firstFreq;
if ((width % step) != 0) {
/* Difference between last and first center frequency is not
an integer number of steps */
commonAttr->scanActive = FALSE;
return DRX_STS_INVALID_ARG;
}
/* Check if frequency plan entry intersects with tuner range */
if (lastFreq >= minTunerFreq) {
if (firstFreq <= maxTunerFreq) {
if (firstFreq >= minTunerFreq) {
minFreq = firstFreq;
} else {
DRXFrequency_t n = 0;
n = (minTunerFreq - firstFreq) / step;
if (((minTunerFreq -
firstFreq) % step) != 0) {
n++;
}
minFreq = firstFreq + n * step;
}
if (lastFreq <= maxTunerFreq) {
maxFreq = lastFreq;
} else {
DRXFrequency_t n = 0;
n = (lastFreq - maxTunerFreq) / step;
if (((lastFreq -
maxTunerFreq) % step) != 0) {
n++;
}
maxFreq = lastFreq - n * step;
}
}
}
/* Keep track of total number of channels within tuner range
in this frequency plan. */
if ((minFreq != 0) && (maxFreq != 0)) {
nrChannelsInPlan +=
(u16_t) (((maxFreq - minFreq) / step) + 1);
/* Determine first frequency (within tuner range) to scan */
if (commonAttr->scanNextFrequency == 0) {
commonAttr->scanNextFrequency = minFreq;
commonAttr->scanFreqPlanIndex = i;
}
}
} /* for ( ... ) */
if (nrChannelsInPlan == 0) {
/* Tuner range and frequency plan ranges do not overlap */
commonAttr->scanActive = FALSE;
return DRX_STS_ERROR;
}
/* Store parameters */
commonAttr->scanReady = FALSE;
commonAttr->scanMaxChannels = nrChannelsInPlan;
commonAttr->scanChannelsScanned = 0;
commonAttr->scanParam = scanParam; /* SCAN_NEXT is now allowed */
scanContext = GetScanContext(demod, scanContext);
status = (*(GetScanFunction(demod)))
(scanContext, DRX_SCAN_COMMAND_INIT, NULL, NULL);
commonAttr->scanActive = FALSE;
return DRX_STS_OK;
}
/*============================================================================*/
/**
* \brief Stop scanning.
* \param demod: Pointer to demodulator instance.
* \return DRXStatus_t.
* \retval DRX_STS_OK: Scan stopped.
* \retval DRX_STS_ERROR: Something went wrong.
* \retval DRX_STS_INVALID_ARG: Wrong parameters.
*/
static DRXStatus_t CtrlScanStop(pDRXDemodInstance_t demod)
{
DRXStatus_t status = DRX_STS_ERROR;
pDRXCommonAttr_t commonAttr = (pDRXCommonAttr_t) (NULL);
void *scanContext = NULL;
commonAttr = (pDRXCommonAttr_t) demod->myCommonAttr;
commonAttr->scanActive = TRUE;
if ((commonAttr->scanParam == NULL) ||
(commonAttr->scanMaxChannels == 0)) {
/* Scan was not running, just return OK */
commonAttr->scanActive = FALSE;
return DRX_STS_OK;
}
/* Call default or device-specific scanning stop function */
scanContext = GetScanContext(demod, scanContext);
status = (*(GetScanFunction(demod)))
(scanContext, DRX_SCAN_COMMAND_STOP, NULL, NULL);
/* All done, invalidate scan-init */
commonAttr->scanParam = NULL;
commonAttr->scanMaxChannels = 0;
commonAttr->scanActive = FALSE;
return status;
}
/*============================================================================*/
/**
* \brief Scan for next channel.
* \param demod: Pointer to demodulator instance.
* \param scanProgress: Pointer to scan progress.
* \return DRXStatus_t.
* \retval DRX_STS_OK: Channel found, DRX_CTRL_GET_CHANNEL can be used
* to retrieve channel parameters.
* \retval DRX_STS_BUSY: Tried part of the channels, as specified in
* numTries field of scan parameters. At least one
* more call to DRX_CTRL_SCAN_NEXT is needed to
* complete scanning.
* \retval DRX_STS_READY: Reached end of scan range.
* \retval DRX_STS_ERROR: Something went wrong.
* \retval DRX_STS_INVALID_ARG: Wrong parameters. The scanProgress may be NULL.
*
* Progress indication will run from 0 upto DRX_SCAN_MAX_PROGRESS during scan.
*
*/
static DRXStatus_t CtrlScanNext(pDRXDemodInstance_t demod, pu16_t scanProgress)
{
pDRXCommonAttr_t commonAttr = (pDRXCommonAttr_t) (NULL);
pBool_t scanReady = (pBool_t) (NULL);
u16_t maxProgress = DRX_SCAN_MAX_PROGRESS;
u32_t numTries = 0;
u32_t i = 0;
commonAttr = (pDRXCommonAttr_t) demod->myCommonAttr;
/* Check scan parameters */
if (scanProgress == NULL) {
commonAttr->scanActive = FALSE;
return DRX_STS_INVALID_ARG;
}
*scanProgress = 0;
commonAttr->scanActive = TRUE;
if ((commonAttr->scanParam == NULL) ||
(commonAttr->scanMaxChannels == 0)) {
/* CtrlScanInit() was not called succesfully before CtrlScanNext() */
commonAttr->scanActive = FALSE;
return DRX_STS_ERROR;
}
*scanProgress = (u16_t) (((commonAttr->scanChannelsScanned) *
((u32_t) (maxProgress))) /
(commonAttr->scanMaxChannels));
/* Scan */
numTries = commonAttr->scanParam->numTries;
scanReady = &(commonAttr->scanReady);
for (i = 0; ((i < numTries) && ((*scanReady) == FALSE)); i++) {
DRXChannel_t scanChannel = { 0 };
DRXStatus_t status = DRX_STS_ERROR;
pDRXFrequencyPlan_t freqPlan = (pDRXFrequencyPlan_t) (NULL);
Bool_t nextChannel = FALSE;
void *scanContext = NULL;
/* Next channel to scan */
freqPlan =
&(commonAttr->scanParam->
frequencyPlan[commonAttr->scanFreqPlanIndex]);
scanChannel.frequency = commonAttr->scanNextFrequency;
scanChannel.bandwidth = freqPlan->bandwidth;
scanChannel.mirror = DRX_MIRROR_AUTO;
scanChannel.constellation = DRX_CONSTELLATION_AUTO;
scanChannel.hierarchy = DRX_HIERARCHY_AUTO;
scanChannel.priority = DRX_PRIORITY_HIGH;
scanChannel.coderate = DRX_CODERATE_AUTO;
scanChannel.guard = DRX_GUARD_AUTO;
scanChannel.fftmode = DRX_FFTMODE_AUTO;
scanChannel.classification = DRX_CLASSIFICATION_AUTO;
scanChannel.symbolrate = 0;
scanChannel.interleavemode = DRX_INTERLEAVEMODE_AUTO;
scanChannel.ldpc = DRX_LDPC_AUTO;
scanChannel.carrier = DRX_CARRIER_AUTO;
scanChannel.framemode = DRX_FRAMEMODE_AUTO;
scanChannel.pilot = DRX_PILOT_AUTO;
/* Call default or device-specific scanning function */
scanContext = GetScanContext(demod, scanContext);
status = (*(GetScanFunction(demod)))
(scanContext, DRX_SCAN_COMMAND_NEXT, &scanChannel,
&nextChannel);
/* Proceed to next channel if requested */
if (nextChannel == TRUE) {
DRXStatus_t nextStatus = DRX_STS_ERROR;
DRXFrequency_t skip = 0;
if (status == DRX_STS_OK) {
/* a channel was found, so skip some frequency steps */
skip = commonAttr->scanParam->skip;
}
nextStatus = ScanPrepareNextScan(demod, skip);
/* keep track of progress */
*scanProgress =
(u16_t) (((commonAttr->scanChannelsScanned) *
((u32_t) (maxProgress))) /
(commonAttr->scanMaxChannels));
if (nextStatus != DRX_STS_OK) {
commonAttr->scanActive = FALSE;
return (nextStatus);
}
}
if (status != DRX_STS_BUSY) {
/* channel found or error */
commonAttr->scanActive = FALSE;
return status;
}
} /* for ( i = 0; i < ( ... numTries); i++) */
if ((*scanReady) == TRUE) {
/* End of scan reached: call stop-scan, ignore any error */
CtrlScanStop(demod);
commonAttr->scanActive = FALSE;
return (DRX_STS_READY);
}
commonAttr->scanActive = FALSE;
return DRX_STS_BUSY;
}
#endif /* #ifndef DRX_EXCLUDE_SCAN */
/*============================================================================*/
/**
* \brief Program tuner.
* \param demod: Pointer to demodulator instance.
* \param tunerChannel: Pointer to tuning parameters.
* \return DRXStatus_t.
* \retval DRX_STS_OK: Tuner programmed successfully.
* \retval DRX_STS_ERROR: Something went wrong.
* \retval DRX_STS_INVALID_ARG: Wrong parameters.
*
* tunerChannel passes parameters to program the tuner,
* but also returns the actual RF and IF frequency from the tuner.
*
*/
static DRXStatus_t
CtrlProgramTuner(pDRXDemodInstance_t demod, pDRXChannel_t channel)
{
pDRXCommonAttr_t commonAttr = (pDRXCommonAttr_t) (NULL);
DRXStandard_t standard = DRX_STANDARD_UNKNOWN;
TUNERMode_t tunerMode = 0;
DRXStatus_t status = DRX_STS_ERROR;
DRXFrequency_t ifFrequency = 0;
Bool_t tunerSlowMode = FALSE;
/* can't tune without a tuner */
if (demod->myTuner == NULL) {
return DRX_STS_INVALID_ARG;
}
commonAttr = (pDRXCommonAttr_t) demod->myCommonAttr;
/* select analog or digital tuner mode based on current standard */
if (DRX_Ctrl(demod, DRX_CTRL_GET_STANDARD, &standard) != DRX_STS_OK) {
return DRX_STS_ERROR;
}
if (DRX_ISATVSTD(standard)) {
tunerMode |= TUNER_MODE_ANALOG;
} else { /* note: also for unknown standard */
tunerMode |= TUNER_MODE_DIGITAL;
}
/* select tuner bandwidth */
switch (channel->bandwidth) {
case DRX_BANDWIDTH_6MHZ:
tunerMode |= TUNER_MODE_6MHZ;
break;
case DRX_BANDWIDTH_7MHZ:
tunerMode |= TUNER_MODE_7MHZ;
break;
case DRX_BANDWIDTH_8MHZ:
tunerMode |= TUNER_MODE_8MHZ;
break;
default: /* note: also for unknown bandwidth */
return DRX_STS_INVALID_ARG;
}
DRX_GET_TUNERSLOWMODE(demod, tunerSlowMode);
/* select fast (switch) or slow (lock) tuner mode */
if (tunerSlowMode) {
tunerMode |= TUNER_MODE_LOCK;
} else {
tunerMode |= TUNER_MODE_SWITCH;
}
if (commonAttr->tunerPortNr == 1) {
Bool_t bridgeClosed = TRUE;
DRXStatus_t statusBridge = DRX_STS_ERROR;
statusBridge =
DRX_Ctrl(demod, DRX_CTRL_I2C_BRIDGE, &bridgeClosed);
if (statusBridge != DRX_STS_OK) {
return statusBridge;
}
}
status = DRXBSP_TUNER_SetFrequency(demod->myTuner,
tunerMode, channel->frequency);
/* attempt restoring bridge before checking status of SetFrequency */
if (commonAttr->tunerPortNr == 1) {
Bool_t bridgeClosed = FALSE;
DRXStatus_t statusBridge = DRX_STS_ERROR;
statusBridge =
DRX_Ctrl(demod, DRX_CTRL_I2C_BRIDGE, &bridgeClosed);
if (statusBridge != DRX_STS_OK) {
return statusBridge;
}
}
/* now check status of DRXBSP_TUNER_SetFrequency */
if (status != DRX_STS_OK) {
return status;
}
/* get actual RF and IF frequencies from tuner */
status = DRXBSP_TUNER_GetFrequency(demod->myTuner,
tunerMode,
&(channel->frequency),
&(ifFrequency));
if (status != DRX_STS_OK) {
return status;
}
/* update common attributes with information available from this function;
TODO: check if this is required and safe */
DRX_SET_INTERMEDIATEFREQ(demod, ifFrequency);
return DRX_STS_OK;
}
/*============================================================================*/
/**
* \brief function to do a register dump.
* \param demod: Pointer to demodulator instance.
* \param registers: Registers to dump.
* \return DRXStatus_t.
* \retval DRX_STS_OK: Dump executed successfully.
* \retval DRX_STS_ERROR: Something went wrong.
* \retval DRX_STS_INVALID_ARG: Wrong parameters.
*
*/
DRXStatus_t CtrlDumpRegisters(pDRXDemodInstance_t demod,
pDRXRegDump_t registers)
{
u16_t i = 0;
if (registers == NULL) {
/* registers not supplied */
return DRX_STS_INVALID_ARG;
}
/* start dumping registers */
while (registers[i].address != 0) {
DRXStatus_t status = DRX_STS_ERROR;
u16_t value = 0;
u32_t data = 0;
status =
demod->myAccessFunct->readReg16Func(demod->myI2CDevAddr,
registers[i].address,
&value, 0);
data = (u32_t) value;
if (status != DRX_STS_OK) {
/* no breakouts;
depending on device ID, some HW blocks might not be available */
data |= ((u32_t) status) << 16;
}
registers[i].data = data;
i++;
}
/* all done, all OK (any errors are saved inside data) */
return DRX_STS_OK;
}
/*============================================================================*/
/*============================================================================*/
/*===Microcode related functions==============================================*/
/*============================================================================*/
/*============================================================================*/
/**
* \brief Read a 16 bits word, expects big endian data.
* \param addr: Pointer to memory from which to read the 16 bits word.
* \return u16_t The data read.
*
* This function takes care of the possible difference in endianness between the
* host and the data contained in the microcode image file.
*
*/
static u16_t UCodeRead16(pu8_t addr)
{
/* Works fo any host processor */
u16_t word = 0;
word = ((u16_t) addr[0]);
word <<= 8;
word |= ((u16_t) addr[1]);
return word;
}
/*============================================================================*/
/**
* \brief Read a 32 bits word, expects big endian data.
* \param addr: Pointer to memory from which to read the 32 bits word.
* \return u32_t The data read.
*
* This function takes care of the possible difference in endianness between the
* host and the data contained in the microcode image file.
*
*/
static u32_t UCodeRead32(pu8_t addr)
{
/* Works fo any host processor */
u32_t word = 0;
word = ((u16_t) addr[0]);
word <<= 8;
word |= ((u16_t) addr[1]);
word <<= 8;
word |= ((u16_t) addr[2]);
word <<= 8;
word |= ((u16_t) addr[3]);
return word;
}
/*============================================================================*/
/**
* \brief Compute CRC of block of microcode data.
* \param blockData: Pointer to microcode data.
* \param nrWords: Size of microcode block (number of 16 bits words).
* \return u16_t The computed CRC residu.
*/
static u16_t UCodeComputeCRC(pu8_t blockData, u16_t nrWords)
{
u16_t i = 0;
u16_t j = 0;
u32_t CRCWord = 0;
u32_t carry = 0;
while (i < nrWords) {
CRCWord |= (u32_t) UCodeRead16(blockData);
for (j = 0; j < 16; j++) {
CRCWord <<= 1;
if (carry != 0) {
CRCWord ^= 0x80050000UL;
}
carry = CRCWord & 0x80000000UL;
}
i++;
blockData += (sizeof(u16_t));
}
return ((u16_t) (CRCWord >> 16));
}
/*============================================================================*/
/**
* \brief Handle microcode upload or verify.
* \param devAddr: Address of device.
* \param mcInfo: Pointer to information about microcode data.
* \param action: Either UCODE_UPLOAD or UCODE_VERIFY
* \return DRXStatus_t.
* \retval DRX_STS_OK:
* - In case of UCODE_UPLOAD: code is successfully uploaded.
* - In case of UCODE_VERIFY: image on device is equal to
* image provided to this control function.
* \retval DRX_STS_ERROR:
* - In case of UCODE_UPLOAD: I2C error.
* - In case of UCODE_VERIFY: I2C error or image on device
* is not equal to image provided to this control function.
* \retval DRX_STS_INVALID_ARG:
* - Invalid arguments.
* - Provided image is corrupt
*/
static DRXStatus_t
CtrlUCode(pDRXDemodInstance_t demod,
pDRXUCodeInfo_t mcInfo, DRXUCodeAction_t action)
{
DRXStatus_t rc;
u16_t i = 0;
u16_t mcNrOfBlks = 0;
u16_t mcMagicWord = 0;
pu8_t mcData = (pu8_t) (NULL);
pI2CDeviceAddr_t devAddr = (pI2CDeviceAddr_t) (NULL);
devAddr = demod->myI2CDevAddr;
/* Check arguments */
if ((mcInfo == NULL) || (mcInfo->mcData == NULL)) {
return DRX_STS_INVALID_ARG;
}
mcData = mcInfo->mcData;
/* Check data */
mcMagicWord = UCodeRead16(mcData);
mcData += sizeof(u16_t);
mcNrOfBlks = UCodeRead16(mcData);
mcData += sizeof(u16_t);
if ((mcMagicWord != DRX_UCODE_MAGIC_WORD) || (mcNrOfBlks == 0)) {
/* wrong endianess or wrong data ? */
return DRX_STS_INVALID_ARG;
}
/* Scan microcode blocks first for version info if uploading */
if (action == UCODE_UPLOAD) {
/* Clear version block */
DRX_SET_MCVERTYPE(demod, 0);
DRX_SET_MCDEV(demod, 0);
DRX_SET_MCVERSION(demod, 0);
DRX_SET_MCPATCH(demod, 0);
for (i = 0; i < mcNrOfBlks; i++) {
DRXUCodeBlockHdr_t blockHdr;
/* Process block header */
blockHdr.addr = UCodeRead32(mcData);
mcData += sizeof(u32_t);
blockHdr.size = UCodeRead16(mcData);
mcData += sizeof(u16_t);
blockHdr.flags = UCodeRead16(mcData);
mcData += sizeof(u16_t);
blockHdr.CRC = UCodeRead16(mcData);
mcData += sizeof(u16_t);
if (blockHdr.flags & 0x8) {
/* Aux block. Check type */
pu8_t auxblk = mcInfo->mcData + blockHdr.addr;
u16_t auxtype = UCodeRead16(auxblk);
if (DRX_ISMCVERTYPE(auxtype)) {
DRX_SET_MCVERTYPE(demod,
UCodeRead16(auxblk));
auxblk += sizeof(u16_t);
DRX_SET_MCDEV(demod,
UCodeRead32(auxblk));
auxblk += sizeof(u32_t);
DRX_SET_MCVERSION(demod,
UCodeRead32(auxblk));
auxblk += sizeof(u32_t);
DRX_SET_MCPATCH(demod,
UCodeRead32(auxblk));
}
}
/* Next block */
mcData += blockHdr.size * sizeof(u16_t);
}
/* After scanning, validate the microcode.
It is also valid if no validation control exists.
*/
rc = DRX_Ctrl(demod, DRX_CTRL_VALIDATE_UCODE, NULL);
if (rc != DRX_STS_OK && rc != DRX_STS_FUNC_NOT_AVAILABLE) {
return rc;
}
/* Restore data pointer */
mcData = mcInfo->mcData + 2 * sizeof(u16_t);
}
/* Process microcode blocks */
for (i = 0; i < mcNrOfBlks; i++) {
DRXUCodeBlockHdr_t blockHdr;
u16_t mcBlockNrBytes = 0;
/* Process block header */
blockHdr.addr = UCodeRead32(mcData);
mcData += sizeof(u32_t);
blockHdr.size = UCodeRead16(mcData);
mcData += sizeof(u16_t);
blockHdr.flags = UCodeRead16(mcData);
mcData += sizeof(u16_t);
blockHdr.CRC = UCodeRead16(mcData);
mcData += sizeof(u16_t);
/* Check block header on:
- data larger than 64Kb
- if CRC enabled check CRC
*/
if ((blockHdr.size > 0x7FFF) ||
(((blockHdr.flags & DRX_UCODE_CRC_FLAG) != 0) &&
(blockHdr.CRC != UCodeComputeCRC(mcData, blockHdr.size)))
) {
/* Wrong data ! */
return DRX_STS_INVALID_ARG;
}
mcBlockNrBytes = blockHdr.size * ((u16_t) sizeof(u16_t));
if (blockHdr.size != 0) {
/* Perform the desired action */
switch (action) {
/*================================================================*/
case UCODE_UPLOAD:
{
/* Upload microcode */
if (demod->myAccessFunct->
writeBlockFunc(devAddr,
(DRXaddr_t) blockHdr.
addr, mcBlockNrBytes,
mcData,
0x0000) !=
DRX_STS_OK) {
return (DRX_STS_ERROR);
} /* if */
};
break;
/*================================================================*/
case UCODE_VERIFY:
{
int result = 0;
u8_t mcDataBuffer
[DRX_UCODE_MAX_BUF_SIZE];
u32_t bytesToCompare = 0;
u32_t bytesLeftToCompare = 0;
DRXaddr_t currAddr = (DRXaddr_t) 0;
pu8_t currPtr = NULL;
bytesLeftToCompare = mcBlockNrBytes;
currAddr = blockHdr.addr;
currPtr = mcData;
while (bytesLeftToCompare != 0) {
if (bytesLeftToCompare >
((u32_t)
DRX_UCODE_MAX_BUF_SIZE)) {
bytesToCompare =
((u32_t)
DRX_UCODE_MAX_BUF_SIZE);
} else {
bytesToCompare =
bytesLeftToCompare;
}
if (demod->myAccessFunct->
readBlockFunc(devAddr,
currAddr,
(u16_t)
bytesToCompare,
(pu8_t)
mcDataBuffer,
0x0000) !=
DRX_STS_OK) {
return (DRX_STS_ERROR);
}
result =
DRXBSP_HST_Memcmp(currPtr,
mcDataBuffer,
bytesToCompare);
if (result != 0) {
return DRX_STS_ERROR;
}
currAddr +=
((DRXaddr_t)
(bytesToCompare / 2));
currPtr =
&(currPtr[bytesToCompare]);
bytesLeftToCompare -=
((u32_t) bytesToCompare);
} /* while( bytesToCompare > DRX_UCODE_MAX_BUF_SIZE ) */
};
break;
/*================================================================*/
default:
return DRX_STS_INVALID_ARG;
break;
} /* switch ( action ) */
}
/* if (blockHdr.size != 0 ) */
/* Next block */
mcData += mcBlockNrBytes;
} /* for( i = 0 ; i<mcNrOfBlks ; i++ ) */
return DRX_STS_OK;
}
/*============================================================================*/
/**
* \brief Build list of version information.
* \param demod: A pointer to a demodulator instance.
* \param versionList: Pointer to linked list of versions.
* \return DRXStatus_t.
* \retval DRX_STS_OK: Version information stored in versionList
* \retval DRX_STS_INVALID_ARG: Invalid arguments.
*/
static DRXStatus_t
CtrlVersion(pDRXDemodInstance_t demod, pDRXVersionList_t * versionList)
{
static char drxDriverCoreModuleName[] = "Core driver";
static char drxDriverCoreVersionText[] =
DRX_VERSIONSTRING(VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH);
static DRXVersion_t drxDriverCoreVersion;
static DRXVersionList_t drxDriverCoreVersionList;
pDRXVersionList_t demodVersionList = (pDRXVersionList_t) (NULL);
DRXStatus_t returnStatus = DRX_STS_ERROR;
/* Check arguments */
if (versionList == NULL) {
return DRX_STS_INVALID_ARG;
}
/* Get version info list from demod */
returnStatus = (*(demod->myDemodFunct->ctrlFunc)) (demod,
DRX_CTRL_VERSION,
(void *)
&demodVersionList);
/* Always fill in the information of the driver SW . */
drxDriverCoreVersion.moduleType = DRX_MODULE_DRIVERCORE;
drxDriverCoreVersion.moduleName = drxDriverCoreModuleName;
drxDriverCoreVersion.vMajor = VERSION_MAJOR;
drxDriverCoreVersion.vMinor = VERSION_MINOR;
drxDriverCoreVersion.vPatch = VERSION_PATCH;
drxDriverCoreVersion.vString = drxDriverCoreVersionText;
drxDriverCoreVersionList.version = &drxDriverCoreVersion;
drxDriverCoreVersionList.next = (pDRXVersionList_t) (NULL);
if ((returnStatus == DRX_STS_OK) && (demodVersionList != NULL)) {
/* Append versioninfo from driver to versioninfo from demod */
/* Return version info in "bottom-up" order. This way, multiple
devices can be handled without using malloc. */
pDRXVersionList_t currentListElement = demodVersionList;
while (currentListElement->next != NULL) {
currentListElement = currentListElement->next;
}
currentListElement->next = &drxDriverCoreVersionList;
*versionList = demodVersionList;
} else {
/* Just return versioninfo from driver */
*versionList = &drxDriverCoreVersionList;
}
return DRX_STS_OK;
}
/*============================================================================*/
/*============================================================================*/
/*== Exported functions ======================================================*/
/*============================================================================*/
/*============================================================================*/
/**
* \brief This function is obsolete.
* \param demods: Don't care, parameter is ignored.
* \return DRXStatus_t Return status.
* \retval DRX_STS_OK: Initialization completed.
*
* This function is obsolete, prototype available for backward compatability.
*
*/
DRXStatus_t DRX_Init(pDRXDemodInstance_t demods[])
{
return DRX_STS_OK;
}
/*============================================================================*/
/**
* \brief This function is obsolete.
* \return DRXStatus_t Return status.
* \retval DRX_STS_OK: Terminated driver successful.
*
* This function is obsolete, prototype available for backward compatability.
*
*/
DRXStatus_t DRX_Term(void)
{
return DRX_STS_OK;
}
/*============================================================================*/
/**
* \brief Open a demodulator instance.
* \param demod: A pointer to a demodulator instance.
* \return DRXStatus_t Return status.
* \retval DRX_STS_OK: Opened demod instance with succes.
* \retval DRX_STS_ERROR: Driver not initialized or unable to initialize
* demod.
* \retval DRX_STS_INVALID_ARG: Demod instance has invalid content.
*
*/
DRXStatus_t DRX_Open(pDRXDemodInstance_t demod)
{
DRXStatus_t status = DRX_STS_OK;
if ((demod == NULL) ||
(demod->myDemodFunct == NULL) ||
(demod->myCommonAttr == NULL) ||
(demod->myExtAttr == NULL) ||
(demod->myI2CDevAddr == NULL) ||
(demod->myCommonAttr->isOpened == TRUE)) {
return (DRX_STS_INVALID_ARG);
}
status = (*(demod->myDemodFunct->openFunc)) (demod);
if (status == DRX_STS_OK) {
demod->myCommonAttr->isOpened = TRUE;
}
return status;
}
/*============================================================================*/
/**
* \brief Close device.
* \param demod: A pointer to a demodulator instance.
* \return DRXStatus_t Return status.
* \retval DRX_STS_OK: Closed demod instance with succes.
* \retval DRX_STS_ERROR: Driver not initialized or error during close
* demod.
* \retval DRX_STS_INVALID_ARG: Demod instance has invalid content.
*
* Free resources occupied by device instance.
* Put device into sleep mode.
*/
DRXStatus_t DRX_Close(pDRXDemodInstance_t demod)
{
DRXStatus_t status = DRX_STS_OK;
if ((demod == NULL) ||
(demod->myDemodFunct == NULL) ||
(demod->myCommonAttr == NULL) ||
(demod->myExtAttr == NULL) ||
(demod->myI2CDevAddr == NULL) ||
(demod->myCommonAttr->isOpened == FALSE)) {
return DRX_STS_INVALID_ARG;
}
status = (*(demod->myDemodFunct->closeFunc)) (demod);
DRX_SET_ISOPENED(demod, FALSE);
return status;
}
/*============================================================================*/
/**
* \brief Control the device.
* \param demod: A pointer to a demodulator instance.
* \param ctrl: Reference to desired control function.
* \param ctrlData: Pointer to data structure for control function.
* \return DRXStatus_t Return status.
* \retval DRX_STS_OK: Control function completed successfully.
* \retval DRX_STS_ERROR: Driver not initialized or error during
* control demod.
* \retval DRX_STS_INVALID_ARG: Demod instance or ctrlData has invalid
* content.
* \retval DRX_STS_FUNC_NOT_AVAILABLE: Specified control function is not
* available.
*
* Data needed or returned by the control function is stored in ctrlData.
*
*/
DRXStatus_t
DRX_Ctrl(pDRXDemodInstance_t demod, DRXCtrlIndex_t ctrl, void *ctrlData)
{
DRXStatus_t status = DRX_STS_ERROR;
if ((demod == NULL) ||
(demod->myDemodFunct == NULL) ||
(demod->myCommonAttr == NULL) ||
(demod->myExtAttr == NULL) || (demod->myI2CDevAddr == NULL)
) {
return (DRX_STS_INVALID_ARG);
}
if (((demod->myCommonAttr->isOpened == FALSE) &&
(ctrl != DRX_CTRL_PROBE_DEVICE) && (ctrl != DRX_CTRL_VERSION))
) {
return (DRX_STS_INVALID_ARG);
}
if ((DRX_ISPOWERDOWNMODE(demod->myCommonAttr->currentPowerMode) &&
(ctrl != DRX_CTRL_POWER_MODE) &&
(ctrl != DRX_CTRL_PROBE_DEVICE) &&
(ctrl != DRX_CTRL_NOP) && (ctrl != DRX_CTRL_VERSION)
)
) {
return DRX_STS_FUNC_NOT_AVAILABLE;
}
/* Fixed control functions */
switch (ctrl) {
/*======================================================================*/
case DRX_CTRL_NOP:
/* No operation */
return DRX_STS_OK;
break;
/*======================================================================*/
case DRX_CTRL_VERSION:
return CtrlVersion(demod, (pDRXVersionList_t *) ctrlData);
break;
/*======================================================================*/
default:
/* Do nothing */
break;
}
/* Virtual functions */
/* First try calling function from derived class */
status = (*(demod->myDemodFunct->ctrlFunc)) (demod, ctrl, ctrlData);
if (status == DRX_STS_FUNC_NOT_AVAILABLE) {
/* Now try calling a the base class function */
switch (ctrl) {
/*===================================================================*/
case DRX_CTRL_LOAD_UCODE:
return CtrlUCode(demod,
(pDRXUCodeInfo_t) ctrlData,
UCODE_UPLOAD);
break;
/*===================================================================*/
case DRX_CTRL_VERIFY_UCODE:
{
return CtrlUCode(demod,
(pDRXUCodeInfo_t) ctrlData,
UCODE_VERIFY);
}
break;
#ifndef DRX_EXCLUDE_SCAN
/*===================================================================*/
case DRX_CTRL_SCAN_INIT:
{
return CtrlScanInit(demod,
(pDRXScanParam_t) ctrlData);
}
break;
/*===================================================================*/
case DRX_CTRL_SCAN_NEXT:
{
return CtrlScanNext(demod, (pu16_t) ctrlData);
}
break;
/*===================================================================*/
case DRX_CTRL_SCAN_STOP:
{
return CtrlScanStop(demod);
}
break;
#endif /* #ifndef DRX_EXCLUDE_SCAN */
/*===================================================================*/
case DRX_CTRL_PROGRAM_TUNER:
{
return CtrlProgramTuner(demod,
(pDRXChannel_t)
ctrlData);
}
break;
/*===================================================================*/
case DRX_CTRL_DUMP_REGISTERS:
{
return CtrlDumpRegisters(demod,
(pDRXRegDump_t)
ctrlData);
}
break;
/*===================================================================*/
default:
return DRX_STS_FUNC_NOT_AVAILABLE;
}
} else {
return (status);
}
return DRX_STS_OK;
}
/*============================================================================*/
/* END OF FILE */