| /* |
| 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" |
| |
| #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)'H')<<8)+((u16)'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 addr; |
| /**< Destination address of the data in this block */ |
| u16 size; |
| /**< Size of the block data following this header counted in |
| 16 bits words */ |
| u16 flags; |
| /**< Flags for this data block: |
| - bit[0]= CRC on/off |
| - bit[1]= compression on/off |
| - bit[15..2]=reserved */ |
| u16 CRC;/**< CRC value of the data block, only valid if CRC flag is |
| set. */ |
| } drxu_code_block_hdr_t, *pdrxu_code_block_hdr_t; |
| |
| /*------------------------------------------------------------------------------ |
| FUNCTIONS |
| ------------------------------------------------------------------------------*/ |
| |
| /*============================================================================*/ |
| /*============================================================================*/ |
| /*== Channel Scan Functions ==================================================*/ |
| /*============================================================================*/ |
| /*============================================================================*/ |
| |
| #ifndef DRX_EXCLUDE_SCAN |
| |
| /* Prototype of default scanning function */ |
| static int |
| scan_function_default(void *scan_context, |
| enum drx_scan_command scan_command, |
| struct drx_channel *scan_channel, bool *get_next_channel); |
| |
| /** |
| * \brief Get pointer to scanning function. |
| * \param demod: Pointer to demodulator instance. |
| * \return drx_scan_func_t. |
| */ |
| static drx_scan_func_t get_scan_function(struct drx_demod_instance *demod) |
| { |
| struct drx_common_attr *common_attr = (struct drx_common_attr *) (NULL); |
| drx_scan_func_t scan_func = (drx_scan_func_t) (NULL); |
| |
| /* get scan function from common attributes */ |
| common_attr = (struct drx_common_attr *) demod->my_common_attr; |
| scan_func = common_attr->scan_function; |
| |
| if (scan_func != NULL) { |
| /* return device-specific scan function if it's not NULL */ |
| return scan_func; |
| } |
| /* otherwise return default scan function in core driver */ |
| return &scan_function_default; |
| } |
| |
| /** |
| * \brief Get Context pointer. |
| * \param demod: Pointer to demodulator instance. |
| * \param scan_context: Context Pointer. |
| * \return drx_scan_func_t. |
| */ |
| static void *get_scan_context(struct drx_demod_instance *demod, void *scan_context) |
| { |
| struct drx_common_attr *common_attr = (struct drx_common_attr *) (NULL); |
| |
| /* get scan function from common attributes */ |
| common_attr = (struct drx_common_attr *) demod->my_common_attr; |
| scan_context = common_attr->scan_context; |
| |
| if (scan_context == NULL) |
| scan_context = (void *)demod; |
| |
| return scan_context; |
| } |
| |
| /** |
| * \brief Wait for lock while scanning. |
| * \param demod: Pointer to demodulator instance. |
| * \param lock_stat: Pointer to bool indicating if end result is lock or not. |
| * \return int. |
| * \retval 0: Success |
| * \retval -EIO: 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 int scan_wait_for_lock(struct drx_demod_instance *demod, bool *is_locked) |
| { |
| bool done_waiting = false; |
| enum drx_lock_status lock_state = DRX_NOT_LOCKED; |
| enum drx_lock_status desired_lock_state = DRX_NOT_LOCKED; |
| u32 timeout_value = 0; |
| u32 start_time_lock_stage = 0; |
| u32 current_time = 0; |
| u32 timer_value = 0; |
| |
| *is_locked = false; |
| timeout_value = (u32) demod->my_common_attr->scan_demod_lock_timeout; |
| desired_lock_state = demod->my_common_attr->scan_desired_lock; |
| start_time_lock_stage = drxbsp_hst_clock(); |
| |
| /* Start polling loop, checking for lock & timeout */ |
| while (!done_waiting) { |
| |
| if (drx_ctrl(demod, DRX_CTRL_LOCK_STATUS, &lock_state) != |
| 0) { |
| return -EIO; |
| } |
| current_time = drxbsp_hst_clock(); |
| |
| timer_value = current_time - start_time_lock_stage; |
| if (lock_state >= desired_lock_state) { |
| *is_locked = true; |
| done_waiting = true; |
| } /* if ( lock_state >= desired_lock_state ) .. */ |
| else if (lock_state == DRX_NEVER_LOCK) { |
| done_waiting = true; |
| } /* if ( lock_state == DRX_NEVER_LOCK ) .. */ |
| else if (timer_value > timeout_value) { |
| /* lock_state == DRX_NOT_LOCKED and timeout */ |
| done_waiting = true; |
| } else { |
| if (drxbsp_hst_sleep(10) != 0) { |
| return -EIO; |
| } |
| } /* if ( timer_value > timeout_value ) .. */ |
| |
| } /* while */ |
| |
| return 0; |
| } |
| |
| /*============================================================================*/ |
| |
| /** |
| * \brief Determine next frequency to scan. |
| * \param demod: Pointer to demodulator instance. |
| * \param skip : Minimum frequency step to take. |
| * \return int. |
| * \retval 0: Succes. |
| * \retval -EINVAL: Invalid frequency plan. |
| * |
| * Helper function for ctrl_scan_next() function. |
| * Compute next frequency & index in frequency plan. |
| * Check if scan is ready. |
| * |
| */ |
| static int |
| scan_prepare_next_scan(struct drx_demod_instance *demod, s32 skip) |
| { |
| struct drx_common_attr *common_attr = (struct drx_common_attr *) (NULL); |
| u16 table_index = 0; |
| u16 frequency_plan_size = 0; |
| struct drx_frequency_plan *frequency_plan = (struct drx_frequency_plan *) (NULL); |
| s32 next_frequency = 0; |
| s32 tuner_min_frequency = 0; |
| s32 tuner_max_frequency = 0; |
| |
| common_attr = (struct drx_common_attr *) demod->my_common_attr; |
| table_index = common_attr->scan_freq_plan_index; |
| frequency_plan = common_attr->scan_param->frequency_plan; |
| next_frequency = common_attr->scan_next_frequency; |
| tuner_min_frequency = common_attr->tuner_min_freq_rf; |
| tuner_max_frequency = common_attr->tuner_max_freq_rf; |
| |
| do { |
| /* Search next frequency to scan */ |
| |
| /* always take at least one step */ |
| (common_attr->scan_channels_scanned)++; |
| next_frequency += frequency_plan[table_index].step; |
| skip -= frequency_plan[table_index].step; |
| |
| /* and then as many steps necessary to exceed 'skip' |
| without exceeding end of the band */ |
| while ((skip > 0) && |
| (next_frequency <= frequency_plan[table_index].last)) { |
| (common_attr->scan_channels_scanned)++; |
| next_frequency += frequency_plan[table_index].step; |
| skip -= frequency_plan[table_index].step; |
| } |
| /* reset skip, in case we move to the next band later */ |
| skip = 0; |
| |
| if (next_frequency > frequency_plan[table_index].last) { |
| /* reached end of this band */ |
| table_index++; |
| frequency_plan_size = |
| common_attr->scan_param->frequency_plan_size; |
| if (table_index >= frequency_plan_size) { |
| /* reached end of frequency plan */ |
| common_attr->scan_ready = true; |
| } else { |
| next_frequency = frequency_plan[table_index].first; |
| } |
| } |
| if (next_frequency > (tuner_max_frequency)) { |
| /* reached end of tuner range */ |
| common_attr->scan_ready = true; |
| } |
| } while ((next_frequency < tuner_min_frequency) && |
| (!common_attr->scan_ready)); |
| |
| /* Store new values */ |
| common_attr->scan_freq_plan_index = table_index; |
| common_attr->scan_next_frequency = next_frequency; |
| |
| return 0; |
| } |
| |
| /*============================================================================*/ |
| |
| /** |
| * \brief Default DTV scanning function. |
| * |
| * \param demod: Pointer to demodulator instance. |
| * \param scan_command: Scanning command: INIT, NEXT or STOP. |
| * \param scan_channel: Channel to check: frequency and bandwidth, others AUTO |
| * \param get_next_channel: Return true if next frequency is desired at next call |
| * |
| * \return int. |
| * \retval 0: Channel found, DRX_CTRL_GET_CHANNEL can be used |
| * to retrieve channel parameters. |
| * \retval -EBUSY: Channel not found (yet). |
| * \retval -EIO: Something went wrong. |
| * |
| * scan_channel and get_next_channel will be NULL for INIT and STOP. |
| */ |
| static int |
| scan_function_default(void *scan_context, |
| enum drx_scan_command scan_command, |
| struct drx_channel *scan_channel, bool *get_next_channel) |
| { |
| struct drx_demod_instance *demod = NULL; |
| int status = -EIO; |
| bool is_locked = false; |
| |
| demod = (struct drx_demod_instance *) scan_context; |
| |
| if (scan_command != DRX_SCAN_COMMAND_NEXT) { |
| /* just return OK if not doing "scan next" */ |
| return 0; |
| } |
| |
| *get_next_channel = false; |
| |
| status = drx_ctrl(demod, DRX_CTRL_SET_CHANNEL, scan_channel); |
| if (status != 0) { |
| return status; |
| } |
| |
| status = scan_wait_for_lock(demod, &is_locked); |
| if (status != 0) { |
| return status; |
| } |
| |
| /* done with this channel, move to next one */ |
| *get_next_channel = true; |
| |
| if (!is_locked) { |
| /* no channel found */ |
| return -EBUSY; |
| } |
| /* channel found */ |
| return 0; |
| } |
| |
| /*============================================================================*/ |
| |
| /** |
| * \brief Initialize for channel scan. |
| * \param demod: Pointer to demodulator instance. |
| * \param scan_param: Pointer to scan parameters. |
| * \return int. |
| * \retval 0: Initialized for scan. |
| * \retval -EIO: No overlap between frequency plan and tuner |
| * range. |
| * \retval -EINVAL: 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 int |
| ctrl_scan_init(struct drx_demod_instance *demod, struct drx_scan_param *scan_param) |
| { |
| struct drx_common_attr *common_attr = (struct drx_common_attr *) (NULL); |
| s32 max_tuner_freq = 0; |
| s32 min_tuner_freq = 0; |
| u16 nr_channels_in_plan = 0; |
| u16 i = 0; |
| void *scan_context = NULL; |
| |
| common_attr = (struct drx_common_attr *) demod->my_common_attr; |
| common_attr->scan_active = true; |
| |
| /* invalidate a previous SCAN_INIT */ |
| common_attr->scan_param = NULL; |
| common_attr->scan_next_frequency = 0; |
| |
| /* Check parameters */ |
| if (((demod->my_tuner == NULL) && |
| (scan_param->num_tries != 1)) || |
| (scan_param == NULL) || |
| (scan_param->num_tries == 0) || |
| (scan_param->frequency_plan == NULL) || |
| (scan_param->frequency_plan_size == 0) |
| ) { |
| common_attr->scan_active = false; |
| return -EINVAL; |
| } |
| |
| /* Check frequency plan contents */ |
| max_tuner_freq = common_attr->tuner_max_freq_rf; |
| min_tuner_freq = common_attr->tuner_min_freq_rf; |
| for (i = 0; i < (scan_param->frequency_plan_size); i++) { |
| s32 width = 0; |
| s32 step = scan_param->frequency_plan[i].step; |
| s32 first_freq = scan_param->frequency_plan[i].first; |
| s32 last_freq = scan_param->frequency_plan[i].last; |
| s32 min_freq = 0; |
| s32 max_freq = 0; |
| |
| if (step <= 0) { |
| /* Step must be positive and non-zero */ |
| common_attr->scan_active = false; |
| return -EINVAL; |
| } |
| |
| if (first_freq > last_freq) { |
| /* First center frequency is higher than last center frequency */ |
| common_attr->scan_active = false; |
| return -EINVAL; |
| } |
| |
| width = last_freq - first_freq; |
| |
| if ((width % step) != 0) { |
| /* Difference between last and first center frequency is not |
| an integer number of steps */ |
| common_attr->scan_active = false; |
| return -EINVAL; |
| } |
| |
| /* Check if frequency plan entry intersects with tuner range */ |
| if (last_freq >= min_tuner_freq) { |
| if (first_freq <= max_tuner_freq) { |
| if (first_freq >= min_tuner_freq) { |
| min_freq = first_freq; |
| } else { |
| s32 n = 0; |
| |
| n = (min_tuner_freq - first_freq) / step; |
| if (((min_tuner_freq - first_freq) % step) != 0) |
| n++; |
| min_freq = first_freq + n * step; |
| } |
| |
| if (last_freq <= max_tuner_freq) { |
| max_freq = last_freq; |
| } else { |
| s32 n = 0; |
| |
| n = (last_freq - max_tuner_freq) / step; |
| if (((last_freq - max_tuner_freq) % step) != 0) |
| n++; |
| max_freq = last_freq - n * step; |
| } |
| } |
| } |
| |
| /* Keep track of total number of channels within tuner range |
| in this frequency plan. */ |
| if ((min_freq != 0) && (max_freq != 0)) { |
| nr_channels_in_plan += |
| (u16) (((max_freq - min_freq) / step) + 1); |
| |
| /* Determine first frequency (within tuner range) to scan */ |
| if (common_attr->scan_next_frequency == 0) { |
| common_attr->scan_next_frequency = min_freq; |
| common_attr->scan_freq_plan_index = i; |
| } |
| } |
| |
| } /* for ( ... ) */ |
| |
| if (nr_channels_in_plan == 0) { |
| /* Tuner range and frequency plan ranges do not overlap */ |
| common_attr->scan_active = false; |
| return -EIO; |
| } |
| |
| /* Store parameters */ |
| common_attr->scan_ready = false; |
| common_attr->scan_max_channels = nr_channels_in_plan; |
| common_attr->scan_channels_scanned = 0; |
| common_attr->scan_param = scan_param; /* SCAN_NEXT is now allowed */ |
| |
| scan_context = get_scan_context(demod, scan_context); |
| |
| /* |
| * FIXME: Should we really ignore the result of the scan function? |
| */ |
| (*(get_scan_function(demod)))(scan_context, DRX_SCAN_COMMAND_INIT, NULL, NULL); |
| |
| common_attr->scan_active = false; |
| |
| return 0; |
| } |
| |
| /*============================================================================*/ |
| |
| /** |
| * \brief Stop scanning. |
| * \param demod: Pointer to demodulator instance. |
| * \return int. |
| * \retval 0: Scan stopped. |
| * \retval -EIO: Something went wrong. |
| * \retval -EINVAL: Wrong parameters. |
| */ |
| static int ctrl_scan_stop(struct drx_demod_instance *demod) |
| { |
| int status = -EIO; |
| struct drx_common_attr *common_attr = (struct drx_common_attr *) (NULL); |
| void *scan_context = NULL; |
| |
| common_attr = (struct drx_common_attr *) demod->my_common_attr; |
| common_attr->scan_active = true; |
| |
| if ((common_attr->scan_param == NULL) || |
| (common_attr->scan_max_channels == 0)) { |
| /* Scan was not running, just return OK */ |
| common_attr->scan_active = false; |
| return 0; |
| } |
| |
| /* Call default or device-specific scanning stop function */ |
| scan_context = get_scan_context(demod, scan_context); |
| |
| status = (*(get_scan_function(demod))) |
| (scan_context, DRX_SCAN_COMMAND_STOP, NULL, NULL); |
| |
| /* All done, invalidate scan-init */ |
| common_attr->scan_param = NULL; |
| common_attr->scan_max_channels = 0; |
| common_attr->scan_active = false; |
| |
| return status; |
| } |
| |
| /*============================================================================*/ |
| |
| /** |
| * \brief Scan for next channel. |
| * \param demod: Pointer to demodulator instance. |
| * \param scan_progress: Pointer to scan progress. |
| * \return int. |
| * \retval 0: Channel found, DRX_CTRL_GET_CHANNEL can be used |
| * to retrieve channel parameters. |
| * \retval -EBUSY: Tried part of the channels, as specified in |
| * num_tries field of scan parameters. At least one |
| * more call to DRX_CTRL_SCAN_NEXT is needed to |
| * complete scanning. |
| * \retval -ERANGE: Reached end of scan range. |
| * \retval -EIO: Something went wrong. |
| * \retval -EINVAL: Wrong parameters. The scan_progress may be NULL. |
| * |
| * Progress indication will run from 0 upto DRX_SCAN_MAX_PROGRESS during scan. |
| * |
| */ |
| static int ctrl_scan_next(struct drx_demod_instance *demod, u16 *scan_progress) |
| { |
| struct drx_common_attr *common_attr = (struct drx_common_attr *) (NULL); |
| bool *scan_ready = (bool *)(NULL); |
| u16 max_progress = DRX_SCAN_MAX_PROGRESS; |
| u32 num_tries = 0; |
| u32 i = 0; |
| |
| common_attr = (struct drx_common_attr *) demod->my_common_attr; |
| |
| /* Check scan parameters */ |
| if (scan_progress == NULL) { |
| common_attr->scan_active = false; |
| return -EINVAL; |
| } |
| |
| *scan_progress = 0; |
| common_attr->scan_active = true; |
| if ((common_attr->scan_param == NULL) || |
| (common_attr->scan_max_channels == 0)) { |
| /* ctrl_scan_init() was not called succesfully before ctrl_scan_next() */ |
| common_attr->scan_active = false; |
| return -EIO; |
| } |
| |
| *scan_progress = (u16) (((common_attr->scan_channels_scanned) * |
| ((u32) (max_progress))) / |
| (common_attr->scan_max_channels)); |
| |
| /* Scan */ |
| num_tries = common_attr->scan_param->num_tries; |
| scan_ready = &(common_attr->scan_ready); |
| |
| for (i = 0; ((i < num_tries) && (!(*scan_ready))); i++) { |
| struct drx_channel scan_channel = { 0 }; |
| int status = -EIO; |
| struct drx_frequency_plan *freq_plan = (struct drx_frequency_plan *) (NULL); |
| bool next_channel = false; |
| void *scan_context = NULL; |
| |
| /* Next channel to scan */ |
| freq_plan = |
| &(common_attr->scan_param-> |
| frequency_plan[common_attr->scan_freq_plan_index]); |
| scan_channel.frequency = common_attr->scan_next_frequency; |
| scan_channel.bandwidth = freq_plan->bandwidth; |
| scan_channel.mirror = DRX_MIRROR_AUTO; |
| scan_channel.constellation = DRX_CONSTELLATION_AUTO; |
| scan_channel.hierarchy = DRX_HIERARCHY_AUTO; |
| scan_channel.priority = DRX_PRIORITY_HIGH; |
| scan_channel.coderate = DRX_CODERATE_AUTO; |
| scan_channel.guard = DRX_GUARD_AUTO; |
| scan_channel.fftmode = DRX_FFTMODE_AUTO; |
| scan_channel.classification = DRX_CLASSIFICATION_AUTO; |
| scan_channel.symbolrate = 0; |
| scan_channel.interleavemode = DRX_INTERLEAVEMODE_AUTO; |
| scan_channel.ldpc = DRX_LDPC_AUTO; |
| scan_channel.carrier = DRX_CARRIER_AUTO; |
| scan_channel.framemode = DRX_FRAMEMODE_AUTO; |
| scan_channel.pilot = DRX_PILOT_AUTO; |
| |
| /* Call default or device-specific scanning function */ |
| scan_context = get_scan_context(demod, scan_context); |
| |
| status = (*(get_scan_function(demod))) |
| (scan_context, DRX_SCAN_COMMAND_NEXT, &scan_channel, |
| &next_channel); |
| |
| /* Proceed to next channel if requested */ |
| if (next_channel) { |
| int next_status = -EIO; |
| s32 skip = 0; |
| |
| if (status == 0) { |
| /* a channel was found, so skip some frequency steps */ |
| skip = common_attr->scan_param->skip; |
| } |
| next_status = scan_prepare_next_scan(demod, skip); |
| |
| /* keep track of progress */ |
| *scan_progress = |
| (u16) (((common_attr->scan_channels_scanned) * |
| ((u32) (max_progress))) / |
| (common_attr->scan_max_channels)); |
| |
| if (next_status != 0) { |
| common_attr->scan_active = false; |
| return next_status; |
| } |
| } |
| if (status != -EBUSY) { |
| /* channel found or error */ |
| common_attr->scan_active = false; |
| return status; |
| } |
| } /* for ( i = 0; i < ( ... num_tries); i++) */ |
| |
| if ((*scan_ready)) { |
| /* End of scan reached: call stop-scan, ignore any error */ |
| ctrl_scan_stop(demod); |
| common_attr->scan_active = false; |
| return -ERANGE; |
| } |
| |
| common_attr->scan_active = false; |
| |
| return -EBUSY; |
| } |
| |
| #endif /* #ifndef DRX_EXCLUDE_SCAN */ |
| |
| /*============================================================================*/ |
| |
| /** |
| * \brief Program tuner. |
| * \param demod: Pointer to demodulator instance. |
| * \param tunerChannel: Pointer to tuning parameters. |
| * \return int. |
| * \retval 0: Tuner programmed successfully. |
| * \retval -EIO: Something went wrong. |
| * \retval -EINVAL: Wrong parameters. |
| * |
| * tunerChannel passes parameters to program the tuner, |
| * but also returns the actual RF and IF frequency from the tuner. |
| * |
| */ |
| static int |
| ctrl_program_tuner(struct drx_demod_instance *demod, struct drx_channel *channel) |
| { |
| struct drx_common_attr *common_attr = (struct drx_common_attr *) (NULL); |
| enum drx_standard standard = DRX_STANDARD_UNKNOWN; |
| u32 tuner_mode = 0; |
| int status = -EIO; |
| s32 if_frequency = 0; |
| bool tuner_slow_mode = false; |
| |
| /* can't tune without a tuner */ |
| if (demod->my_tuner == NULL) { |
| return -EINVAL; |
| } |
| |
| common_attr = (struct drx_common_attr *) demod->my_common_attr; |
| |
| /* select analog or digital tuner mode based on current standard */ |
| if (drx_ctrl(demod, DRX_CTRL_GET_STANDARD, &standard) != 0) { |
| return -EIO; |
| } |
| |
| if (DRX_ISATVSTD(standard)) { |
| tuner_mode |= TUNER_MODE_ANALOG; |
| } else { /* note: also for unknown standard */ |
| |
| tuner_mode |= TUNER_MODE_DIGITAL; |
| } |
| |
| /* select tuner bandwidth */ |
| switch (channel->bandwidth) { |
| case DRX_BANDWIDTH_6MHZ: |
| tuner_mode |= TUNER_MODE_6MHZ; |
| break; |
| case DRX_BANDWIDTH_7MHZ: |
| tuner_mode |= TUNER_MODE_7MHZ; |
| break; |
| case DRX_BANDWIDTH_8MHZ: |
| tuner_mode |= TUNER_MODE_8MHZ; |
| break; |
| default: /* note: also for unknown bandwidth */ |
| return -EINVAL; |
| } |
| |
| tuner_slow_mode = DRX_ATTR_TUNERSLOWMODE(demod); |
| |
| /* select fast (switch) or slow (lock) tuner mode */ |
| if (tuner_slow_mode) { |
| tuner_mode |= TUNER_MODE_LOCK; |
| } else { |
| tuner_mode |= TUNER_MODE_SWITCH; |
| } |
| |
| if (common_attr->tuner_port_nr == 1) { |
| bool bridge_closed = true; |
| int status_bridge = -EIO; |
| |
| status_bridge = |
| drx_ctrl(demod, DRX_CTRL_I2C_BRIDGE, &bridge_closed); |
| if (status_bridge != 0) { |
| return status_bridge; |
| } |
| } |
| |
| status = drxbsp_tuner_set_frequency(demod->my_tuner, |
| tuner_mode, channel->frequency); |
| |
| /* attempt restoring bridge before checking status of set_frequency */ |
| if (common_attr->tuner_port_nr == 1) { |
| bool bridge_closed = false; |
| int status_bridge = -EIO; |
| |
| status_bridge = |
| drx_ctrl(demod, DRX_CTRL_I2C_BRIDGE, &bridge_closed); |
| if (status_bridge != 0) { |
| return status_bridge; |
| } |
| } |
| |
| /* now check status of drxbsp_tuner_set_frequency */ |
| if (status != 0) { |
| return status; |
| } |
| |
| /* get actual RF and IF frequencies from tuner */ |
| status = drxbsp_tuner_get_frequency(demod->my_tuner, |
| tuner_mode, |
| &(channel->frequency), |
| &(if_frequency)); |
| if (status != 0) { |
| return status; |
| } |
| |
| /* update common attributes with information available from this function; |
| TODO: check if this is required and safe */ |
| DRX_ATTR_INTERMEDIATEFREQ(demod) = if_frequency; |
| |
| return 0; |
| } |
| |
| /*============================================================================*/ |
| |
| /** |
| * \brief function to do a register dump. |
| * \param demod: Pointer to demodulator instance. |
| * \param registers: Registers to dump. |
| * \return int. |
| * \retval 0: Dump executed successfully. |
| * \retval -EIO: Something went wrong. |
| * \retval -EINVAL: Wrong parameters. |
| * |
| */ |
| static int ctrl_dump_registers(struct drx_demod_instance *demod, |
| struct drx_reg_dump *registers) |
| { |
| u16 i = 0; |
| |
| if (registers == NULL) { |
| /* registers not supplied */ |
| return -EINVAL; |
| } |
| |
| /* start dumping registers */ |
| while (registers[i].address != 0) { |
| int status = -EIO; |
| u16 value = 0; |
| u32 data = 0; |
| |
| status = |
| demod->my_access_funct->read_reg16func(demod->my_i2c_dev_addr, |
| registers[i].address, |
| &value, 0); |
| |
| data = (u32) value; |
| |
| if (status != 0) { |
| /* no breakouts; |
| depending on device ID, some HW blocks might not be available */ |
| data |= ((u32) status) << 16; |
| } |
| registers[i].data = data; |
| i++; |
| } |
| |
| /* all done, all OK (any errors are saved inside data) */ |
| return 0; |
| } |
| |
| /*============================================================================*/ |
| /*============================================================================*/ |
| /*===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 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 u_code_read16(u8 *addr) |
| { |
| /* Works fo any host processor */ |
| |
| u16 word = 0; |
| |
| word = ((u16) addr[0]); |
| word <<= 8; |
| word |= ((u16) 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 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 u_code_read32(u8 *addr) |
| { |
| /* Works fo any host processor */ |
| |
| u32 word = 0; |
| |
| word = ((u16) addr[0]); |
| word <<= 8; |
| word |= ((u16) addr[1]); |
| word <<= 8; |
| word |= ((u16) addr[2]); |
| word <<= 8; |
| word |= ((u16) addr[3]); |
| |
| return word; |
| } |
| |
| /*============================================================================*/ |
| |
| /** |
| * \brief Compute CRC of block of microcode data. |
| * \param block_data: Pointer to microcode data. |
| * \param nr_words: Size of microcode block (number of 16 bits words). |
| * \return u16 The computed CRC residu. |
| */ |
| static u16 u_code_compute_crc(u8 *block_data, u16 nr_words) |
| { |
| u16 i = 0; |
| u16 j = 0; |
| u32 crc_word = 0; |
| u32 carry = 0; |
| |
| while (i < nr_words) { |
| crc_word |= (u32) u_code_read16(block_data); |
| for (j = 0; j < 16; j++) { |
| crc_word <<= 1; |
| if (carry != 0) |
| crc_word ^= 0x80050000UL; |
| carry = crc_word & 0x80000000UL; |
| } |
| i++; |
| block_data += (sizeof(u16)); |
| } |
| return (u16)(crc_word >> 16); |
| } |
| |
| /*============================================================================*/ |
| |
| /** |
| * \brief Handle microcode upload or verify. |
| * \param dev_addr: Address of device. |
| * \param mc_info: Pointer to information about microcode data. |
| * \param action: Either UCODE_UPLOAD or UCODE_VERIFY |
| * \return int. |
| * \retval 0: |
| * - 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 -EIO: |
| * - 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 -EINVAL: |
| * - Invalid arguments. |
| * - Provided image is corrupt |
| */ |
| static int |
| ctrl_u_code(struct drx_demod_instance *demod, |
| struct drxu_code_info *mc_info, enum drxu_code_action action) |
| { |
| int rc; |
| u16 i = 0; |
| u16 mc_nr_of_blks = 0; |
| u16 mc_magic_word = 0; |
| u8 *mc_data = (u8 *)(NULL); |
| struct i2c_device_addr *dev_addr = (struct i2c_device_addr *)(NULL); |
| |
| dev_addr = demod->my_i2c_dev_addr; |
| |
| /* Check arguments */ |
| if ((mc_info == NULL) || (mc_info->mc_data == NULL)) { |
| return -EINVAL; |
| } |
| |
| mc_data = mc_info->mc_data; |
| |
| /* Check data */ |
| mc_magic_word = u_code_read16(mc_data); |
| mc_data += sizeof(u16); |
| mc_nr_of_blks = u_code_read16(mc_data); |
| mc_data += sizeof(u16); |
| |
| if ((mc_magic_word != DRX_UCODE_MAGIC_WORD) || (mc_nr_of_blks == 0)) { |
| /* wrong endianess or wrong data ? */ |
| return -EINVAL; |
| } |
| |
| /* Scan microcode blocks first for version info if uploading */ |
| if (action == UCODE_UPLOAD) { |
| /* Clear version block */ |
| DRX_ATTR_MCRECORD(demod).aux_type = 0; |
| DRX_ATTR_MCRECORD(demod).mc_dev_type = 0; |
| DRX_ATTR_MCRECORD(demod).mc_version = 0; |
| DRX_ATTR_MCRECORD(demod).mc_base_version = 0; |
| for (i = 0; i < mc_nr_of_blks; i++) { |
| drxu_code_block_hdr_t block_hdr; |
| |
| /* Process block header */ |
| block_hdr.addr = u_code_read32(mc_data); |
| mc_data += sizeof(u32); |
| block_hdr.size = u_code_read16(mc_data); |
| mc_data += sizeof(u16); |
| block_hdr.flags = u_code_read16(mc_data); |
| mc_data += sizeof(u16); |
| block_hdr.CRC = u_code_read16(mc_data); |
| mc_data += sizeof(u16); |
| |
| if (block_hdr.flags & 0x8) { |
| /* Aux block. Check type */ |
| u8 *auxblk = mc_info->mc_data + block_hdr.addr; |
| u16 auxtype = u_code_read16(auxblk); |
| if (DRX_ISMCVERTYPE(auxtype)) { |
| DRX_ATTR_MCRECORD(demod).aux_type = u_code_read16(auxblk); |
| auxblk += sizeof(u16); |
| DRX_ATTR_MCRECORD(demod).mc_dev_type = u_code_read32(auxblk); |
| auxblk += sizeof(u32); |
| DRX_ATTR_MCRECORD(demod).mc_version = u_code_read32(auxblk); |
| auxblk += sizeof(u32); |
| DRX_ATTR_MCRECORD(demod).mc_base_version = u_code_read32(auxblk); |
| } |
| } |
| |
| /* Next block */ |
| mc_data += block_hdr.size * sizeof(u16); |
| } |
| |
| /* 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 != 0 && rc != -ENOTSUPP) { |
| return rc; |
| } |
| |
| /* Restore data pointer */ |
| mc_data = mc_info->mc_data + 2 * sizeof(u16); |
| } |
| |
| /* Process microcode blocks */ |
| for (i = 0; i < mc_nr_of_blks; i++) { |
| drxu_code_block_hdr_t block_hdr; |
| u16 mc_block_nr_bytes = 0; |
| |
| /* Process block header */ |
| block_hdr.addr = u_code_read32(mc_data); |
| mc_data += sizeof(u32); |
| block_hdr.size = u_code_read16(mc_data); |
| mc_data += sizeof(u16); |
| block_hdr.flags = u_code_read16(mc_data); |
| mc_data += sizeof(u16); |
| block_hdr.CRC = u_code_read16(mc_data); |
| mc_data += sizeof(u16); |
| |
| /* Check block header on: |
| - data larger than 64Kb |
| - if CRC enabled check CRC |
| */ |
| if ((block_hdr.size > 0x7FFF) || |
| (((block_hdr.flags & DRX_UCODE_CRC_FLAG) != 0) && |
| (block_hdr.CRC != u_code_compute_crc(mc_data, block_hdr.size))) |
| ) { |
| /* Wrong data ! */ |
| return -EINVAL; |
| } |
| |
| mc_block_nr_bytes = block_hdr.size * ((u16) sizeof(u16)); |
| |
| if (block_hdr.size != 0) { |
| /* Perform the desired action */ |
| switch (action) { |
| /*================================================================*/ |
| case UCODE_UPLOAD: |
| { |
| /* Upload microcode */ |
| if (demod->my_access_funct-> |
| write_block_func(dev_addr, |
| (dr_xaddr_t) block_hdr. |
| addr, mc_block_nr_bytes, |
| mc_data, |
| 0x0000) != |
| 0) { |
| return -EIO; |
| } /* if */ |
| } |
| break; |
| |
| /*================================================================*/ |
| case UCODE_VERIFY: |
| { |
| int result = 0; |
| u8 mc_data_buffer |
| [DRX_UCODE_MAX_BUF_SIZE]; |
| u32 bytes_to_compare = 0; |
| u32 bytes_left_to_compare = 0; |
| u32 curr_addr = (dr_xaddr_t) 0; |
| u8 *curr_ptr = NULL; |
| |
| bytes_left_to_compare = mc_block_nr_bytes; |
| curr_addr = block_hdr.addr; |
| curr_ptr = mc_data; |
| |
| while (bytes_left_to_compare != 0) { |
| if (bytes_left_to_compare > |
| ((u32) |
| DRX_UCODE_MAX_BUF_SIZE)) { |
| bytes_to_compare = |
| ((u32) |
| DRX_UCODE_MAX_BUF_SIZE); |
| } else { |
| bytes_to_compare = |
| bytes_left_to_compare; |
| } |
| |
| if (demod->my_access_funct-> |
| read_block_func(dev_addr, |
| curr_addr, |
| (u16) |
| bytes_to_compare, |
| (u8 *) |
| mc_data_buffer, |
| 0x0000) != |
| 0) { |
| return -EIO; |
| } |
| |
| result = |
| drxbsp_hst_memcmp(curr_ptr, |
| mc_data_buffer, |
| bytes_to_compare); |
| |
| if (result != 0) { |
| return -EIO; |
| } |
| |
| curr_addr += |
| ((dr_xaddr_t) |
| (bytes_to_compare / 2)); |
| curr_ptr = |
| &(curr_ptr[bytes_to_compare]); |
| bytes_left_to_compare -= |
| ((u32) bytes_to_compare); |
| } /* while( bytes_to_compare > DRX_UCODE_MAX_BUF_SIZE ) */ |
| } |
| break; |
| |
| /*================================================================*/ |
| default: |
| return -EINVAL; |
| break; |
| |
| } /* switch ( action ) */ |
| } |
| |
| /* if (block_hdr.size != 0 ) */ |
| /* Next block */ |
| mc_data += mc_block_nr_bytes; |
| |
| } /* for( i = 0 ; i<mc_nr_of_blks ; i++ ) */ |
| |
| return 0; |
| } |
| |
| /*============================================================================*/ |
| |
| /** |
| * \brief Build list of version information. |
| * \param demod: A pointer to a demodulator instance. |
| * \param version_list: Pointer to linked list of versions. |
| * \return int. |
| * \retval 0: Version information stored in version_list |
| * \retval -EINVAL: Invalid arguments. |
| */ |
| static int |
| ctrl_version(struct drx_demod_instance *demod, struct drx_version_list **version_list) |
| { |
| static char drx_driver_core_module_name[] = "Core driver"; |
| static char drx_driver_core_version_text[] = |
| DRX_VERSIONSTRING(VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH); |
| |
| static struct drx_version drx_driver_core_version; |
| static struct drx_version_list drx_driver_core_version_list; |
| |
| struct drx_version_list *demod_version_list = (struct drx_version_list *) (NULL); |
| int return_status = -EIO; |
| |
| /* Check arguments */ |
| if (version_list == NULL) { |
| return -EINVAL; |
| } |
| |
| /* Get version info list from demod */ |
| return_status = (*(demod->my_demod_funct->ctrl_func)) (demod, |
| DRX_CTRL_VERSION, |
| (void *) |
| &demod_version_list); |
| |
| /* Always fill in the information of the driver SW . */ |
| drx_driver_core_version.module_type = DRX_MODULE_DRIVERCORE; |
| drx_driver_core_version.module_name = drx_driver_core_module_name; |
| drx_driver_core_version.v_major = VERSION_MAJOR; |
| drx_driver_core_version.v_minor = VERSION_MINOR; |
| drx_driver_core_version.v_patch = VERSION_PATCH; |
| drx_driver_core_version.v_string = drx_driver_core_version_text; |
| |
| drx_driver_core_version_list.version = &drx_driver_core_version; |
| drx_driver_core_version_list.next = (struct drx_version_list *) (NULL); |
| |
| if ((return_status == 0) && (demod_version_list != 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. */ |
| struct drx_version_list *current_list_element = demod_version_list; |
| while (current_list_element->next != NULL) { |
| current_list_element = current_list_element->next; |
| } |
| current_list_element->next = &drx_driver_core_version_list; |
| |
| *version_list = demod_version_list; |
| } else { |
| /* Just return versioninfo from driver */ |
| *version_list = &drx_driver_core_version_list; |
| } |
| |
| return 0; |
| } |
| |
| /*============================================================================*/ |
| /*============================================================================*/ |
| /*== Exported functions ======================================================*/ |
| /*============================================================================*/ |
| /*============================================================================*/ |
| |
| /** |
| * \brief This function is obsolete. |
| * \param demods: Don't care, parameter is ignored. |
| * \return int Return status. |
| * \retval 0: Initialization completed. |
| * |
| * This function is obsolete, prototype available for backward compatability. |
| * |
| */ |
| |
| int drx_init(struct drx_demod_instance *demods[]) |
| { |
| return 0; |
| } |
| |
| /*============================================================================*/ |
| |
| /** |
| * \brief This function is obsolete. |
| * \return int Return status. |
| * \retval 0: Terminated driver successful. |
| * |
| * This function is obsolete, prototype available for backward compatability. |
| * |
| */ |
| |
| int drx_term(void) |
| { |
| return 0; |
| } |
| |
| /*============================================================================*/ |
| |
| /** |
| * \brief Open a demodulator instance. |
| * \param demod: A pointer to a demodulator instance. |
| * \return int Return status. |
| * \retval 0: Opened demod instance with succes. |
| * \retval -EIO: Driver not initialized or unable to initialize |
| * demod. |
| * \retval -EINVAL: Demod instance has invalid content. |
| * |
| */ |
| |
| int drx_open(struct drx_demod_instance *demod) |
| { |
| int status = 0; |
| |
| if ((demod == NULL) || |
| (demod->my_demod_funct == NULL) || |
| (demod->my_common_attr == NULL) || |
| (demod->my_ext_attr == NULL) || |
| (demod->my_i2c_dev_addr == NULL) || |
| (demod->my_common_attr->is_opened)) { |
| return -EINVAL; |
| } |
| |
| status = (*(demod->my_demod_funct->open_func)) (demod); |
| |
| if (status == 0) |
| demod->my_common_attr->is_opened = true; |
| |
| return status; |
| } |
| |
| /*============================================================================*/ |
| |
| /** |
| * \brief Close device. |
| * \param demod: A pointer to a demodulator instance. |
| * \return int Return status. |
| * \retval 0: Closed demod instance with succes. |
| * \retval -EIO: Driver not initialized or error during close |
| * demod. |
| * \retval -EINVAL: Demod instance has invalid content. |
| * |
| * Free resources occupied by device instance. |
| * Put device into sleep mode. |
| */ |
| |
| int drx_close(struct drx_demod_instance *demod) |
| { |
| int status = 0; |
| |
| if ((demod == NULL) || |
| (demod->my_demod_funct == NULL) || |
| (demod->my_common_attr == NULL) || |
| (demod->my_ext_attr == NULL) || |
| (demod->my_i2c_dev_addr == NULL) || |
| (!demod->my_common_attr->is_opened)) { |
| return -EINVAL; |
| } |
| |
| status = (*(demod->my_demod_funct->close_func)) (demod); |
| |
| DRX_ATTR_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 ctrl_data: Pointer to data structure for control function. |
| * \return int Return status. |
| * \retval 0: Control function completed successfully. |
| * \retval -EIO: Driver not initialized or error during |
| * control demod. |
| * \retval -EINVAL: Demod instance or ctrl_data has invalid |
| * content. |
| * \retval -ENOTSUPP: Specified control function is not |
| * available. |
| * |
| * Data needed or returned by the control function is stored in ctrl_data. |
| * |
| */ |
| |
| int |
| drx_ctrl(struct drx_demod_instance *demod, u32 ctrl, void *ctrl_data) |
| { |
| int status = -EIO; |
| |
| if ((demod == NULL) || |
| (demod->my_demod_funct == NULL) || |
| (demod->my_common_attr == NULL) || |
| (demod->my_ext_attr == NULL) || (demod->my_i2c_dev_addr == NULL) |
| ) { |
| return -EINVAL; |
| } |
| |
| if (((!demod->my_common_attr->is_opened) && |
| (ctrl != DRX_CTRL_PROBE_DEVICE) && (ctrl != DRX_CTRL_VERSION)) |
| ) { |
| return -EINVAL; |
| } |
| |
| if ((DRX_ISPOWERDOWNMODE(demod->my_common_attr->current_power_mode) && |
| (ctrl != DRX_CTRL_POWER_MODE) && |
| (ctrl != DRX_CTRL_PROBE_DEVICE) && |
| (ctrl != DRX_CTRL_NOP) && (ctrl != DRX_CTRL_VERSION) |
| ) |
| ) { |
| return -ENOTSUPP; |
| } |
| |
| /* Fixed control functions */ |
| switch (ctrl) { |
| /*======================================================================*/ |
| case DRX_CTRL_NOP: |
| /* No operation */ |
| return 0; |
| break; |
| |
| /*======================================================================*/ |
| case DRX_CTRL_VERSION: |
| return ctrl_version(demod, (struct drx_version_list **)ctrl_data); |
| break; |
| |
| /*======================================================================*/ |
| default: |
| /* Do nothing */ |
| break; |
| } |
| |
| /* Virtual functions */ |
| /* First try calling function from derived class */ |
| status = (*(demod->my_demod_funct->ctrl_func)) (demod, ctrl, ctrl_data); |
| if (status == -ENOTSUPP) { |
| /* Now try calling a the base class function */ |
| switch (ctrl) { |
| /*===================================================================*/ |
| case DRX_CTRL_LOAD_UCODE: |
| return ctrl_u_code(demod, |
| (struct drxu_code_info *)ctrl_data, |
| UCODE_UPLOAD); |
| break; |
| |
| /*===================================================================*/ |
| case DRX_CTRL_VERIFY_UCODE: |
| { |
| return ctrl_u_code(demod, |
| (struct drxu_code_info *)ctrl_data, |
| UCODE_VERIFY); |
| } |
| break; |
| |
| #ifndef DRX_EXCLUDE_SCAN |
| /*===================================================================*/ |
| case DRX_CTRL_SCAN_INIT: |
| { |
| return ctrl_scan_init(demod, |
| (struct drx_scan_param *) ctrl_data); |
| } |
| break; |
| |
| /*===================================================================*/ |
| case DRX_CTRL_SCAN_NEXT: |
| { |
| return ctrl_scan_next(demod, (u16 *)ctrl_data); |
| } |
| break; |
| |
| /*===================================================================*/ |
| case DRX_CTRL_SCAN_STOP: |
| { |
| return ctrl_scan_stop(demod); |
| } |
| break; |
| #endif /* #ifndef DRX_EXCLUDE_SCAN */ |
| |
| /*===================================================================*/ |
| case DRX_CTRL_PROGRAM_TUNER: |
| { |
| return ctrl_program_tuner(demod, |
| (struct drx_channel *) |
| ctrl_data); |
| } |
| break; |
| |
| /*===================================================================*/ |
| case DRX_CTRL_DUMP_REGISTERS: |
| { |
| return ctrl_dump_registers(demod, |
| (struct drx_reg_dump *) |
| ctrl_data); |
| } |
| break; |
| |
| /*===================================================================*/ |
| default: |
| return -ENOTSUPP; |
| } |
| } else { |
| return status; |
| } |
| |
| return 0; |
| } |
| |
| /*============================================================================*/ |
| |
| /* END OF FILE */ |