Commit | Line | Data |
---|---|---|
ca3355a9 DH |
1 | /* |
2 | Copyright (c), 2004-2005,2007-2010 Trident Microsystems, Inc. | |
3 | All rights reserved. | |
4 | ||
5 | Redistribution and use in source and binary forms, with or without | |
6 | modification, are permitted provided that the following conditions are met: | |
7 | ||
8 | * Redistributions of source code must retain the above copyright notice, | |
9 | this list of conditions and the following disclaimer. | |
10 | * Redistributions in binary form must reproduce the above copyright notice, | |
11 | this list of conditions and the following disclaimer in the documentation | |
12 | and/or other materials provided with the distribution. | |
13 | * Neither the name of Trident Microsystems nor Hauppauge Computer Works | |
14 | nor the names of its contributors may be used to endorse or promote | |
15 | products derived from this software without specific prior written | |
16 | permission. | |
17 | ||
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
21 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE | |
22 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
23 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
24 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
25 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
26 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
28 | POSSIBILITY OF SUCH DAMAGE. | |
29 | */ | |
30 | ||
38b2df95 DH |
31 | /** |
32 | * \file $Id: drx_driver.c,v 1.40 2010/01/12 01:24:56 lfeng Exp $ | |
33 | * | |
34 | * \brief Generic DRX functionality, DRX driver core. | |
35 | * | |
38b2df95 DH |
36 | */ |
37 | ||
38 | /*------------------------------------------------------------------------------ | |
39 | INCLUDE FILES | |
40 | ------------------------------------------------------------------------------*/ | |
41 | #include "drx_driver.h" | |
42 | #include "bsp_host.h" | |
43 | ||
44 | #define VERSION_FIXED 0 | |
45 | #if VERSION_FIXED | |
46 | #define VERSION_MAJOR 0 | |
47 | #define VERSION_MINOR 0 | |
48 | #define VERSION_PATCH 0 | |
49 | #else | |
50 | #include "drx_driver_version.h" | |
51 | #endif | |
52 | ||
53 | /*------------------------------------------------------------------------------ | |
54 | DEFINES | |
55 | ------------------------------------------------------------------------------*/ | |
56 | ||
57 | /*============================================================================*/ | |
58 | /*=== MICROCODE RELATED DEFINES ==============================================*/ | |
59 | /*============================================================================*/ | |
60 | ||
61 | /** \brief Magic word for checking correct Endianess of microcode data. */ | |
62 | #ifndef DRX_UCODE_MAGIC_WORD | |
43a431e4 | 63 | #define DRX_UCODE_MAGIC_WORD ((((u16)'H')<<8)+((u16)'L')) |
38b2df95 DH |
64 | #endif |
65 | ||
66 | /** \brief CRC flag in ucode header, flags field. */ | |
67 | #ifndef DRX_UCODE_CRC_FLAG | |
68 | #define DRX_UCODE_CRC_FLAG (0x0001) | |
69 | #endif | |
70 | ||
71 | /** \brief Compression flag in ucode header, flags field. */ | |
72 | #ifndef DRX_UCODE_COMPRESSION_FLAG | |
73 | #define DRX_UCODE_COMPRESSION_FLAG (0x0002) | |
74 | #endif | |
75 | ||
76 | /** \brief Maximum size of buffer used to verify the microcode. | |
77 | Must be an even number. */ | |
78 | #ifndef DRX_UCODE_MAX_BUF_SIZE | |
79 | #define DRX_UCODE_MAX_BUF_SIZE (DRXDAP_MAX_RCHUNKSIZE) | |
80 | #endif | |
81 | #if DRX_UCODE_MAX_BUF_SIZE & 1 | |
82 | #error DRX_UCODE_MAX_BUF_SIZE must be an even number | |
83 | #endif | |
84 | ||
85 | /*============================================================================*/ | |
86 | /*=== CHANNEL SCAN RELATED DEFINES ===========================================*/ | |
87 | /*============================================================================*/ | |
88 | ||
89 | /** | |
90 | * \brief Maximum progress indication. | |
91 | * | |
92 | * Progress indication will run from 0 upto DRX_SCAN_MAX_PROGRESS during scan. | |
93 | * | |
94 | */ | |
95 | #ifndef DRX_SCAN_MAX_PROGRESS | |
96 | #define DRX_SCAN_MAX_PROGRESS 1000 | |
97 | #endif | |
98 | ||
99 | /*============================================================================*/ | |
100 | /*=== MACROS =================================================================*/ | |
101 | /*============================================================================*/ | |
102 | ||
103 | #define DRX_ISPOWERDOWNMODE( mode ) ( ( mode == DRX_POWER_MODE_9 ) || \ | |
104 | ( mode == DRX_POWER_MODE_10 ) || \ | |
105 | ( mode == DRX_POWER_MODE_11 ) || \ | |
106 | ( mode == DRX_POWER_MODE_12 ) || \ | |
107 | ( mode == DRX_POWER_MODE_13 ) || \ | |
108 | ( mode == DRX_POWER_MODE_14 ) || \ | |
109 | ( mode == DRX_POWER_MODE_15 ) || \ | |
110 | ( mode == DRX_POWER_MODE_16 ) || \ | |
111 | ( mode == DRX_POWER_DOWN ) ) | |
112 | ||
113 | /*------------------------------------------------------------------------------ | |
114 | GLOBAL VARIABLES | |
115 | ------------------------------------------------------------------------------*/ | |
116 | ||
117 | /*------------------------------------------------------------------------------ | |
118 | STRUCTURES | |
119 | ------------------------------------------------------------------------------*/ | |
120 | /** \brief Structure of the microcode block headers */ | |
121 | typedef struct { | |
43a431e4 | 122 | u32 addr; |
443f18d0 | 123 | /**< Destination address of the data in this block */ |
43a431e4 | 124 | u16 size; |
443f18d0 | 125 | /**< Size of the block data following this header counted in |
38b2df95 | 126 | 16 bits words */ |
43a431e4 | 127 | u16 flags; |
443f18d0 | 128 | /**< Flags for this data block: |
38b2df95 DH |
129 | - bit[0]= CRC on/off |
130 | - bit[1]= compression on/off | |
131 | - bit[15..2]=reserved */ | |
43a431e4 | 132 | u16 CRC;/**< CRC value of the data block, only valid if CRC flag is |
38b2df95 DH |
133 | set. */ |
134 | } DRXUCodeBlockHdr_t, *pDRXUCodeBlockHdr_t; | |
135 | ||
136 | /*------------------------------------------------------------------------------ | |
137 | FUNCTIONS | |
138 | ------------------------------------------------------------------------------*/ | |
139 | ||
140 | /*============================================================================*/ | |
141 | /*============================================================================*/ | |
142 | /*== Channel Scan Functions ==================================================*/ | |
143 | /*============================================================================*/ | |
144 | /*============================================================================*/ | |
145 | ||
146 | #ifndef DRX_EXCLUDE_SCAN | |
147 | ||
148 | /* Prototype of default scanning function */ | |
149 | static DRXStatus_t | |
443f18d0 MCC |
150 | ScanFunctionDefault(void *scanContext, |
151 | DRXScanCommand_t scanCommand, | |
152 | pDRXChannel_t scanChannel, pBool_t getNextChannel); | |
38b2df95 DH |
153 | |
154 | /** | |
155 | * \brief Get pointer to scanning function. | |
156 | * \param demod: Pointer to demodulator instance. | |
157 | * \return DRXScanFunc_t. | |
158 | */ | |
443f18d0 | 159 | static DRXScanFunc_t GetScanFunction(pDRXDemodInstance_t demod) |
38b2df95 | 160 | { |
443f18d0 MCC |
161 | pDRXCommonAttr_t commonAttr = (pDRXCommonAttr_t) (NULL); |
162 | DRXScanFunc_t scanFunc = (DRXScanFunc_t) (NULL); | |
163 | ||
164 | /* get scan function from common attributes */ | |
165 | commonAttr = (pDRXCommonAttr_t) demod->myCommonAttr; | |
166 | scanFunc = commonAttr->scanFunction; | |
167 | ||
168 | if (scanFunc != NULL) { | |
169 | /* return device-specific scan function if it's not NULL */ | |
170 | return scanFunc; | |
171 | } | |
172 | /* otherwise return default scan function in core driver */ | |
173 | return &ScanFunctionDefault; | |
38b2df95 DH |
174 | } |
175 | ||
176 | /** | |
177 | * \brief Get Context pointer. | |
178 | * \param demod: Pointer to demodulator instance. | |
179 | * \param scanContext: Context Pointer. | |
180 | * \return DRXScanFunc_t. | |
181 | */ | |
443f18d0 | 182 | void *GetScanContext(pDRXDemodInstance_t demod, void *scanContext) |
38b2df95 | 183 | { |
443f18d0 | 184 | pDRXCommonAttr_t commonAttr = (pDRXCommonAttr_t) (NULL); |
38b2df95 | 185 | |
443f18d0 MCC |
186 | /* get scan function from common attributes */ |
187 | commonAttr = (pDRXCommonAttr_t) demod->myCommonAttr; | |
188 | scanContext = commonAttr->scanContext; | |
38b2df95 | 189 | |
443f18d0 MCC |
190 | if (scanContext == NULL) { |
191 | scanContext = (void *)demod; | |
192 | } | |
38b2df95 | 193 | |
443f18d0 | 194 | return scanContext; |
38b2df95 DH |
195 | } |
196 | ||
197 | /** | |
198 | * \brief Wait for lock while scanning. | |
199 | * \param demod: Pointer to demodulator instance. | |
200 | * \param lockStat: Pointer to bool indicating if end result is lock or not. | |
201 | * \return DRXStatus_t. | |
202 | * \retval DRX_STS_OK: Success | |
203 | * \retval DRX_STS_ERROR: I2C failure or bsp function failure. | |
204 | * | |
205 | * Wait until timeout, desired lock or NEVER_LOCK. | |
206 | * Assume: | |
207 | * - lock function returns : at least DRX_NOT_LOCKED and a lock state | |
208 | * higher than DRX_NOT_LOCKED. | |
209 | * - BSP has a clock function to retrieve a millisecond ticker value. | |
210 | * - BSP has a sleep function to enable sleep of n millisecond. | |
211 | * | |
212 | * In case DRX_NEVER_LOCK is returned the poll-wait will be aborted. | |
213 | * | |
214 | */ | |
443f18d0 | 215 | static DRXStatus_t ScanWaitForLock(pDRXDemodInstance_t demod, pBool_t isLocked) |
38b2df95 | 216 | { |
443f18d0 MCC |
217 | Bool_t doneWaiting = FALSE; |
218 | DRXLockStatus_t lockState = DRX_NOT_LOCKED; | |
219 | DRXLockStatus_t desiredLockState = DRX_NOT_LOCKED; | |
43a431e4 MCC |
220 | u32 timeoutValue = 0; |
221 | u32 startTimeLockStage = 0; | |
222 | u32 currentTime = 0; | |
223 | u32 timerValue = 0; | |
443f18d0 MCC |
224 | |
225 | *isLocked = FALSE; | |
43a431e4 | 226 | timeoutValue = (u32) demod->myCommonAttr->scanDemodLockTimeout; |
443f18d0 MCC |
227 | desiredLockState = demod->myCommonAttr->scanDesiredLock; |
228 | startTimeLockStage = DRXBSP_HST_Clock(); | |
229 | ||
230 | /* Start polling loop, checking for lock & timeout */ | |
231 | while (doneWaiting == FALSE) { | |
232 | ||
233 | if (DRX_Ctrl(demod, DRX_CTRL_LOCK_STATUS, &lockState) != | |
234 | DRX_STS_OK) { | |
235 | return DRX_STS_ERROR; | |
236 | } | |
237 | currentTime = DRXBSP_HST_Clock(); | |
238 | ||
239 | timerValue = currentTime - startTimeLockStage; | |
240 | if (lockState >= desiredLockState) { | |
241 | *isLocked = TRUE; | |
242 | doneWaiting = TRUE; | |
243 | } /* if ( lockState >= desiredLockState ) .. */ | |
244 | else if (lockState == DRX_NEVER_LOCK) { | |
245 | doneWaiting = TRUE; | |
246 | } /* if ( lockState == DRX_NEVER_LOCK ) .. */ | |
247 | else if (timerValue > timeoutValue) { | |
248 | /* lockState == DRX_NOT_LOCKED and timeout */ | |
249 | doneWaiting = TRUE; | |
250 | } else { | |
251 | if (DRXBSP_HST_Sleep(10) != DRX_STS_OK) { | |
252 | return DRX_STS_ERROR; | |
253 | } | |
254 | } /* if ( timerValue > timeoutValue ) .. */ | |
255 | ||
256 | } /* while */ | |
257 | ||
258 | return DRX_STS_OK; | |
38b2df95 DH |
259 | } |
260 | ||
261 | /*============================================================================*/ | |
262 | ||
263 | /** | |
264 | * \brief Determine next frequency to scan. | |
265 | * \param demod: Pointer to demodulator instance. | |
266 | * \param skip : Minimum frequency step to take. | |
267 | * \return DRXStatus_t. | |
268 | * \retval DRX_STS_OK: Succes. | |
269 | * \retval DRX_STS_INVALID_ARG: Invalid frequency plan. | |
270 | * | |
271 | * Helper function for CtrlScanNext() function. | |
272 | * Compute next frequency & index in frequency plan. | |
273 | * Check if scan is ready. | |
274 | * | |
275 | */ | |
276 | static DRXStatus_t | |
443f18d0 | 277 | ScanPrepareNextScan(pDRXDemodInstance_t demod, DRXFrequency_t skip) |
38b2df95 | 278 | { |
443f18d0 | 279 | pDRXCommonAttr_t commonAttr = (pDRXCommonAttr_t) (NULL); |
43a431e4 MCC |
280 | u16 tableIndex = 0; |
281 | u16 frequencyPlanSize = 0; | |
443f18d0 MCC |
282 | pDRXFrequencyPlan_t frequencyPlan = (pDRXFrequencyPlan_t) (NULL); |
283 | DRXFrequency_t nextFrequency = 0; | |
284 | DRXFrequency_t tunerMinFrequency = 0; | |
285 | DRXFrequency_t tunerMaxFrequency = 0; | |
286 | ||
287 | commonAttr = (pDRXCommonAttr_t) demod->myCommonAttr; | |
288 | tableIndex = commonAttr->scanFreqPlanIndex; | |
289 | frequencyPlan = commonAttr->scanParam->frequencyPlan; | |
290 | nextFrequency = commonAttr->scanNextFrequency; | |
291 | tunerMinFrequency = commonAttr->tunerMinFreqRF; | |
292 | tunerMaxFrequency = commonAttr->tunerMaxFreqRF; | |
293 | ||
294 | do { | |
295 | /* Search next frequency to scan */ | |
296 | ||
297 | /* always take at least one step */ | |
298 | (commonAttr->scanChannelsScanned)++; | |
299 | nextFrequency += frequencyPlan[tableIndex].step; | |
300 | skip -= frequencyPlan[tableIndex].step; | |
301 | ||
302 | /* and then as many steps necessary to exceed 'skip' | |
303 | without exceeding end of the band */ | |
304 | while ((skip > 0) && | |
305 | (nextFrequency <= frequencyPlan[tableIndex].last)) { | |
306 | (commonAttr->scanChannelsScanned)++; | |
307 | nextFrequency += frequencyPlan[tableIndex].step; | |
308 | skip -= frequencyPlan[tableIndex].step; | |
309 | } | |
310 | /* reset skip, in case we move to the next band later */ | |
311 | skip = 0; | |
312 | ||
313 | if (nextFrequency > frequencyPlan[tableIndex].last) { | |
314 | /* reached end of this band */ | |
315 | tableIndex++; | |
316 | frequencyPlanSize = | |
317 | commonAttr->scanParam->frequencyPlanSize; | |
318 | if (tableIndex >= frequencyPlanSize) { | |
319 | /* reached end of frequency plan */ | |
320 | commonAttr->scanReady = TRUE; | |
321 | } else { | |
322 | nextFrequency = frequencyPlan[tableIndex].first; | |
323 | } | |
324 | } | |
325 | if (nextFrequency > (tunerMaxFrequency)) { | |
326 | /* reached end of tuner range */ | |
327 | commonAttr->scanReady = TRUE; | |
328 | } | |
329 | } while ((nextFrequency < tunerMinFrequency) && | |
330 | (commonAttr->scanReady == FALSE)); | |
331 | ||
332 | /* Store new values */ | |
333 | commonAttr->scanFreqPlanIndex = tableIndex; | |
334 | commonAttr->scanNextFrequency = nextFrequency; | |
335 | ||
336 | return DRX_STS_OK; | |
38b2df95 DH |
337 | } |
338 | ||
339 | /*============================================================================*/ | |
340 | ||
341 | /** | |
342 | * \brief Default DTV scanning function. | |
343 | * | |
344 | * \param demod: Pointer to demodulator instance. | |
345 | * \param scanCommand: Scanning command: INIT, NEXT or STOP. | |
346 | * \param scanChannel: Channel to check: frequency and bandwidth, others AUTO | |
347 | * \param getNextChannel: Return TRUE if next frequency is desired at next call | |
348 | * | |
349 | * \return DRXStatus_t. | |
350 | * \retval DRX_STS_OK: Channel found, DRX_CTRL_GET_CHANNEL can be used | |
351 | * to retrieve channel parameters. | |
352 | * \retval DRX_STS_BUSY: Channel not found (yet). | |
353 | * \retval DRX_STS_ERROR: Something went wrong. | |
354 | * | |
355 | * scanChannel and getNextChannel will be NULL for INIT and STOP. | |
356 | */ | |
357 | static DRXStatus_t | |
443f18d0 MCC |
358 | ScanFunctionDefault(void *scanContext, |
359 | DRXScanCommand_t scanCommand, | |
360 | pDRXChannel_t scanChannel, pBool_t getNextChannel) | |
38b2df95 | 361 | { |
443f18d0 MCC |
362 | pDRXDemodInstance_t demod = NULL; |
363 | DRXStatus_t status = DRX_STS_ERROR; | |
364 | Bool_t isLocked = FALSE; | |
365 | ||
366 | demod = (pDRXDemodInstance_t) scanContext; | |
367 | ||
368 | if (scanCommand != DRX_SCAN_COMMAND_NEXT) { | |
369 | /* just return OK if not doing "scan next" */ | |
370 | return DRX_STS_OK; | |
371 | } | |
372 | ||
373 | *getNextChannel = FALSE; | |
374 | ||
375 | status = DRX_Ctrl(demod, DRX_CTRL_SET_CHANNEL, scanChannel); | |
376 | if (status != DRX_STS_OK) { | |
377 | return (status); | |
378 | } | |
379 | ||
380 | status = ScanWaitForLock(demod, &isLocked); | |
381 | if (status != DRX_STS_OK) { | |
382 | return status; | |
383 | } | |
384 | ||
385 | /* done with this channel, move to next one */ | |
386 | *getNextChannel = TRUE; | |
387 | ||
388 | if (isLocked == FALSE) { | |
389 | /* no channel found */ | |
390 | return DRX_STS_BUSY; | |
391 | } | |
392 | /* channel found */ | |
393 | return DRX_STS_OK; | |
38b2df95 DH |
394 | } |
395 | ||
396 | /*============================================================================*/ | |
397 | ||
398 | /** | |
399 | * \brief Initialize for channel scan. | |
400 | * \param demod: Pointer to demodulator instance. | |
401 | * \param scanParam: Pointer to scan parameters. | |
402 | * \return DRXStatus_t. | |
403 | * \retval DRX_STS_OK: Initialized for scan. | |
404 | * \retval DRX_STS_ERROR: No overlap between frequency plan and tuner | |
405 | * range. | |
406 | * \retval DRX_STS_INVALID_ARG: Wrong parameters. | |
407 | * | |
408 | * This function should be called before starting a complete channel scan. | |
409 | * It will prepare everything for a complete channel scan. | |
410 | * After calling this function the DRX_CTRL_SCAN_NEXT control function can be | |
411 | * used to perform the actual scanning. Scanning will start at the first | |
412 | * center frequency of the frequency plan that is within the tuner range. | |
413 | * | |
414 | */ | |
415 | static DRXStatus_t | |
443f18d0 | 416 | CtrlScanInit(pDRXDemodInstance_t demod, pDRXScanParam_t scanParam) |
38b2df95 | 417 | { |
443f18d0 MCC |
418 | DRXStatus_t status = DRX_STS_ERROR; |
419 | pDRXCommonAttr_t commonAttr = (pDRXCommonAttr_t) (NULL); | |
420 | DRXFrequency_t maxTunerFreq = 0; | |
421 | DRXFrequency_t minTunerFreq = 0; | |
43a431e4 MCC |
422 | u16 nrChannelsInPlan = 0; |
423 | u16 i = 0; | |
443f18d0 MCC |
424 | void *scanContext = NULL; |
425 | ||
426 | commonAttr = (pDRXCommonAttr_t) demod->myCommonAttr; | |
427 | commonAttr->scanActive = TRUE; | |
428 | ||
429 | /* invalidate a previous SCAN_INIT */ | |
430 | commonAttr->scanParam = (pDRXScanParam_t) (NULL); | |
431 | commonAttr->scanNextFrequency = 0; | |
432 | ||
433 | /* Check parameters */ | |
434 | if (((demod->myTuner == NULL) && | |
435 | (scanParam->numTries != 1)) || | |
436 | (scanParam == NULL) || | |
437 | (scanParam->numTries == 0) || | |
438 | (scanParam->frequencyPlan == NULL) || | |
439 | (scanParam->frequencyPlanSize == 0) | |
440 | ) { | |
441 | commonAttr->scanActive = FALSE; | |
442 | return DRX_STS_INVALID_ARG; | |
443 | } | |
444 | ||
445 | /* Check frequency plan contents */ | |
446 | maxTunerFreq = commonAttr->tunerMaxFreqRF; | |
447 | minTunerFreq = commonAttr->tunerMinFreqRF; | |
448 | for (i = 0; i < (scanParam->frequencyPlanSize); i++) { | |
449 | DRXFrequency_t width = 0; | |
450 | DRXFrequency_t step = scanParam->frequencyPlan[i].step; | |
451 | DRXFrequency_t firstFreq = scanParam->frequencyPlan[i].first; | |
452 | DRXFrequency_t lastFreq = scanParam->frequencyPlan[i].last; | |
453 | DRXFrequency_t minFreq = 0; | |
454 | DRXFrequency_t maxFreq = 0; | |
455 | ||
456 | if (step <= 0) { | |
457 | /* Step must be positive and non-zero */ | |
458 | commonAttr->scanActive = FALSE; | |
459 | return DRX_STS_INVALID_ARG; | |
460 | } | |
461 | ||
462 | if (firstFreq > lastFreq) { | |
463 | /* First center frequency is higher than last center frequency */ | |
464 | commonAttr->scanActive = FALSE; | |
465 | return DRX_STS_INVALID_ARG; | |
466 | } | |
467 | ||
468 | width = lastFreq - firstFreq; | |
469 | ||
470 | if ((width % step) != 0) { | |
471 | /* Difference between last and first center frequency is not | |
472 | an integer number of steps */ | |
473 | commonAttr->scanActive = FALSE; | |
474 | return DRX_STS_INVALID_ARG; | |
475 | } | |
476 | ||
477 | /* Check if frequency plan entry intersects with tuner range */ | |
478 | if (lastFreq >= minTunerFreq) { | |
479 | if (firstFreq <= maxTunerFreq) { | |
480 | if (firstFreq >= minTunerFreq) { | |
481 | minFreq = firstFreq; | |
482 | } else { | |
483 | DRXFrequency_t n = 0; | |
484 | ||
485 | n = (minTunerFreq - firstFreq) / step; | |
486 | if (((minTunerFreq - | |
487 | firstFreq) % step) != 0) { | |
488 | n++; | |
489 | } | |
490 | minFreq = firstFreq + n * step; | |
491 | } | |
492 | ||
493 | if (lastFreq <= maxTunerFreq) { | |
494 | maxFreq = lastFreq; | |
495 | } else { | |
496 | DRXFrequency_t n = 0; | |
497 | ||
498 | n = (lastFreq - maxTunerFreq) / step; | |
499 | if (((lastFreq - | |
500 | maxTunerFreq) % step) != 0) { | |
501 | n++; | |
502 | } | |
503 | maxFreq = lastFreq - n * step; | |
504 | } | |
505 | } | |
506 | } | |
507 | ||
508 | /* Keep track of total number of channels within tuner range | |
509 | in this frequency plan. */ | |
510 | if ((minFreq != 0) && (maxFreq != 0)) { | |
511 | nrChannelsInPlan += | |
43a431e4 | 512 | (u16) (((maxFreq - minFreq) / step) + 1); |
443f18d0 MCC |
513 | |
514 | /* Determine first frequency (within tuner range) to scan */ | |
515 | if (commonAttr->scanNextFrequency == 0) { | |
516 | commonAttr->scanNextFrequency = minFreq; | |
517 | commonAttr->scanFreqPlanIndex = i; | |
518 | } | |
519 | } | |
520 | ||
521 | } /* for ( ... ) */ | |
522 | ||
523 | if (nrChannelsInPlan == 0) { | |
524 | /* Tuner range and frequency plan ranges do not overlap */ | |
525 | commonAttr->scanActive = FALSE; | |
526 | return DRX_STS_ERROR; | |
527 | } | |
528 | ||
529 | /* Store parameters */ | |
530 | commonAttr->scanReady = FALSE; | |
531 | commonAttr->scanMaxChannels = nrChannelsInPlan; | |
532 | commonAttr->scanChannelsScanned = 0; | |
533 | commonAttr->scanParam = scanParam; /* SCAN_NEXT is now allowed */ | |
534 | ||
535 | scanContext = GetScanContext(demod, scanContext); | |
536 | ||
537 | status = (*(GetScanFunction(demod))) | |
538 | (scanContext, DRX_SCAN_COMMAND_INIT, NULL, NULL); | |
539 | ||
540 | commonAttr->scanActive = FALSE; | |
541 | ||
542 | return DRX_STS_OK; | |
38b2df95 DH |
543 | } |
544 | ||
545 | /*============================================================================*/ | |
546 | ||
547 | /** | |
548 | * \brief Stop scanning. | |
549 | * \param demod: Pointer to demodulator instance. | |
550 | * \return DRXStatus_t. | |
551 | * \retval DRX_STS_OK: Scan stopped. | |
552 | * \retval DRX_STS_ERROR: Something went wrong. | |
553 | * \retval DRX_STS_INVALID_ARG: Wrong parameters. | |
554 | */ | |
443f18d0 | 555 | static DRXStatus_t CtrlScanStop(pDRXDemodInstance_t demod) |
38b2df95 | 556 | { |
443f18d0 MCC |
557 | DRXStatus_t status = DRX_STS_ERROR; |
558 | pDRXCommonAttr_t commonAttr = (pDRXCommonAttr_t) (NULL); | |
559 | void *scanContext = NULL; | |
38b2df95 | 560 | |
443f18d0 MCC |
561 | commonAttr = (pDRXCommonAttr_t) demod->myCommonAttr; |
562 | commonAttr->scanActive = TRUE; | |
38b2df95 | 563 | |
443f18d0 MCC |
564 | if ((commonAttr->scanParam == NULL) || |
565 | (commonAttr->scanMaxChannels == 0)) { | |
566 | /* Scan was not running, just return OK */ | |
567 | commonAttr->scanActive = FALSE; | |
568 | return DRX_STS_OK; | |
569 | } | |
38b2df95 | 570 | |
443f18d0 MCC |
571 | /* Call default or device-specific scanning stop function */ |
572 | scanContext = GetScanContext(demod, scanContext); | |
38b2df95 | 573 | |
443f18d0 MCC |
574 | status = (*(GetScanFunction(demod))) |
575 | (scanContext, DRX_SCAN_COMMAND_STOP, NULL, NULL); | |
38b2df95 | 576 | |
443f18d0 MCC |
577 | /* All done, invalidate scan-init */ |
578 | commonAttr->scanParam = NULL; | |
579 | commonAttr->scanMaxChannels = 0; | |
580 | commonAttr->scanActive = FALSE; | |
38b2df95 | 581 | |
443f18d0 | 582 | return status; |
38b2df95 DH |
583 | } |
584 | ||
585 | /*============================================================================*/ | |
586 | ||
587 | /** | |
588 | * \brief Scan for next channel. | |
589 | * \param demod: Pointer to demodulator instance. | |
590 | * \param scanProgress: Pointer to scan progress. | |
591 | * \return DRXStatus_t. | |
592 | * \retval DRX_STS_OK: Channel found, DRX_CTRL_GET_CHANNEL can be used | |
593 | * to retrieve channel parameters. | |
594 | * \retval DRX_STS_BUSY: Tried part of the channels, as specified in | |
595 | * numTries field of scan parameters. At least one | |
596 | * more call to DRX_CTRL_SCAN_NEXT is needed to | |
597 | * complete scanning. | |
598 | * \retval DRX_STS_READY: Reached end of scan range. | |
599 | * \retval DRX_STS_ERROR: Something went wrong. | |
600 | * \retval DRX_STS_INVALID_ARG: Wrong parameters. The scanProgress may be NULL. | |
601 | * | |
602 | * Progress indication will run from 0 upto DRX_SCAN_MAX_PROGRESS during scan. | |
603 | * | |
604 | */ | |
43a431e4 | 605 | static DRXStatus_t CtrlScanNext(pDRXDemodInstance_t demod, u16 *scanProgress) |
38b2df95 | 606 | { |
443f18d0 MCC |
607 | pDRXCommonAttr_t commonAttr = (pDRXCommonAttr_t) (NULL); |
608 | pBool_t scanReady = (pBool_t) (NULL); | |
43a431e4 MCC |
609 | u16 maxProgress = DRX_SCAN_MAX_PROGRESS; |
610 | u32 numTries = 0; | |
611 | u32 i = 0; | |
443f18d0 MCC |
612 | |
613 | commonAttr = (pDRXCommonAttr_t) demod->myCommonAttr; | |
614 | ||
615 | /* Check scan parameters */ | |
616 | if (scanProgress == NULL) { | |
617 | commonAttr->scanActive = FALSE; | |
618 | return DRX_STS_INVALID_ARG; | |
619 | } | |
620 | ||
621 | *scanProgress = 0; | |
622 | commonAttr->scanActive = TRUE; | |
623 | if ((commonAttr->scanParam == NULL) || | |
624 | (commonAttr->scanMaxChannels == 0)) { | |
625 | /* CtrlScanInit() was not called succesfully before CtrlScanNext() */ | |
626 | commonAttr->scanActive = FALSE; | |
627 | return DRX_STS_ERROR; | |
628 | } | |
629 | ||
43a431e4 MCC |
630 | *scanProgress = (u16) (((commonAttr->scanChannelsScanned) * |
631 | ((u32) (maxProgress))) / | |
443f18d0 MCC |
632 | (commonAttr->scanMaxChannels)); |
633 | ||
634 | /* Scan */ | |
635 | numTries = commonAttr->scanParam->numTries; | |
636 | scanReady = &(commonAttr->scanReady); | |
637 | ||
638 | for (i = 0; ((i < numTries) && ((*scanReady) == FALSE)); i++) { | |
639 | DRXChannel_t scanChannel = { 0 }; | |
640 | DRXStatus_t status = DRX_STS_ERROR; | |
641 | pDRXFrequencyPlan_t freqPlan = (pDRXFrequencyPlan_t) (NULL); | |
642 | Bool_t nextChannel = FALSE; | |
643 | void *scanContext = NULL; | |
644 | ||
645 | /* Next channel to scan */ | |
646 | freqPlan = | |
647 | &(commonAttr->scanParam-> | |
648 | frequencyPlan[commonAttr->scanFreqPlanIndex]); | |
649 | scanChannel.frequency = commonAttr->scanNextFrequency; | |
650 | scanChannel.bandwidth = freqPlan->bandwidth; | |
651 | scanChannel.mirror = DRX_MIRROR_AUTO; | |
652 | scanChannel.constellation = DRX_CONSTELLATION_AUTO; | |
653 | scanChannel.hierarchy = DRX_HIERARCHY_AUTO; | |
654 | scanChannel.priority = DRX_PRIORITY_HIGH; | |
655 | scanChannel.coderate = DRX_CODERATE_AUTO; | |
656 | scanChannel.guard = DRX_GUARD_AUTO; | |
657 | scanChannel.fftmode = DRX_FFTMODE_AUTO; | |
658 | scanChannel.classification = DRX_CLASSIFICATION_AUTO; | |
659 | scanChannel.symbolrate = 0; | |
660 | scanChannel.interleavemode = DRX_INTERLEAVEMODE_AUTO; | |
661 | scanChannel.ldpc = DRX_LDPC_AUTO; | |
662 | scanChannel.carrier = DRX_CARRIER_AUTO; | |
663 | scanChannel.framemode = DRX_FRAMEMODE_AUTO; | |
664 | scanChannel.pilot = DRX_PILOT_AUTO; | |
665 | ||
666 | /* Call default or device-specific scanning function */ | |
667 | scanContext = GetScanContext(demod, scanContext); | |
668 | ||
669 | status = (*(GetScanFunction(demod))) | |
670 | (scanContext, DRX_SCAN_COMMAND_NEXT, &scanChannel, | |
671 | &nextChannel); | |
672 | ||
673 | /* Proceed to next channel if requested */ | |
674 | if (nextChannel == TRUE) { | |
675 | DRXStatus_t nextStatus = DRX_STS_ERROR; | |
676 | DRXFrequency_t skip = 0; | |
677 | ||
678 | if (status == DRX_STS_OK) { | |
679 | /* a channel was found, so skip some frequency steps */ | |
680 | skip = commonAttr->scanParam->skip; | |
681 | } | |
682 | nextStatus = ScanPrepareNextScan(demod, skip); | |
683 | ||
684 | /* keep track of progress */ | |
685 | *scanProgress = | |
43a431e4 MCC |
686 | (u16) (((commonAttr->scanChannelsScanned) * |
687 | ((u32) (maxProgress))) / | |
443f18d0 MCC |
688 | (commonAttr->scanMaxChannels)); |
689 | ||
690 | if (nextStatus != DRX_STS_OK) { | |
691 | commonAttr->scanActive = FALSE; | |
692 | return (nextStatus); | |
693 | } | |
694 | } | |
695 | if (status != DRX_STS_BUSY) { | |
696 | /* channel found or error */ | |
697 | commonAttr->scanActive = FALSE; | |
698 | return status; | |
699 | } | |
700 | } /* for ( i = 0; i < ( ... numTries); i++) */ | |
701 | ||
702 | if ((*scanReady) == TRUE) { | |
703 | /* End of scan reached: call stop-scan, ignore any error */ | |
704 | CtrlScanStop(demod); | |
705 | commonAttr->scanActive = FALSE; | |
706 | return (DRX_STS_READY); | |
707 | } | |
708 | ||
709 | commonAttr->scanActive = FALSE; | |
710 | ||
711 | return DRX_STS_BUSY; | |
38b2df95 DH |
712 | } |
713 | ||
714 | #endif /* #ifndef DRX_EXCLUDE_SCAN */ | |
715 | ||
716 | /*============================================================================*/ | |
717 | ||
718 | /** | |
719 | * \brief Program tuner. | |
720 | * \param demod: Pointer to demodulator instance. | |
721 | * \param tunerChannel: Pointer to tuning parameters. | |
722 | * \return DRXStatus_t. | |
723 | * \retval DRX_STS_OK: Tuner programmed successfully. | |
724 | * \retval DRX_STS_ERROR: Something went wrong. | |
725 | * \retval DRX_STS_INVALID_ARG: Wrong parameters. | |
726 | * | |
727 | * tunerChannel passes parameters to program the tuner, | |
728 | * but also returns the actual RF and IF frequency from the tuner. | |
729 | * | |
730 | */ | |
731 | static DRXStatus_t | |
443f18d0 | 732 | CtrlProgramTuner(pDRXDemodInstance_t demod, pDRXChannel_t channel) |
38b2df95 | 733 | { |
443f18d0 MCC |
734 | pDRXCommonAttr_t commonAttr = (pDRXCommonAttr_t) (NULL); |
735 | DRXStandard_t standard = DRX_STANDARD_UNKNOWN; | |
736 | TUNERMode_t tunerMode = 0; | |
737 | DRXStatus_t status = DRX_STS_ERROR; | |
738 | DRXFrequency_t ifFrequency = 0; | |
739 | Bool_t tunerSlowMode = FALSE; | |
740 | ||
741 | /* can't tune without a tuner */ | |
742 | if (demod->myTuner == NULL) { | |
743 | return DRX_STS_INVALID_ARG; | |
744 | } | |
745 | ||
746 | commonAttr = (pDRXCommonAttr_t) demod->myCommonAttr; | |
747 | ||
748 | /* select analog or digital tuner mode based on current standard */ | |
749 | if (DRX_Ctrl(demod, DRX_CTRL_GET_STANDARD, &standard) != DRX_STS_OK) { | |
750 | return DRX_STS_ERROR; | |
751 | } | |
752 | ||
753 | if (DRX_ISATVSTD(standard)) { | |
754 | tunerMode |= TUNER_MODE_ANALOG; | |
755 | } else { /* note: also for unknown standard */ | |
756 | ||
757 | tunerMode |= TUNER_MODE_DIGITAL; | |
758 | } | |
759 | ||
760 | /* select tuner bandwidth */ | |
761 | switch (channel->bandwidth) { | |
762 | case DRX_BANDWIDTH_6MHZ: | |
763 | tunerMode |= TUNER_MODE_6MHZ; | |
764 | break; | |
765 | case DRX_BANDWIDTH_7MHZ: | |
766 | tunerMode |= TUNER_MODE_7MHZ; | |
767 | break; | |
768 | case DRX_BANDWIDTH_8MHZ: | |
769 | tunerMode |= TUNER_MODE_8MHZ; | |
770 | break; | |
771 | default: /* note: also for unknown bandwidth */ | |
772 | return DRX_STS_INVALID_ARG; | |
773 | } | |
774 | ||
775 | DRX_GET_TUNERSLOWMODE(demod, tunerSlowMode); | |
776 | ||
777 | /* select fast (switch) or slow (lock) tuner mode */ | |
778 | if (tunerSlowMode) { | |
779 | tunerMode |= TUNER_MODE_LOCK; | |
780 | } else { | |
781 | tunerMode |= TUNER_MODE_SWITCH; | |
782 | } | |
783 | ||
784 | if (commonAttr->tunerPortNr == 1) { | |
785 | Bool_t bridgeClosed = TRUE; | |
786 | DRXStatus_t statusBridge = DRX_STS_ERROR; | |
787 | ||
788 | statusBridge = | |
789 | DRX_Ctrl(demod, DRX_CTRL_I2C_BRIDGE, &bridgeClosed); | |
790 | if (statusBridge != DRX_STS_OK) { | |
791 | return statusBridge; | |
792 | } | |
793 | } | |
794 | ||
795 | status = DRXBSP_TUNER_SetFrequency(demod->myTuner, | |
796 | tunerMode, channel->frequency); | |
797 | ||
798 | /* attempt restoring bridge before checking status of SetFrequency */ | |
799 | if (commonAttr->tunerPortNr == 1) { | |
800 | Bool_t bridgeClosed = FALSE; | |
801 | DRXStatus_t statusBridge = DRX_STS_ERROR; | |
802 | ||
803 | statusBridge = | |
804 | DRX_Ctrl(demod, DRX_CTRL_I2C_BRIDGE, &bridgeClosed); | |
805 | if (statusBridge != DRX_STS_OK) { | |
806 | return statusBridge; | |
807 | } | |
808 | } | |
809 | ||
810 | /* now check status of DRXBSP_TUNER_SetFrequency */ | |
811 | if (status != DRX_STS_OK) { | |
812 | return status; | |
813 | } | |
814 | ||
815 | /* get actual RF and IF frequencies from tuner */ | |
816 | status = DRXBSP_TUNER_GetFrequency(demod->myTuner, | |
817 | tunerMode, | |
818 | &(channel->frequency), | |
819 | &(ifFrequency)); | |
820 | if (status != DRX_STS_OK) { | |
821 | return status; | |
822 | } | |
823 | ||
824 | /* update common attributes with information available from this function; | |
825 | TODO: check if this is required and safe */ | |
826 | DRX_SET_INTERMEDIATEFREQ(demod, ifFrequency); | |
827 | ||
828 | return DRX_STS_OK; | |
38b2df95 DH |
829 | } |
830 | ||
831 | /*============================================================================*/ | |
832 | ||
833 | /** | |
834 | * \brief function to do a register dump. | |
835 | * \param demod: Pointer to demodulator instance. | |
836 | * \param registers: Registers to dump. | |
837 | * \return DRXStatus_t. | |
838 | * \retval DRX_STS_OK: Dump executed successfully. | |
839 | * \retval DRX_STS_ERROR: Something went wrong. | |
840 | * \retval DRX_STS_INVALID_ARG: Wrong parameters. | |
841 | * | |
842 | */ | |
443f18d0 MCC |
843 | DRXStatus_t CtrlDumpRegisters(pDRXDemodInstance_t demod, |
844 | pDRXRegDump_t registers) | |
38b2df95 | 845 | { |
43a431e4 | 846 | u16 i = 0; |
443f18d0 MCC |
847 | |
848 | if (registers == NULL) { | |
849 | /* registers not supplied */ | |
850 | return DRX_STS_INVALID_ARG; | |
851 | } | |
852 | ||
853 | /* start dumping registers */ | |
854 | while (registers[i].address != 0) { | |
855 | DRXStatus_t status = DRX_STS_ERROR; | |
43a431e4 MCC |
856 | u16 value = 0; |
857 | u32 data = 0; | |
443f18d0 MCC |
858 | |
859 | status = | |
860 | demod->myAccessFunct->readReg16Func(demod->myI2CDevAddr, | |
861 | registers[i].address, | |
862 | &value, 0); | |
863 | ||
43a431e4 | 864 | data = (u32) value; |
443f18d0 MCC |
865 | |
866 | if (status != DRX_STS_OK) { | |
867 | /* no breakouts; | |
868 | depending on device ID, some HW blocks might not be available */ | |
43a431e4 | 869 | data |= ((u32) status) << 16; |
443f18d0 MCC |
870 | } |
871 | registers[i].data = data; | |
872 | i++; | |
873 | } | |
874 | ||
875 | /* all done, all OK (any errors are saved inside data) */ | |
876 | return DRX_STS_OK; | |
38b2df95 DH |
877 | } |
878 | ||
879 | /*============================================================================*/ | |
880 | /*============================================================================*/ | |
881 | /*===Microcode related functions==============================================*/ | |
882 | /*============================================================================*/ | |
883 | /*============================================================================*/ | |
884 | ||
885 | /** | |
886 | * \brief Read a 16 bits word, expects big endian data. | |
887 | * \param addr: Pointer to memory from which to read the 16 bits word. | |
43a431e4 | 888 | * \return u16 The data read. |
38b2df95 DH |
889 | * |
890 | * This function takes care of the possible difference in endianness between the | |
891 | * host and the data contained in the microcode image file. | |
892 | * | |
893 | */ | |
43a431e4 | 894 | static u16 UCodeRead16(u8 *addr) |
38b2df95 | 895 | { |
443f18d0 | 896 | /* Works fo any host processor */ |
38b2df95 | 897 | |
43a431e4 | 898 | u16 word = 0; |
38b2df95 | 899 | |
43a431e4 | 900 | word = ((u16) addr[0]); |
443f18d0 | 901 | word <<= 8; |
43a431e4 | 902 | word |= ((u16) addr[1]); |
38b2df95 | 903 | |
443f18d0 | 904 | return word; |
38b2df95 DH |
905 | } |
906 | ||
907 | /*============================================================================*/ | |
908 | ||
909 | /** | |
910 | * \brief Read a 32 bits word, expects big endian data. | |
911 | * \param addr: Pointer to memory from which to read the 32 bits word. | |
43a431e4 | 912 | * \return u32 The data read. |
38b2df95 DH |
913 | * |
914 | * This function takes care of the possible difference in endianness between the | |
915 | * host and the data contained in the microcode image file. | |
916 | * | |
917 | */ | |
43a431e4 | 918 | static u32 UCodeRead32(u8 *addr) |
38b2df95 | 919 | { |
443f18d0 | 920 | /* Works fo any host processor */ |
38b2df95 | 921 | |
43a431e4 | 922 | u32 word = 0; |
38b2df95 | 923 | |
43a431e4 | 924 | word = ((u16) addr[0]); |
443f18d0 | 925 | word <<= 8; |
43a431e4 | 926 | word |= ((u16) addr[1]); |
443f18d0 | 927 | word <<= 8; |
43a431e4 | 928 | word |= ((u16) addr[2]); |
443f18d0 | 929 | word <<= 8; |
43a431e4 | 930 | word |= ((u16) addr[3]); |
38b2df95 | 931 | |
443f18d0 | 932 | return word; |
38b2df95 DH |
933 | } |
934 | ||
935 | /*============================================================================*/ | |
936 | ||
937 | /** | |
938 | * \brief Compute CRC of block of microcode data. | |
939 | * \param blockData: Pointer to microcode data. | |
940 | * \param nrWords: Size of microcode block (number of 16 bits words). | |
43a431e4 | 941 | * \return u16 The computed CRC residu. |
38b2df95 | 942 | */ |
43a431e4 | 943 | static u16 UCodeComputeCRC(u8 *blockData, u16 nrWords) |
38b2df95 | 944 | { |
43a431e4 MCC |
945 | u16 i = 0; |
946 | u16 j = 0; | |
947 | u32 CRCWord = 0; | |
948 | u32 carry = 0; | |
443f18d0 MCC |
949 | |
950 | while (i < nrWords) { | |
43a431e4 | 951 | CRCWord |= (u32) UCodeRead16(blockData); |
443f18d0 MCC |
952 | for (j = 0; j < 16; j++) { |
953 | CRCWord <<= 1; | |
954 | if (carry != 0) { | |
955 | CRCWord ^= 0x80050000UL; | |
956 | } | |
957 | carry = CRCWord & 0x80000000UL; | |
958 | } | |
959 | i++; | |
43a431e4 | 960 | blockData += (sizeof(u16)); |
443f18d0 | 961 | } |
43a431e4 | 962 | return ((u16) (CRCWord >> 16)); |
38b2df95 DH |
963 | } |
964 | ||
965 | /*============================================================================*/ | |
966 | ||
967 | /** | |
968 | * \brief Handle microcode upload or verify. | |
969 | * \param devAddr: Address of device. | |
970 | * \param mcInfo: Pointer to information about microcode data. | |
971 | * \param action: Either UCODE_UPLOAD or UCODE_VERIFY | |
972 | * \return DRXStatus_t. | |
973 | * \retval DRX_STS_OK: | |
974 | * - In case of UCODE_UPLOAD: code is successfully uploaded. | |
975 | * - In case of UCODE_VERIFY: image on device is equal to | |
976 | * image provided to this control function. | |
977 | * \retval DRX_STS_ERROR: | |
978 | * - In case of UCODE_UPLOAD: I2C error. | |
979 | * - In case of UCODE_VERIFY: I2C error or image on device | |
980 | * is not equal to image provided to this control function. | |
981 | * \retval DRX_STS_INVALID_ARG: | |
982 | * - Invalid arguments. | |
983 | * - Provided image is corrupt | |
984 | */ | |
985 | static DRXStatus_t | |
443f18d0 MCC |
986 | CtrlUCode(pDRXDemodInstance_t demod, |
987 | pDRXUCodeInfo_t mcInfo, DRXUCodeAction_t action) | |
38b2df95 | 988 | { |
443f18d0 | 989 | DRXStatus_t rc; |
43a431e4 MCC |
990 | u16 i = 0; |
991 | u16 mcNrOfBlks = 0; | |
992 | u16 mcMagicWord = 0; | |
993 | u8 *mcData = (u8 *) (NULL); | |
5b223b39 | 994 | struct i2c_device_addr *devAddr = (struct i2c_device_addr *) (NULL); |
443f18d0 MCC |
995 | |
996 | devAddr = demod->myI2CDevAddr; | |
997 | ||
998 | /* Check arguments */ | |
999 | if ((mcInfo == NULL) || (mcInfo->mcData == NULL)) { | |
1000 | return DRX_STS_INVALID_ARG; | |
1001 | } | |
1002 | ||
1003 | mcData = mcInfo->mcData; | |
1004 | ||
1005 | /* Check data */ | |
1006 | mcMagicWord = UCodeRead16(mcData); | |
43a431e4 | 1007 | mcData += sizeof(u16); |
443f18d0 | 1008 | mcNrOfBlks = UCodeRead16(mcData); |
43a431e4 | 1009 | mcData += sizeof(u16); |
443f18d0 MCC |
1010 | |
1011 | if ((mcMagicWord != DRX_UCODE_MAGIC_WORD) || (mcNrOfBlks == 0)) { | |
1012 | /* wrong endianess or wrong data ? */ | |
1013 | return DRX_STS_INVALID_ARG; | |
1014 | } | |
1015 | ||
1016 | /* Scan microcode blocks first for version info if uploading */ | |
1017 | if (action == UCODE_UPLOAD) { | |
1018 | /* Clear version block */ | |
1019 | DRX_SET_MCVERTYPE(demod, 0); | |
1020 | DRX_SET_MCDEV(demod, 0); | |
1021 | DRX_SET_MCVERSION(demod, 0); | |
1022 | DRX_SET_MCPATCH(demod, 0); | |
1023 | for (i = 0; i < mcNrOfBlks; i++) { | |
1024 | DRXUCodeBlockHdr_t blockHdr; | |
1025 | ||
1026 | /* Process block header */ | |
1027 | blockHdr.addr = UCodeRead32(mcData); | |
43a431e4 | 1028 | mcData += sizeof(u32); |
443f18d0 | 1029 | blockHdr.size = UCodeRead16(mcData); |
43a431e4 | 1030 | mcData += sizeof(u16); |
443f18d0 | 1031 | blockHdr.flags = UCodeRead16(mcData); |
43a431e4 | 1032 | mcData += sizeof(u16); |
443f18d0 | 1033 | blockHdr.CRC = UCodeRead16(mcData); |
43a431e4 | 1034 | mcData += sizeof(u16); |
443f18d0 MCC |
1035 | |
1036 | if (blockHdr.flags & 0x8) { | |
1037 | /* Aux block. Check type */ | |
43a431e4 MCC |
1038 | u8 *auxblk = mcInfo->mcData + blockHdr.addr; |
1039 | u16 auxtype = UCodeRead16(auxblk); | |
443f18d0 MCC |
1040 | if (DRX_ISMCVERTYPE(auxtype)) { |
1041 | DRX_SET_MCVERTYPE(demod, | |
1042 | UCodeRead16(auxblk)); | |
43a431e4 | 1043 | auxblk += sizeof(u16); |
443f18d0 MCC |
1044 | DRX_SET_MCDEV(demod, |
1045 | UCodeRead32(auxblk)); | |
43a431e4 | 1046 | auxblk += sizeof(u32); |
443f18d0 MCC |
1047 | DRX_SET_MCVERSION(demod, |
1048 | UCodeRead32(auxblk)); | |
43a431e4 | 1049 | auxblk += sizeof(u32); |
443f18d0 MCC |
1050 | DRX_SET_MCPATCH(demod, |
1051 | UCodeRead32(auxblk)); | |
1052 | } | |
1053 | } | |
1054 | ||
1055 | /* Next block */ | |
43a431e4 | 1056 | mcData += blockHdr.size * sizeof(u16); |
443f18d0 MCC |
1057 | } |
1058 | ||
1059 | /* After scanning, validate the microcode. | |
1060 | It is also valid if no validation control exists. | |
1061 | */ | |
1062 | rc = DRX_Ctrl(demod, DRX_CTRL_VALIDATE_UCODE, NULL); | |
1063 | if (rc != DRX_STS_OK && rc != DRX_STS_FUNC_NOT_AVAILABLE) { | |
1064 | return rc; | |
1065 | } | |
1066 | ||
1067 | /* Restore data pointer */ | |
43a431e4 | 1068 | mcData = mcInfo->mcData + 2 * sizeof(u16); |
443f18d0 MCC |
1069 | } |
1070 | ||
1071 | /* Process microcode blocks */ | |
1072 | for (i = 0; i < mcNrOfBlks; i++) { | |
1073 | DRXUCodeBlockHdr_t blockHdr; | |
43a431e4 | 1074 | u16 mcBlockNrBytes = 0; |
443f18d0 MCC |
1075 | |
1076 | /* Process block header */ | |
1077 | blockHdr.addr = UCodeRead32(mcData); | |
43a431e4 | 1078 | mcData += sizeof(u32); |
443f18d0 | 1079 | blockHdr.size = UCodeRead16(mcData); |
43a431e4 | 1080 | mcData += sizeof(u16); |
443f18d0 | 1081 | blockHdr.flags = UCodeRead16(mcData); |
43a431e4 | 1082 | mcData += sizeof(u16); |
443f18d0 | 1083 | blockHdr.CRC = UCodeRead16(mcData); |
43a431e4 | 1084 | mcData += sizeof(u16); |
443f18d0 MCC |
1085 | |
1086 | /* Check block header on: | |
1087 | - data larger than 64Kb | |
1088 | - if CRC enabled check CRC | |
1089 | */ | |
1090 | if ((blockHdr.size > 0x7FFF) || | |
1091 | (((blockHdr.flags & DRX_UCODE_CRC_FLAG) != 0) && | |
1092 | (blockHdr.CRC != UCodeComputeCRC(mcData, blockHdr.size))) | |
1093 | ) { | |
1094 | /* Wrong data ! */ | |
1095 | return DRX_STS_INVALID_ARG; | |
1096 | } | |
1097 | ||
43a431e4 | 1098 | mcBlockNrBytes = blockHdr.size * ((u16) sizeof(u16)); |
443f18d0 MCC |
1099 | |
1100 | if (blockHdr.size != 0) { | |
1101 | /* Perform the desired action */ | |
1102 | switch (action) { | |
38b2df95 | 1103 | /*================================================================*/ |
443f18d0 MCC |
1104 | case UCODE_UPLOAD: |
1105 | { | |
1106 | /* Upload microcode */ | |
1107 | if (demod->myAccessFunct-> | |
1108 | writeBlockFunc(devAddr, | |
1109 | (DRXaddr_t) blockHdr. | |
1110 | addr, mcBlockNrBytes, | |
1111 | mcData, | |
1112 | 0x0000) != | |
1113 | DRX_STS_OK) { | |
1114 | return (DRX_STS_ERROR); | |
1115 | } /* if */ | |
1116 | }; | |
1117 | break; | |
38b2df95 DH |
1118 | |
1119 | /*================================================================*/ | |
443f18d0 MCC |
1120 | case UCODE_VERIFY: |
1121 | { | |
1122 | int result = 0; | |
43a431e4 | 1123 | u8 mcDataBuffer |
443f18d0 | 1124 | [DRX_UCODE_MAX_BUF_SIZE]; |
43a431e4 MCC |
1125 | u32 bytesToCompare = 0; |
1126 | u32 bytesLeftToCompare = 0; | |
443f18d0 | 1127 | DRXaddr_t currAddr = (DRXaddr_t) 0; |
43a431e4 | 1128 | u8 *currPtr = NULL; |
443f18d0 MCC |
1129 | |
1130 | bytesLeftToCompare = mcBlockNrBytes; | |
1131 | currAddr = blockHdr.addr; | |
1132 | currPtr = mcData; | |
1133 | ||
1134 | while (bytesLeftToCompare != 0) { | |
1135 | if (bytesLeftToCompare > | |
43a431e4 | 1136 | ((u32) |
443f18d0 MCC |
1137 | DRX_UCODE_MAX_BUF_SIZE)) { |
1138 | bytesToCompare = | |
43a431e4 | 1139 | ((u32) |
443f18d0 MCC |
1140 | DRX_UCODE_MAX_BUF_SIZE); |
1141 | } else { | |
1142 | bytesToCompare = | |
1143 | bytesLeftToCompare; | |
1144 | } | |
1145 | ||
1146 | if (demod->myAccessFunct-> | |
1147 | readBlockFunc(devAddr, | |
1148 | currAddr, | |
43a431e4 | 1149 | (u16) |
443f18d0 | 1150 | bytesToCompare, |
43a431e4 | 1151 | (u8 *) |
443f18d0 MCC |
1152 | mcDataBuffer, |
1153 | 0x0000) != | |
1154 | DRX_STS_OK) { | |
1155 | return (DRX_STS_ERROR); | |
1156 | } | |
1157 | ||
1158 | result = | |
1159 | DRXBSP_HST_Memcmp(currPtr, | |
1160 | mcDataBuffer, | |
1161 | bytesToCompare); | |
1162 | ||
1163 | if (result != 0) { | |
1164 | return DRX_STS_ERROR; | |
1165 | } | |
1166 | ||
1167 | currAddr += | |
1168 | ((DRXaddr_t) | |
1169 | (bytesToCompare / 2)); | |
1170 | currPtr = | |
1171 | &(currPtr[bytesToCompare]); | |
1172 | bytesLeftToCompare -= | |
43a431e4 | 1173 | ((u32) bytesToCompare); |
443f18d0 MCC |
1174 | } /* while( bytesToCompare > DRX_UCODE_MAX_BUF_SIZE ) */ |
1175 | }; | |
1176 | break; | |
38b2df95 DH |
1177 | |
1178 | /*================================================================*/ | |
443f18d0 MCC |
1179 | default: |
1180 | return DRX_STS_INVALID_ARG; | |
1181 | break; | |
38b2df95 | 1182 | |
443f18d0 MCC |
1183 | } /* switch ( action ) */ |
1184 | } | |
38b2df95 | 1185 | |
443f18d0 MCC |
1186 | /* if (blockHdr.size != 0 ) */ |
1187 | /* Next block */ | |
1188 | mcData += mcBlockNrBytes; | |
38b2df95 | 1189 | |
443f18d0 | 1190 | } /* for( i = 0 ; i<mcNrOfBlks ; i++ ) */ |
38b2df95 | 1191 | |
443f18d0 | 1192 | return DRX_STS_OK; |
38b2df95 DH |
1193 | } |
1194 | ||
1195 | /*============================================================================*/ | |
1196 | ||
1197 | /** | |
1198 | * \brief Build list of version information. | |
1199 | * \param demod: A pointer to a demodulator instance. | |
1200 | * \param versionList: Pointer to linked list of versions. | |
1201 | * \return DRXStatus_t. | |
1202 | * \retval DRX_STS_OK: Version information stored in versionList | |
1203 | * \retval DRX_STS_INVALID_ARG: Invalid arguments. | |
1204 | */ | |
1205 | static DRXStatus_t | |
443f18d0 | 1206 | CtrlVersion(pDRXDemodInstance_t demod, pDRXVersionList_t * versionList) |
38b2df95 | 1207 | { |
443f18d0 MCC |
1208 | static char drxDriverCoreModuleName[] = "Core driver"; |
1209 | static char drxDriverCoreVersionText[] = | |
1210 | DRX_VERSIONSTRING(VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH); | |
1211 | ||
1212 | static DRXVersion_t drxDriverCoreVersion; | |
1213 | static DRXVersionList_t drxDriverCoreVersionList; | |
1214 | ||
1215 | pDRXVersionList_t demodVersionList = (pDRXVersionList_t) (NULL); | |
1216 | DRXStatus_t returnStatus = DRX_STS_ERROR; | |
1217 | ||
1218 | /* Check arguments */ | |
1219 | if (versionList == NULL) { | |
1220 | return DRX_STS_INVALID_ARG; | |
1221 | } | |
1222 | ||
1223 | /* Get version info list from demod */ | |
1224 | returnStatus = (*(demod->myDemodFunct->ctrlFunc)) (demod, | |
1225 | DRX_CTRL_VERSION, | |
1226 | (void *) | |
1227 | &demodVersionList); | |
1228 | ||
1229 | /* Always fill in the information of the driver SW . */ | |
1230 | drxDriverCoreVersion.moduleType = DRX_MODULE_DRIVERCORE; | |
1231 | drxDriverCoreVersion.moduleName = drxDriverCoreModuleName; | |
1232 | drxDriverCoreVersion.vMajor = VERSION_MAJOR; | |
1233 | drxDriverCoreVersion.vMinor = VERSION_MINOR; | |
1234 | drxDriverCoreVersion.vPatch = VERSION_PATCH; | |
1235 | drxDriverCoreVersion.vString = drxDriverCoreVersionText; | |
1236 | ||
1237 | drxDriverCoreVersionList.version = &drxDriverCoreVersion; | |
1238 | drxDriverCoreVersionList.next = (pDRXVersionList_t) (NULL); | |
1239 | ||
1240 | if ((returnStatus == DRX_STS_OK) && (demodVersionList != NULL)) { | |
1241 | /* Append versioninfo from driver to versioninfo from demod */ | |
1242 | /* Return version info in "bottom-up" order. This way, multiple | |
1243 | devices can be handled without using malloc. */ | |
1244 | pDRXVersionList_t currentListElement = demodVersionList; | |
1245 | while (currentListElement->next != NULL) { | |
1246 | currentListElement = currentListElement->next; | |
1247 | } | |
1248 | currentListElement->next = &drxDriverCoreVersionList; | |
1249 | ||
1250 | *versionList = demodVersionList; | |
1251 | } else { | |
1252 | /* Just return versioninfo from driver */ | |
1253 | *versionList = &drxDriverCoreVersionList; | |
1254 | } | |
1255 | ||
1256 | return DRX_STS_OK; | |
38b2df95 DH |
1257 | } |
1258 | ||
1259 | /*============================================================================*/ | |
1260 | /*============================================================================*/ | |
1261 | /*== Exported functions ======================================================*/ | |
1262 | /*============================================================================*/ | |
1263 | /*============================================================================*/ | |
1264 | ||
38b2df95 DH |
1265 | /** |
1266 | * \brief This function is obsolete. | |
1267 | * \param demods: Don't care, parameter is ignored. | |
1268 | * \return DRXStatus_t Return status. | |
1269 | * \retval DRX_STS_OK: Initialization completed. | |
1270 | * | |
1271 | * This function is obsolete, prototype available for backward compatability. | |
1272 | * | |
1273 | */ | |
1274 | ||
443f18d0 | 1275 | DRXStatus_t DRX_Init(pDRXDemodInstance_t demods[]) |
38b2df95 | 1276 | { |
443f18d0 | 1277 | return DRX_STS_OK; |
38b2df95 DH |
1278 | } |
1279 | ||
1280 | /*============================================================================*/ | |
1281 | ||
1282 | /** | |
1283 | * \brief This function is obsolete. | |
1284 | * \return DRXStatus_t Return status. | |
1285 | * \retval DRX_STS_OK: Terminated driver successful. | |
1286 | * | |
1287 | * This function is obsolete, prototype available for backward compatability. | |
1288 | * | |
1289 | */ | |
1290 | ||
443f18d0 | 1291 | DRXStatus_t DRX_Term(void) |
38b2df95 | 1292 | { |
443f18d0 | 1293 | return DRX_STS_OK; |
38b2df95 DH |
1294 | } |
1295 | ||
1296 | /*============================================================================*/ | |
1297 | ||
1298 | /** | |
1299 | * \brief Open a demodulator instance. | |
1300 | * \param demod: A pointer to a demodulator instance. | |
1301 | * \return DRXStatus_t Return status. | |
1302 | * \retval DRX_STS_OK: Opened demod instance with succes. | |
1303 | * \retval DRX_STS_ERROR: Driver not initialized or unable to initialize | |
1304 | * demod. | |
1305 | * \retval DRX_STS_INVALID_ARG: Demod instance has invalid content. | |
1306 | * | |
1307 | */ | |
1308 | ||
443f18d0 | 1309 | DRXStatus_t DRX_Open(pDRXDemodInstance_t demod) |
38b2df95 | 1310 | { |
443f18d0 MCC |
1311 | DRXStatus_t status = DRX_STS_OK; |
1312 | ||
1313 | if ((demod == NULL) || | |
1314 | (demod->myDemodFunct == NULL) || | |
1315 | (demod->myCommonAttr == NULL) || | |
1316 | (demod->myExtAttr == NULL) || | |
1317 | (demod->myI2CDevAddr == NULL) || | |
1318 | (demod->myCommonAttr->isOpened == TRUE)) { | |
1319 | return (DRX_STS_INVALID_ARG); | |
1320 | } | |
1321 | ||
1322 | status = (*(demod->myDemodFunct->openFunc)) (demod); | |
1323 | ||
1324 | if (status == DRX_STS_OK) { | |
1325 | demod->myCommonAttr->isOpened = TRUE; | |
1326 | } | |
1327 | ||
1328 | return status; | |
38b2df95 DH |
1329 | } |
1330 | ||
1331 | /*============================================================================*/ | |
1332 | ||
1333 | /** | |
1334 | * \brief Close device. | |
1335 | * \param demod: A pointer to a demodulator instance. | |
1336 | * \return DRXStatus_t Return status. | |
1337 | * \retval DRX_STS_OK: Closed demod instance with succes. | |
1338 | * \retval DRX_STS_ERROR: Driver not initialized or error during close | |
1339 | * demod. | |
1340 | * \retval DRX_STS_INVALID_ARG: Demod instance has invalid content. | |
1341 | * | |
1342 | * Free resources occupied by device instance. | |
1343 | * Put device into sleep mode. | |
1344 | */ | |
1345 | ||
443f18d0 | 1346 | DRXStatus_t DRX_Close(pDRXDemodInstance_t demod) |
38b2df95 | 1347 | { |
443f18d0 | 1348 | DRXStatus_t status = DRX_STS_OK; |
38b2df95 | 1349 | |
443f18d0 MCC |
1350 | if ((demod == NULL) || |
1351 | (demod->myDemodFunct == NULL) || | |
1352 | (demod->myCommonAttr == NULL) || | |
1353 | (demod->myExtAttr == NULL) || | |
1354 | (demod->myI2CDevAddr == NULL) || | |
1355 | (demod->myCommonAttr->isOpened == FALSE)) { | |
1356 | return DRX_STS_INVALID_ARG; | |
1357 | } | |
38b2df95 | 1358 | |
443f18d0 | 1359 | status = (*(demod->myDemodFunct->closeFunc)) (demod); |
38b2df95 | 1360 | |
443f18d0 | 1361 | DRX_SET_ISOPENED(demod, FALSE); |
38b2df95 | 1362 | |
443f18d0 | 1363 | return status; |
38b2df95 DH |
1364 | } |
1365 | ||
1366 | /*============================================================================*/ | |
1367 | ||
1368 | /** | |
1369 | * \brief Control the device. | |
1370 | * \param demod: A pointer to a demodulator instance. | |
1371 | * \param ctrl: Reference to desired control function. | |
1372 | * \param ctrlData: Pointer to data structure for control function. | |
1373 | * \return DRXStatus_t Return status. | |
1374 | * \retval DRX_STS_OK: Control function completed successfully. | |
1375 | * \retval DRX_STS_ERROR: Driver not initialized or error during | |
1376 | * control demod. | |
1377 | * \retval DRX_STS_INVALID_ARG: Demod instance or ctrlData has invalid | |
1378 | * content. | |
1379 | * \retval DRX_STS_FUNC_NOT_AVAILABLE: Specified control function is not | |
1380 | * available. | |
1381 | * | |
1382 | * Data needed or returned by the control function is stored in ctrlData. | |
1383 | * | |
1384 | */ | |
1385 | ||
1386 | DRXStatus_t | |
1387 | DRX_Ctrl(pDRXDemodInstance_t demod, DRXCtrlIndex_t ctrl, void *ctrlData) | |
1388 | { | |
443f18d0 MCC |
1389 | DRXStatus_t status = DRX_STS_ERROR; |
1390 | ||
1391 | if ((demod == NULL) || | |
1392 | (demod->myDemodFunct == NULL) || | |
1393 | (demod->myCommonAttr == NULL) || | |
1394 | (demod->myExtAttr == NULL) || (demod->myI2CDevAddr == NULL) | |
1395 | ) { | |
1396 | return (DRX_STS_INVALID_ARG); | |
1397 | } | |
1398 | ||
1399 | if (((demod->myCommonAttr->isOpened == FALSE) && | |
1400 | (ctrl != DRX_CTRL_PROBE_DEVICE) && (ctrl != DRX_CTRL_VERSION)) | |
1401 | ) { | |
1402 | return (DRX_STS_INVALID_ARG); | |
1403 | } | |
1404 | ||
1405 | if ((DRX_ISPOWERDOWNMODE(demod->myCommonAttr->currentPowerMode) && | |
1406 | (ctrl != DRX_CTRL_POWER_MODE) && | |
1407 | (ctrl != DRX_CTRL_PROBE_DEVICE) && | |
1408 | (ctrl != DRX_CTRL_NOP) && (ctrl != DRX_CTRL_VERSION) | |
1409 | ) | |
1410 | ) { | |
1411 | return DRX_STS_FUNC_NOT_AVAILABLE; | |
1412 | } | |
1413 | ||
1414 | /* Fixed control functions */ | |
1415 | switch (ctrl) { | |
38b2df95 | 1416 | /*======================================================================*/ |
443f18d0 MCC |
1417 | case DRX_CTRL_NOP: |
1418 | /* No operation */ | |
1419 | return DRX_STS_OK; | |
1420 | break; | |
38b2df95 DH |
1421 | |
1422 | /*======================================================================*/ | |
443f18d0 MCC |
1423 | case DRX_CTRL_VERSION: |
1424 | return CtrlVersion(demod, (pDRXVersionList_t *) ctrlData); | |
1425 | break; | |
38b2df95 DH |
1426 | |
1427 | /*======================================================================*/ | |
443f18d0 MCC |
1428 | default: |
1429 | /* Do nothing */ | |
1430 | break; | |
1431 | } | |
1432 | ||
1433 | /* Virtual functions */ | |
1434 | /* First try calling function from derived class */ | |
1435 | status = (*(demod->myDemodFunct->ctrlFunc)) (demod, ctrl, ctrlData); | |
1436 | if (status == DRX_STS_FUNC_NOT_AVAILABLE) { | |
1437 | /* Now try calling a the base class function */ | |
1438 | switch (ctrl) { | |
38b2df95 | 1439 | /*===================================================================*/ |
443f18d0 MCC |
1440 | case DRX_CTRL_LOAD_UCODE: |
1441 | return CtrlUCode(demod, | |
1442 | (pDRXUCodeInfo_t) ctrlData, | |
1443 | UCODE_UPLOAD); | |
1444 | break; | |
38b2df95 DH |
1445 | |
1446 | /*===================================================================*/ | |
443f18d0 MCC |
1447 | case DRX_CTRL_VERIFY_UCODE: |
1448 | { | |
1449 | return CtrlUCode(demod, | |
1450 | (pDRXUCodeInfo_t) ctrlData, | |
1451 | UCODE_VERIFY); | |
1452 | } | |
1453 | break; | |
38b2df95 DH |
1454 | |
1455 | #ifndef DRX_EXCLUDE_SCAN | |
1456 | /*===================================================================*/ | |
443f18d0 MCC |
1457 | case DRX_CTRL_SCAN_INIT: |
1458 | { | |
1459 | return CtrlScanInit(demod, | |
1460 | (pDRXScanParam_t) ctrlData); | |
1461 | } | |
1462 | break; | |
38b2df95 DH |
1463 | |
1464 | /*===================================================================*/ | |
443f18d0 MCC |
1465 | case DRX_CTRL_SCAN_NEXT: |
1466 | { | |
43a431e4 | 1467 | return CtrlScanNext(demod, (u16 *) ctrlData); |
443f18d0 MCC |
1468 | } |
1469 | break; | |
38b2df95 DH |
1470 | |
1471 | /*===================================================================*/ | |
443f18d0 MCC |
1472 | case DRX_CTRL_SCAN_STOP: |
1473 | { | |
1474 | return CtrlScanStop(demod); | |
1475 | } | |
1476 | break; | |
38b2df95 DH |
1477 | #endif /* #ifndef DRX_EXCLUDE_SCAN */ |
1478 | ||
1479 | /*===================================================================*/ | |
443f18d0 MCC |
1480 | case DRX_CTRL_PROGRAM_TUNER: |
1481 | { | |
1482 | return CtrlProgramTuner(demod, | |
1483 | (pDRXChannel_t) | |
1484 | ctrlData); | |
1485 | } | |
1486 | break; | |
38b2df95 DH |
1487 | |
1488 | /*===================================================================*/ | |
443f18d0 MCC |
1489 | case DRX_CTRL_DUMP_REGISTERS: |
1490 | { | |
1491 | return CtrlDumpRegisters(demod, | |
1492 | (pDRXRegDump_t) | |
1493 | ctrlData); | |
1494 | } | |
1495 | break; | |
38b2df95 DH |
1496 | |
1497 | /*===================================================================*/ | |
443f18d0 MCC |
1498 | default: |
1499 | return DRX_STS_FUNC_NOT_AVAILABLE; | |
1500 | } | |
1501 | } else { | |
1502 | return (status); | |
1503 | } | |
1504 | ||
1505 | return DRX_STS_OK; | |
38b2df95 DH |
1506 | } |
1507 | ||
38b2df95 DH |
1508 | /*============================================================================*/ |
1509 | ||
1510 | /* END OF FILE */ |