blob: 09f69ce32239f1f783d80ca87ceecfd870e3c4e4 [file] [log] [blame]
Wolfgang Denk4646d2a2006-05-30 15:56:48 +02001/**
2 * @file IxQMgrDispatcher.c
3 *
4 * @author Intel Corporation
5 * @date 20-Dec-2001
6 *
7 * @brief This file contains the implementation of the Dispatcher sub component
8 *
9 *
10 * @par
11 * IXP400 SW Release version 2.0
12 *
13 * -- Copyright Notice --
14 *
15 * @par
16 * Copyright 2001-2005, Intel Corporation.
17 * All rights reserved.
18 *
19 * @par
20 * Redistribution and use in source and binary forms, with or without
21 * modification, are permitted provided that the following conditions
22 * are met:
23 * 1. Redistributions of source code must retain the above copyright
24 * notice, this list of conditions and the following disclaimer.
25 * 2. Redistributions in binary form must reproduce the above copyright
26 * notice, this list of conditions and the following disclaimer in the
27 * documentation and/or other materials provided with the distribution.
28 * 3. Neither the name of the Intel Corporation nor the names of its contributors
29 * may be used to endorse or promote products derived from this software
30 * without specific prior written permission.
31 *
32 * @par
33 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
34 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
35 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
36 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
37 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
38 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
39 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
41 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
42 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
43 * SUCH DAMAGE.
44 *
45 * @par
46 * -- End of Copyright Notice --
47*/
48
49/*
50 * User defined include files.
51 */
52#include "IxQMgr.h"
53#include "IxQMgrAqmIf_p.h"
54#include "IxQMgrQCfg_p.h"
55#include "IxQMgrDispatcher_p.h"
56#include "IxQMgrLog_p.h"
57#include "IxQMgrDefines_p.h"
58#include "IxFeatureCtrl.h"
59#include "IxOsal.h"
60
61
62
63/*
64 * #defines and macros used in this file.
65 */
66
67
68/*
69 * This constant is used to indicate the number of priority levels supported
70 */
71#define IX_QMGR_NUM_PRIORITY_LEVELS 3
72
73/*
74 * This constant is used to set the size of the array of status words
75 */
76#define MAX_Q_STATUS_WORDS 4
77
78/*
79 * This macro is used to check if a given priority is valid
80 */
81#define IX_QMGR_DISPATCHER_PRIORITY_CHECK(priority) \
82(((priority) >= IX_QMGR_Q_PRIORITY_0) && ((priority) <= IX_QMGR_Q_PRIORITY_2))
83
84/*
85 * This macto is used to check that a given interrupt source is valid
86 */
87#define IX_QMGR_DISPATCHER_SOURCE_ID_CHECK(srcSel) \
88(((srcSel) >= IX_QMGR_Q_SOURCE_ID_E) && ((srcSel) <= IX_QMGR_Q_SOURCE_ID_NOT_F))
89
90/*
91 * Number of times a dummy callback is called before logging a trace
92 * message
93 */
94#define LOG_THROTTLE_COUNT 1000000
95
96/* Priority tables limits */
97#define IX_QMGR_MIN_LOW_QUE_PRIORITY_TABLE_INDEX (0)
98#define IX_QMGR_MID_LOW_QUE_PRIORITY_TABLE_INDEX (16)
99#define IX_QMGR_MAX_LOW_QUE_PRIORITY_TABLE_INDEX (31)
100#define IX_QMGR_MIN_UPP_QUE_PRIORITY_TABLE_INDEX (32)
101#define IX_QMGR_MID_UPP_QUE_PRIORITY_TABLE_INDEX (48)
102#define IX_QMGR_MAX_UPP_QUE_PRIORITY_TABLE_INDEX (63)
103
104/*
105 * This macro is used to check if a given callback type is valid
106 */
107#define IX_QMGR_DISPATCHER_CALLBACK_TYPE_CHECK(type) \
108 (((type) >= IX_QMGR_TYPE_REALTIME_OTHER) && \
109 ((type) <= IX_QMGR_TYPE_REALTIME_SPORADIC))
110
111/*
112 * define max index in lower queue to use in loops
113 */
114#define IX_QMGR_MAX_LOW_QUE_TABLE_INDEX (31)
115
116/*
117 * Typedefs whose scope is limited to this file.
118 */
119
120/*
121 * Information on a queue needed by the Dispatcher
122 */
123typedef struct
124{
125 IxQMgrCallback callback; /* Notification callback */
126 IxQMgrCallbackId callbackId; /* Notification callback identifier */
127 unsigned dummyCallbackCount; /* Number of times runs of dummy callback */
128 IxQMgrPriority priority; /* Dispatch priority */
129 unsigned int statusWordOffset; /* Offset to the status word to check */
130 UINT32 statusMask; /* Status mask */
131 UINT32 statusCheckValue; /* Status check value */
132 UINT32 intRegCheckMask; /* Interrupt register check mask */
133} IxQMgrQInfo;
134
135/*
136 * Variable declarations global to this file. Externs are followed by
137 * statics.
138 */
139
140/*
141 * Flag to keep record of what dispatcher set in featureCtrl when ixQMgrInit()
142 * is called. This is needed because it is possible that a client might
143 * change whether the live lock prevention dispatcher is used between
144 * calls to ixQMgrInit() and ixQMgrDispatcherLoopGet().
145 */
146PRIVATE IX_STATUS ixQMgrOrigB0Dispatcher = IX_FEATURE_CTRL_COMPONENT_ENABLED;
147
148/*
149 * keep record of Q types - not in IxQMgrQInfo for performance as
150 * it is only used with ixQMgrDispatcherLoopRunB0LLP()
151 */
152PRIVATE IxQMgrType ixQMgrQTypes[IX_QMGR_MAX_NUM_QUEUES];
153
154/*
155 * This array contains a list of queue identifiers ordered by priority. The table
156 * is split logically between queue identifiers 0-31 and 32-63.
157 */
158static IxQMgrQId priorityTable[IX_QMGR_MAX_NUM_QUEUES];
159
160/*
161 * This flag indicates to the dispatcher that the priority table needs to be rebuilt.
162 */
163static BOOL rebuildTable = FALSE;
164
165/* Dispatcher statistics */
166static IxQMgrDispatcherStats dispatcherStats;
167
168/* Table of queue information */
169static IxQMgrQInfo dispatchQInfo[IX_QMGR_MAX_NUM_QUEUES];
170
171/* Masks use to identify the first queues in the priority tables
172* when comparing with the interrupt register
173*/
174static unsigned int lowPriorityTableFirstHalfMask;
175static unsigned int uppPriorityTableFirstHalfMask;
176
177/*
178 * Static function prototypes
179 */
180
181/*
182 * This function is the default callback for all queues
183 */
184PRIVATE void
185dummyCallback (IxQMgrQId qId,
186 IxQMgrCallbackId cbId);
187
188PRIVATE void
189ixQMgrDispatcherReBuildPriorityTable (void);
190
191/*
192 * Function definitions.
193 */
194void
195ixQMgrDispatcherInit (void)
196{
197 int i;
198 IxFeatureCtrlProductId productId = 0;
199 IxFeatureCtrlDeviceId deviceId = 0;
200 BOOL stickyIntSilicon = TRUE;
201
202 /* Set default priorities */
203 for (i=0; i< IX_QMGR_MAX_NUM_QUEUES; i++)
204 {
205 dispatchQInfo[i].callback = dummyCallback;
206 dispatchQInfo[i].callbackId = 0;
207 dispatchQInfo[i].dummyCallbackCount = 0;
208 dispatchQInfo[i].priority = IX_QMGR_Q_PRIORITY_2;
209 dispatchQInfo[i].statusWordOffset = 0;
210 dispatchQInfo[i].statusCheckValue = 0;
211 dispatchQInfo[i].statusMask = 0;
212 /*
213 * There are two interrupt registers, 32 bits each. One for the lower
214 * queues(0-31) and one for the upper queues(32-63). Therefore need to
215 * mod by 32 i.e the min upper queue identifier.
216 */
217 dispatchQInfo[i].intRegCheckMask = (1<<(i%(IX_QMGR_MIN_QUEUPP_QID)));
218
219 /*
220 * Set the Q types - will only be used with livelock
221 */
222 ixQMgrQTypes[i] = IX_QMGR_TYPE_REALTIME_OTHER;
223
224 /* Reset queue statistics */
225 dispatcherStats.queueStats[i].callbackCnt = 0;
226 dispatcherStats.queueStats[i].priorityChangeCnt = 0;
227 dispatcherStats.queueStats[i].intNoCallbackCnt = 0;
228 dispatcherStats.queueStats[i].intLostCallbackCnt = 0;
229 dispatcherStats.queueStats[i].notificationEnabled = FALSE;
230 dispatcherStats.queueStats[i].srcSel = 0;
231
232 }
233
234 /* Priority table. Order the table from queue 0 to 63 */
235 ixQMgrDispatcherReBuildPriorityTable();
236
237 /* Reset statistics */
238 dispatcherStats.loopRunCnt = 0;
239
240 /* Get the device ID for the underlying silicon */
241 deviceId = ixFeatureCtrlDeviceRead();
242
243 /* Get the product ID for the underlying silicon */
244 productId = ixFeatureCtrlProductIdRead();
245
246 /*
247 * Check featureCtrl to see if Livelock prevention is required
248 */
249 ixQMgrOrigB0Dispatcher = ixFeatureCtrlSwConfigurationCheck(
250 IX_FEATURECTRL_ORIGB0_DISPATCHER);
251
252 /*
253 * Check if the silicon supports the sticky interrupt feature.
254 * IF (IXP42X AND A0) -> No sticky interrupt feature supported
255 */
256 if ((IX_FEATURE_CTRL_DEVICE_TYPE_IXP42X ==
257 (IX_FEATURE_CTRL_DEVICE_TYPE_MASK & deviceId)) &&
258 (IX_FEATURE_CTRL_SILICON_TYPE_A0 ==
259 (IX_FEATURE_CTRL_SILICON_STEPPING_MASK & productId)))
260 {
261 stickyIntSilicon = FALSE;
262 }
263
264 /*
265 * IF user wants livelock prev option AND silicon supports sticky interrupt
266 * feature -> enable the sticky interrupt bit
267 */
268 if ((IX_FEATURE_CTRL_SWCONFIG_DISABLED == ixQMgrOrigB0Dispatcher) &&
269 stickyIntSilicon)
270 {
271 ixQMgrStickyInterruptRegEnable();
272 }
273}
274
275IX_STATUS
276ixQMgrDispatcherPrioritySet (IxQMgrQId qId,
277 IxQMgrPriority priority)
278{
279 int ixQMgrLockKey;
280
281 if (!ixQMgrQIsConfigured(qId))
282 {
283 return IX_QMGR_Q_NOT_CONFIGURED;
284 }
285
286 if (!IX_QMGR_DISPATCHER_PRIORITY_CHECK(priority))
287 {
288 return IX_QMGR_Q_INVALID_PRIORITY;
289 }
290
291 ixQMgrLockKey = ixOsalIrqLock();
292
293 /* Change priority */
294 dispatchQInfo[qId].priority = priority;
295 /* Set flag */
296 rebuildTable = TRUE;
297
298 ixOsalIrqUnlock(ixQMgrLockKey);
299
300#ifndef NDEBUG
301 /* Update statistics */
302 dispatcherStats.queueStats[qId].priorityChangeCnt++;
303#endif
304
305 return IX_SUCCESS;
306}
307
308IX_STATUS
309ixQMgrNotificationCallbackSet (IxQMgrQId qId,
310 IxQMgrCallback callback,
311 IxQMgrCallbackId callbackId)
312{
313 if (!ixQMgrQIsConfigured(qId))
314 {
315 return IX_QMGR_Q_NOT_CONFIGURED;
316 }
317
318 if (NULL == callback)
319 {
320 /* Reset to dummy callback */
321 dispatchQInfo[qId].callback = dummyCallback;
322 dispatchQInfo[qId].dummyCallbackCount = 0;
323 dispatchQInfo[qId].callbackId = 0;
324 }
325 else
326 {
327 dispatchQInfo[qId].callback = callback;
328 dispatchQInfo[qId].callbackId = callbackId;
329 }
330
331 return IX_SUCCESS;
332}
333
334IX_STATUS
335ixQMgrNotificationEnable (IxQMgrQId qId,
336 IxQMgrSourceId srcSel)
337{
338 IxQMgrQStatus qStatusOnEntry;/* The queue status on entry/exit */
339 IxQMgrQStatus qStatusOnExit; /* to this function */
340 int ixQMgrLockKey;
341
342#ifndef NDEBUG
343 if (!ixQMgrQIsConfigured (qId))
344 {
345 return IX_QMGR_Q_NOT_CONFIGURED;
346 }
347
348 if ((qId < IX_QMGR_MIN_QUEUPP_QID) &&
349 !IX_QMGR_DISPATCHER_SOURCE_ID_CHECK(srcSel))
350 {
351 /* QId 0-31 source id invalid */
352 return IX_QMGR_INVALID_INT_SOURCE_ID;
353 }
354
355 if ((IX_QMGR_Q_SOURCE_ID_NE != srcSel) &&
356 (qId >= IX_QMGR_MIN_QUEUPP_QID))
357 {
358 /*
359 * For queues 32-63 the interrupt source is fixed to the Nearly
360 * Empty status flag and therefore should have a srcSel of NE.
361 */
362 return IX_QMGR_INVALID_INT_SOURCE_ID;
363 }
364#endif
365
366#ifndef NDEBUG
367 dispatcherStats.queueStats[qId].notificationEnabled = TRUE;
368 dispatcherStats.queueStats[qId].srcSel = srcSel;
369#endif
370
371 /* Get the current queue status */
372 ixQMgrAqmIfQueStatRead (qId, &qStatusOnEntry);
373
374 /*
375 * Enabling interrupts results in Read-Modify-Write
376 * so need critical section
377 */
378
379 ixQMgrLockKey = ixOsalIrqLock();
380
381 /* Calculate the checkMask and checkValue for this q */
382 ixQMgrAqmIfQStatusCheckValsCalc (qId,
383 srcSel,
384 &dispatchQInfo[qId].statusWordOffset,
385 &dispatchQInfo[qId].statusCheckValue,
386 &dispatchQInfo[qId].statusMask);
387
388
389 /* Set the interupt source is this queue is in the range 0-31 */
390 if (qId < IX_QMGR_MIN_QUEUPP_QID)
391 {
392 ixQMgrAqmIfIntSrcSelWrite (qId, srcSel);
393 }
394
395 /* Enable the interrupt */
396 ixQMgrAqmIfQInterruptEnable (qId);
397
398 ixOsalIrqUnlock(ixQMgrLockKey);
399
400 /* Get the current queue status */
401 ixQMgrAqmIfQueStatRead (qId, &qStatusOnExit);
402
403 /* If the status has changed return a warning */
404 if (qStatusOnEntry != qStatusOnExit)
405 {
406 return IX_QMGR_WARNING;
407 }
408
409 return IX_SUCCESS;
410}
411
412
413IX_STATUS
414ixQMgrNotificationDisable (IxQMgrQId qId)
415{
416 int ixQMgrLockKey;
417
418#ifndef NDEBUG
419 /* Validate parameters */
420 if (!ixQMgrQIsConfigured (qId))
421 {
422 return IX_QMGR_Q_NOT_CONFIGURED;
423 }
424#endif
425
426 /*
427 * Enabling interrupts results in Read-Modify-Write
428 * so need critical section
429 */
430#ifndef NDEBUG
431 dispatcherStats.queueStats[qId].notificationEnabled = FALSE;
432#endif
433
434 ixQMgrLockKey = ixOsalIrqLock();
435
436 ixQMgrAqmIfQInterruptDisable (qId);
437
438 ixOsalIrqUnlock(ixQMgrLockKey);
439
440 return IX_SUCCESS;
441}
442
443void
444ixQMgrStickyInterruptRegEnable(void)
445{
446 /* Use Aqm If function to set Interrupt Register0 Bit-3 */
447 ixQMgrAqmIfIntSrcSelReg0Bit3Set ();
448}
449
450#if !defined __XSCALE__ || defined __linux
451
452/* Count the number of leading zero bits in a word,
453 * and return the same value than the CLZ instruction.
454 *
455 * word (in) return value (out)
456 * 0x80000000 0
457 * 0x40000000 1
458 * ,,, ,,,
459 * 0x00000002 30
460 * 0x00000001 31
461 * 0x00000000 32
462 *
463 * The C version of this function is used as a replacement
464 * for system not providing the equivalent of the CLZ
465 * assembly language instruction.
466 *
467 * Note that this version is big-endian
468 */
469unsigned int
470ixQMgrCountLeadingZeros(UINT32 word)
471{
472 unsigned int leadingZerosCount = 0;
473
474 if (word == 0)
475 {
476 return 32;
477 }
478 /* search the first bit set by testing the MSB and shifting the input word */
479 while ((word & 0x80000000) == 0)
480 {
481 word <<= 1;
482 leadingZerosCount++;
483 }
484 return leadingZerosCount;
485}
486#endif /* not __XSCALE__ or __linux */
487
488void
489ixQMgrDispatcherLoopGet (IxQMgrDispatcherFuncPtr *qDispatcherFuncPtr)
490{
491 IxFeatureCtrlProductId productId = 0;
492 IxFeatureCtrlDeviceId deviceId = 0;
493
494 /* Get the device ID for the underlying silicon */
495 deviceId = ixFeatureCtrlDeviceRead();
496
497 /* Get the product ID for the underlying silicon */
498 productId = ixFeatureCtrlProductIdRead ();
499
500 /* IF (IXP42X AND A0 silicon) -> use ixQMgrDispatcherLoopRunA0 */
501 if ((IX_FEATURE_CTRL_DEVICE_TYPE_IXP42X ==
502 (IX_FEATURE_CTRL_DEVICE_TYPE_MASK & deviceId)) &&
503 (IX_FEATURE_CTRL_SILICON_TYPE_A0 ==
504 (IX_FEATURE_CTRL_SILICON_STEPPING_MASK & productId)))
505 {
506 /*For IXP42X A0 silicon */
507 *qDispatcherFuncPtr = &ixQMgrDispatcherLoopRunA0 ;
508 }
509 else /*For IXP42X B0 or IXP46X silicon*/
510 {
511 if (IX_FEATURE_CTRL_SWCONFIG_ENABLED == ixQMgrOrigB0Dispatcher)
512 {
513 /* Default for IXP42X B0 and IXP46X silicon */
514 *qDispatcherFuncPtr = &ixQMgrDispatcherLoopRunB0;
515 }
516 else
517 {
518 /* FeatureCtrl indicated that livelock dispatcher be used */
519 *qDispatcherFuncPtr = &ixQMgrDispatcherLoopRunB0LLP;
520 }
521 }
522}
523
524void
525ixQMgrDispatcherLoopRunA0 (IxQMgrDispatchGroup group)
526{
527 UINT32 intRegVal; /* Interrupt reg val */
528 UINT32 intRegValAfterWrite; /* Interrupt reg val after writing back */
529 UINT32 intRegCheckMask; /* Mask for checking interrupt bits */
530 UINT32 qStatusWordsB4Write[MAX_Q_STATUS_WORDS]; /* Status b4 interrupt write */
531 UINT32 qStatusWordsAfterWrite[MAX_Q_STATUS_WORDS]; /* Status after interrupt write */
532 IxQMgrQInfo *currDispatchQInfo;
533 BOOL statusChangeFlag;
534
535 int priorityTableIndex;/* Priority table index */
536 int qIndex; /* Current queue being processed */
537 int endIndex; /* Index of last queue to process */
538
539#ifndef NDEBUG
540 IX_OSAL_ASSERT((group == IX_QMGR_QUEUPP_GROUP) ||
541 (group == IX_QMGR_QUELOW_GROUP));
542#endif
543
544 /* Read Q status registers before interrupt status read/write */
545 ixQMgrAqmIfQStatusRegsRead (group, qStatusWordsB4Write);
546
547 /* Read the interrupt register */
548 ixQMgrAqmIfQInterruptRegRead (group, &intRegVal);
549
550 /* No bit set : nothing to process (the reaminder of the algorithm is
551 * based on the fact that the interrupt register value contains at
552 * least one bit set
553 */
554 if (intRegVal == 0)
555 {
556#ifndef NDEBUG
557 /* Update statistics */
558 dispatcherStats.loopRunCnt++;
559#endif
560
561 /* Rebuild the priority table if needed */
562 if (rebuildTable)
563 {
564 ixQMgrDispatcherReBuildPriorityTable ();
565 }
566
567 return;
568 }
569
570 /* Write it back to clear the interrupt */
571 ixQMgrAqmIfQInterruptRegWrite (group, intRegVal);
572
573 /* Read Q status registers after interrupt status read/write */
574 ixQMgrAqmIfQStatusRegsRead (group, qStatusWordsAfterWrite);
575
576 /* get the first queue Id from the interrupt register value */
577 qIndex = (BITS_PER_WORD - 1) - ixQMgrCountLeadingZeros(intRegVal);
578
579 /* check if any change occured during hw register modifications */
580 if (IX_QMGR_QUELOW_GROUP == group)
581 {
582 statusChangeFlag =
583 (qStatusWordsB4Write[0] != qStatusWordsAfterWrite[0]) ||
584 (qStatusWordsB4Write[1] != qStatusWordsAfterWrite[1]) ||
585 (qStatusWordsB4Write[2] != qStatusWordsAfterWrite[2]) ||
586 (qStatusWordsB4Write[3] != qStatusWordsAfterWrite[3]);
587 }
588 else
589 {
590 statusChangeFlag =
591 (qStatusWordsB4Write[0] != qStatusWordsAfterWrite[0]);
592 /* Set the queue range based on the queue group to proccess */
593 qIndex += IX_QMGR_MIN_QUEUPP_QID;
594 }
595
596 if (statusChangeFlag == FALSE)
597 {
598 /* check if the interrupt register contains
599 * only 1 bit set (happy day scenario)
600 */
601 currDispatchQInfo = &dispatchQInfo[qIndex];
602 if (intRegVal == currDispatchQInfo->intRegCheckMask)
603 {
604 /* only 1 queue event triggered a notification *
605 * Call the callback function for this queue
606 */
607 currDispatchQInfo->callback (qIndex,
608 currDispatchQInfo->callbackId);
609#ifndef NDEBUG
610 /* Update statistics */
611 dispatcherStats.queueStats[qIndex].callbackCnt++;
612#endif
613 }
614 else
615 {
616 /* the event is triggered by more than 1 queue,
617 * the queue search will be starting from the beginning
618 * or the middle of the priority table
619 *
620 * the serach will end when all the bits of the interrupt
621 * register are cleared. There is no need to maintain
622 * a seperate value and test it at each iteration.
623 */
624 if (IX_QMGR_QUELOW_GROUP == group)
625 {
626 /* check if any bit related to queues in the first
627 * half of the priority table is set
628 */
629 if (intRegVal & lowPriorityTableFirstHalfMask)
630 {
631 priorityTableIndex = IX_QMGR_MIN_LOW_QUE_PRIORITY_TABLE_INDEX;
632 }
633 else
634 {
635 priorityTableIndex = IX_QMGR_MID_LOW_QUE_PRIORITY_TABLE_INDEX;
636 }
637 }
638 else
639 {
640 /* check if any bit related to queues in the first
641 * half of the priority table is set
642 */
643 if (intRegVal & uppPriorityTableFirstHalfMask)
644 {
645 priorityTableIndex = IX_QMGR_MIN_UPP_QUE_PRIORITY_TABLE_INDEX;
646 }
647 else
648 {
649 priorityTableIndex = IX_QMGR_MID_UPP_QUE_PRIORITY_TABLE_INDEX;
650 }
651 }
652
653 /* iterate following the priority table until all the bits
654 * of the interrupt register are cleared.
655 */
656 do
657 {
658 qIndex = priorityTable[priorityTableIndex++];
659 currDispatchQInfo = &dispatchQInfo[qIndex];
660 intRegCheckMask = currDispatchQInfo->intRegCheckMask;
661
662 /* If this queue caused this interrupt to be raised */
663 if (intRegVal & intRegCheckMask)
664 {
665 /* Call the callback function for this queue */
666 currDispatchQInfo->callback (qIndex,
667 currDispatchQInfo->callbackId);
668#ifndef NDEBUG
669 /* Update statistics */
670 dispatcherStats.queueStats[qIndex].callbackCnt++;
671#endif
672
673 /* Clear the interrupt register bit */
674 intRegVal &= ~intRegCheckMask;
675 }
676 }
677 while(intRegVal);
678 }
679 }
680 else
681 {
682 /* A change in queue status occured during the hw interrupt
683 * register update. To maintain the interrupt consistency, it
684 * is necessary to iterate through all queues of the queue group.
685 */
686
687 /* Read interrupt status again */
688 ixQMgrAqmIfQInterruptRegRead (group, &intRegValAfterWrite);
689
690 if (IX_QMGR_QUELOW_GROUP == group)
691 {
692 priorityTableIndex = IX_QMGR_MIN_LOW_QUE_PRIORITY_TABLE_INDEX;
693 endIndex = IX_QMGR_MAX_LOW_QUE_PRIORITY_TABLE_INDEX;
694 }
695 else
696 {
697 priorityTableIndex = IX_QMGR_MIN_UPP_QUE_PRIORITY_TABLE_INDEX;
698 endIndex = IX_QMGR_MAX_UPP_QUE_PRIORITY_TABLE_INDEX;
699 }
700
701 for ( ; priorityTableIndex<=endIndex; priorityTableIndex++)
702 {
703 qIndex = priorityTable[priorityTableIndex];
704 currDispatchQInfo = &dispatchQInfo[qIndex];
705 intRegCheckMask = currDispatchQInfo->intRegCheckMask;
706
707 /* If this queue caused this interrupt to be raised */
708 if (intRegVal & intRegCheckMask)
709 {
710 /* Call the callback function for this queue */
711 currDispatchQInfo->callback (qIndex,
712 currDispatchQInfo->callbackId);
713#ifndef NDEBUG
714 /* Update statistics */
715 dispatcherStats.queueStats[qIndex].callbackCnt++;
716#endif
717
718 } /* if (intRegVal .. */
719
720 /*
721 * If interrupt bit is set in intRegValAfterWrite don't
722 * proceed as this will be caught in next interrupt
723 */
724 else if ((intRegValAfterWrite & intRegCheckMask) == 0)
725 {
726 /* Check if an interrupt was lost for this Q */
727 if (ixQMgrAqmIfQStatusCheck(qStatusWordsB4Write,
728 qStatusWordsAfterWrite,
729 currDispatchQInfo->statusWordOffset,
730 currDispatchQInfo->statusCheckValue,
731 currDispatchQInfo->statusMask))
732 {
733 /* Call the callback function for this queue */
734 currDispatchQInfo->callback (qIndex,
735 dispatchQInfo[qIndex].callbackId);
736#ifndef NDEBUG
737 /* Update statistics */
738 dispatcherStats.queueStats[qIndex].callbackCnt++;
739 dispatcherStats.queueStats[qIndex].intLostCallbackCnt++;
740#endif
741 } /* if ixQMgrAqmIfQStatusCheck(.. */
742 } /* else if ((intRegValAfterWrite ... */
743 } /* for (priorityTableIndex=0 ... */
744 }
745
746 /* Rebuild the priority table if needed */
747 if (rebuildTable)
748 {
749 ixQMgrDispatcherReBuildPriorityTable ();
750 }
751
752#ifndef NDEBUG
753 /* Update statistics */
754 dispatcherStats.loopRunCnt++;
755#endif
756}
757
758
759
760void
761ixQMgrDispatcherLoopRunB0 (IxQMgrDispatchGroup group)
762{
763 UINT32 intRegVal; /* Interrupt reg val */
764 UINT32 intRegCheckMask; /* Mask for checking interrupt bits */
765 IxQMgrQInfo *currDispatchQInfo;
766
767
768 int priorityTableIndex; /* Priority table index */
769 int qIndex; /* Current queue being processed */
770
771#ifndef NDEBUG
772 IX_OSAL_ASSERT((group == IX_QMGR_QUEUPP_GROUP) ||
773 (group == IX_QMGR_QUELOW_GROUP));
774 IX_OSAL_ASSERT((group == IX_QMGR_QUEUPP_GROUP) ||
775 (group == IX_QMGR_QUELOW_GROUP));
776#endif
777
778 /* Read the interrupt register */
779 ixQMgrAqmIfQInterruptRegRead (group, &intRegVal);
780
781
782 /* No queue has interrupt register set */
783 if (intRegVal != 0)
784 {
785
786 /* Write it back to clear the interrupt */
787 ixQMgrAqmIfQInterruptRegWrite (group, intRegVal);
788
789 /* get the first queue Id from the interrupt register value */
790 qIndex = (BITS_PER_WORD - 1) - ixQMgrCountLeadingZeros(intRegVal);
791
792 if (IX_QMGR_QUEUPP_GROUP == group)
793 {
794 /* Set the queue range based on the queue group to proccess */
795 qIndex += IX_QMGR_MIN_QUEUPP_QID;
796 }
797
798 /* check if the interrupt register contains
799 * only 1 bit set
800 * For example:
801 * intRegVal = 0x0010
802 * currDispatchQInfo->intRegCheckMask = 0x0010
803 * intRegVal == currDispatchQInfo->intRegCheckMask is TRUE.
804 */
805 currDispatchQInfo = &dispatchQInfo[qIndex];
806 if (intRegVal == currDispatchQInfo->intRegCheckMask)
807 {
808 /* only 1 queue event triggered a notification *
809 * Call the callback function for this queue
810 */
811 currDispatchQInfo->callback (qIndex,
812 currDispatchQInfo->callbackId);
813#ifndef NDEBUG
814 /* Update statistics */
815 dispatcherStats.queueStats[qIndex].callbackCnt++;
816#endif
817 }
818 else
819 {
820 /* the event is triggered by more than 1 queue,
821 * the queue search will be starting from the beginning
822 * or the middle of the priority table
823 *
824 * the serach will end when all the bits of the interrupt
825 * register are cleared. There is no need to maintain
826 * a seperate value and test it at each iteration.
827 */
828 if (IX_QMGR_QUELOW_GROUP == group)
829 {
830 /* check if any bit related to queues in the first
831 * half of the priority table is set
832 */
833 if (intRegVal & lowPriorityTableFirstHalfMask)
834 {
835 priorityTableIndex = IX_QMGR_MIN_LOW_QUE_PRIORITY_TABLE_INDEX;
836 }
837 else
838 {
839 priorityTableIndex = IX_QMGR_MID_LOW_QUE_PRIORITY_TABLE_INDEX;
840 }
841 }
842 else
843 {
844 /* check if any bit related to queues in the first
845 * half of the priority table is set
846 */
847 if (intRegVal & uppPriorityTableFirstHalfMask)
848 {
849 priorityTableIndex = IX_QMGR_MIN_UPP_QUE_PRIORITY_TABLE_INDEX;
850 }
851 else
852 {
853 priorityTableIndex = IX_QMGR_MID_UPP_QUE_PRIORITY_TABLE_INDEX;
854 }
855 }
856
857 /* iterate following the priority table until all the bits
858 * of the interrupt register are cleared.
859 */
860 do
861 {
862 qIndex = priorityTable[priorityTableIndex++];
863 currDispatchQInfo = &dispatchQInfo[qIndex];
864 intRegCheckMask = currDispatchQInfo->intRegCheckMask;
865
866 /* If this queue caused this interrupt to be raised */
867 if (intRegVal & intRegCheckMask)
868 {
869 /* Call the callback function for this queue */
870 currDispatchQInfo->callback (qIndex,
871 currDispatchQInfo->callbackId);
872#ifndef NDEBUG
873 /* Update statistics */
874 dispatcherStats.queueStats[qIndex].callbackCnt++;
875#endif
876
877 /* Clear the interrupt register bit */
878 intRegVal &= ~intRegCheckMask;
879 }
880 }
881 while(intRegVal);
882 } /*End of intRegVal == currDispatchQInfo->intRegCheckMask */
883 } /* End of intRegVal != 0 */
884
885#ifndef NDEBUG
886 /* Update statistics */
887 dispatcherStats.loopRunCnt++;
888#endif
889
890 /* Rebuild the priority table if needed */
891 if (rebuildTable)
892 {
893 ixQMgrDispatcherReBuildPriorityTable ();
894 }
895}
896
897void
898ixQMgrDispatcherLoopRunB0LLP (IxQMgrDispatchGroup group)
899{
900 UINT32 intRegVal =0; /* Interrupt reg val */
901 UINT32 intRegCheckMask; /* Mask for checking interrupt bits */
902 IxQMgrQInfo *currDispatchQInfo;
903
904 int priorityTableIndex; /* Priority table index */
905 int qIndex; /* Current queue being processed */
906
907 UINT32 intRegValCopy = 0;
908 UINT32 intEnableRegVal = 0;
909 UINT8 i = 0;
910
911#ifndef NDEBUG
912 IX_OSAL_ASSERT((group == IX_QMGR_QUEUPP_GROUP) ||
913 (group == IX_QMGR_QUELOW_GROUP));
914#endif
915
916 /* Read the interrupt register */
917 ixQMgrAqmIfQInterruptRegRead (group, &intRegVal);
918
919 /*
920 * mask any interrupts that are not enabled
921 */
922 ixQMgrAqmIfQInterruptEnableRegRead (group, &intEnableRegVal);
923 intRegVal &= intEnableRegVal;
924
925 /* No queue has interrupt register set */
926 if (intRegVal != 0)
927 {
928 if (IX_QMGR_QUELOW_GROUP == group)
929 {
930 /*
931 * As the sticky bit is set, the interrupt register will
932 * not clear if write back at this point because the condition
933 * has not been cleared. Take a copy and write back later after
934 * the condition has been cleared
935 */
936 intRegValCopy = intRegVal;
937 }
938 else
939 {
940 /* no sticky for upper Q's, so write back now */
941 ixQMgrAqmIfQInterruptRegWrite (group, intRegVal);
942 }
943
944 /* get the first queue Id from the interrupt register value */
945 qIndex = (BITS_PER_WORD - 1) - ixQMgrCountLeadingZeros(intRegVal);
946
947 if (IX_QMGR_QUEUPP_GROUP == group)
948 {
949 /* Set the queue range based on the queue group to proccess */
950 qIndex += IX_QMGR_MIN_QUEUPP_QID;
951 }
952
953 /* check if the interrupt register contains
954 * only 1 bit set
955 * For example:
956 * intRegVal = 0x0010
957 * currDispatchQInfo->intRegCheckMask = 0x0010
958 * intRegVal == currDispatchQInfo->intRegCheckMask is TRUE.
959 */
960 currDispatchQInfo = &dispatchQInfo[qIndex];
961 if (intRegVal == currDispatchQInfo->intRegCheckMask)
962 {
963
964 /*
965 * check if Q type periodic - only lower queues can
966 * have there type set to periodic
967 */
968 if (IX_QMGR_TYPE_REALTIME_PERIODIC == ixQMgrQTypes[qIndex])
969 {
970 /*
971 * Disable the notifications on any sporadics
972 */
973 for (i=0; i <= IX_QMGR_MAX_LOW_QUE_TABLE_INDEX; i++)
974 {
975 if (IX_QMGR_TYPE_REALTIME_SPORADIC == ixQMgrQTypes[i])
976 {
977 ixQMgrNotificationDisable(i);
978#ifndef NDEBUG
979 /* Update statistics */
980 dispatcherStats.queueStats[i].disableCount++;
981#endif
982 }
983 }
984 }
985
986 currDispatchQInfo->callback (qIndex,
987 currDispatchQInfo->callbackId);
988#ifndef NDEBUG
989 /* Update statistics */
990 dispatcherStats.queueStats[qIndex].callbackCnt++;
991#endif
992 }
993 else
994 {
995 /* the event is triggered by more than 1 queue,
996 * the queue search will be starting from the beginning
997 * or the middle of the priority table
998 *
999 * the serach will end when all the bits of the interrupt
1000 * register are cleared. There is no need to maintain
1001 * a seperate value and test it at each iteration.
1002 */
1003 if (IX_QMGR_QUELOW_GROUP == group)
1004 {
1005 /* check if any bit related to queues in the first
1006 * half of the priority table is set
1007 */
1008 if (intRegVal & lowPriorityTableFirstHalfMask)
1009 {
1010 priorityTableIndex =
1011 IX_QMGR_MIN_LOW_QUE_PRIORITY_TABLE_INDEX;
1012 }
1013 else
1014 {
1015 priorityTableIndex =
1016 IX_QMGR_MID_LOW_QUE_PRIORITY_TABLE_INDEX;
1017 }
1018 }
1019 else
1020 {
1021 /* check if any bit related to queues in the first
1022 * half of the priority table is set
1023 */
1024 if (intRegVal & uppPriorityTableFirstHalfMask)
1025 {
1026 priorityTableIndex =
1027 IX_QMGR_MIN_UPP_QUE_PRIORITY_TABLE_INDEX;
1028 }
1029 else
1030 {
1031 priorityTableIndex =
1032 IX_QMGR_MID_UPP_QUE_PRIORITY_TABLE_INDEX;
1033 }
1034 }
1035
1036 /* iterate following the priority table until all the bits
1037 * of the interrupt register are cleared.
1038 */
1039 do
1040 {
1041 qIndex = priorityTable[priorityTableIndex++];
1042 currDispatchQInfo = &dispatchQInfo[qIndex];
1043 intRegCheckMask = currDispatchQInfo->intRegCheckMask;
1044
1045 /* If this queue caused this interrupt to be raised */
1046 if (intRegVal & intRegCheckMask)
1047 {
1048 /*
1049 * check if Q type periodic - only lower queues can
1050 * have there type set to periodic. There can only be one
1051 * periodic queue, so the sporadics are only disabled once.
1052 */
1053 if (IX_QMGR_TYPE_REALTIME_PERIODIC == ixQMgrQTypes[qIndex])
1054 {
1055 /*
1056 * Disable the notifications on any sporadics
1057 */
1058 for (i=0; i <= IX_QMGR_MAX_LOW_QUE_TABLE_INDEX; i++)
1059 {
1060 if (IX_QMGR_TYPE_REALTIME_SPORADIC ==
1061 ixQMgrQTypes[i])
1062 {
1063 ixQMgrNotificationDisable(i);
1064 /*
1065 * remove from intRegVal as we don't want
1066 * to service any sporadics now
1067 */
1068 intRegVal &= ~dispatchQInfo[i].intRegCheckMask;
1069#ifndef NDEBUG
1070 /* Update statistics */
1071 dispatcherStats.queueStats[i].disableCount++;
1072#endif
1073 }
1074 }
1075 }
1076
1077 currDispatchQInfo->callback (qIndex,
1078 currDispatchQInfo->callbackId);
1079#ifndef NDEBUG
1080 /* Update statistics */
1081 dispatcherStats.queueStats[qIndex].callbackCnt++;
1082#endif
1083 /* Clear the interrupt register bit */
1084 intRegVal &= ~intRegCheckMask;
1085 }
1086 }
1087 while(intRegVal);
1088 } /*End of intRegVal == currDispatchQInfo->intRegCheckMask */
1089 } /* End of intRegVal != 0 */
1090
1091#ifndef NDEBUG
1092 /* Update statistics */
1093 dispatcherStats.loopRunCnt++;
1094#endif
1095
1096 if ((intRegValCopy != 0) && (IX_QMGR_QUELOW_GROUP == group))
1097 {
1098 /*
1099 * lower groups (therefore sticky) AND at least one enabled interrupt
1100 * Write back to clear the interrupt
1101 */
1102 ixQMgrAqmIfQInterruptRegWrite (IX_QMGR_QUELOW_GROUP, intRegValCopy);
1103 }
1104
1105 /* Rebuild the priority table if needed */
1106 if (rebuildTable)
1107 {
1108 ixQMgrDispatcherReBuildPriorityTable ();
1109 }
1110}
1111
1112PRIVATE void
1113ixQMgrDispatcherReBuildPriorityTable (void)
1114{
1115 UINT32 qIndex;
1116 UINT32 priority;
1117 int lowQuePriorityTableIndex = IX_QMGR_MIN_LOW_QUE_PRIORITY_TABLE_INDEX;
1118 int uppQuePriorityTableIndex = IX_QMGR_MIN_UPP_QUE_PRIORITY_TABLE_INDEX;
1119
1120 /* Reset the rebuild flag */
1121 rebuildTable = FALSE;
1122
1123 /* initialize the mak used to identify the queues in the first half
1124 * of the priority table
1125 */
1126 lowPriorityTableFirstHalfMask = 0;
1127 uppPriorityTableFirstHalfMask = 0;
1128
1129 /* For each priority level */
1130 for(priority=0; priority<IX_QMGR_NUM_PRIORITY_LEVELS; priority++)
1131 {
1132 /* Foreach low queue in this priority */
1133 for(qIndex=0; qIndex<IX_QMGR_MIN_QUEUPP_QID; qIndex++)
1134 {
1135 if (dispatchQInfo[qIndex].priority == priority)
1136 {
1137 /* build the priority table bitmask which match the
1138 * queues of the first half of the priority table
1139 */
1140 if (lowQuePriorityTableIndex < IX_QMGR_MID_LOW_QUE_PRIORITY_TABLE_INDEX)
1141 {
1142 lowPriorityTableFirstHalfMask |= dispatchQInfo[qIndex].intRegCheckMask;
1143 }
1144 /* build the priority table */
1145 priorityTable[lowQuePriorityTableIndex++] = qIndex;
1146 }
1147 }
1148 /* Foreach upp queue */
1149 for(qIndex=IX_QMGR_MIN_QUEUPP_QID; qIndex<=IX_QMGR_MAX_QID; qIndex++)
1150 {
1151 if (dispatchQInfo[qIndex].priority == priority)
1152 {
1153 /* build the priority table bitmask which match the
1154 * queues of the first half of the priority table
1155 */
1156 if (uppQuePriorityTableIndex < IX_QMGR_MID_UPP_QUE_PRIORITY_TABLE_INDEX)
1157 {
1158 uppPriorityTableFirstHalfMask |= dispatchQInfo[qIndex].intRegCheckMask;
1159 }
1160 /* build the priority table */
1161 priorityTable[uppQuePriorityTableIndex++] = qIndex;
1162 }
1163 }
1164 }
1165}
1166
1167IxQMgrDispatcherStats*
1168ixQMgrDispatcherStatsGet (void)
1169{
1170 return &dispatcherStats;
1171}
1172
1173PRIVATE void
1174dummyCallback (IxQMgrQId qId,
1175 IxQMgrCallbackId cbId)
1176{
1177 /* Throttle the trace message */
1178 if ((dispatchQInfo[qId].dummyCallbackCount % LOG_THROTTLE_COUNT) == 0)
1179 {
1180 IX_QMGR_LOG_WARNING2("--> dummyCallback: qId (%d), callbackId (%d)\n",qId,cbId);
1181 }
1182 dispatchQInfo[qId].dummyCallbackCount++;
1183
1184#ifndef NDEBUG
1185 /* Update statistcs */
1186 dispatcherStats.queueStats[qId].intNoCallbackCnt++;
1187#endif
1188}
1189void
1190ixQMgrLLPShow (int resetStats)
1191{
1192#ifndef NDEBUG
1193 UINT8 i = 0;
1194 IxQMgrQInfo *q;
1195 UINT32 intEnableRegVal = 0;
1196
1197 printf ("Livelock statistics are printed on the fly.\n");
1198 printf ("qId Type EnableCnt DisableCnt IntEnableState Callbacks\n");
1199 printf ("=== ======== ========= ========== ============== =========\n");
1200
1201 for (i=0; i<= IX_QMGR_MAX_LOW_QUE_TABLE_INDEX; i++)
1202 {
1203 q = &dispatchQInfo[i];
1204
1205 if (ixQMgrQTypes[i] != IX_QMGR_TYPE_REALTIME_OTHER)
1206 {
1207 printf (" %2d ", i);
1208
1209 if (ixQMgrQTypes[i] == IX_QMGR_TYPE_REALTIME_SPORADIC)
1210 {
1211 printf ("Sporadic");
1212 }
1213 else
1214 {
1215 printf ("Periodic");
1216 }
1217
1218
1219 ixQMgrAqmIfQInterruptEnableRegRead (IX_QMGR_QUELOW_GROUP,
1220 &intEnableRegVal);
1221
1222
1223 intEnableRegVal &= dispatchQInfo[i].intRegCheckMask;
1224 intEnableRegVal = intEnableRegVal >> i;
1225
1226 printf (" %10d %10d %10d %10d\n",
1227 dispatcherStats.queueStats[i].enableCount,
1228 dispatcherStats.queueStats[i].disableCount,
1229 intEnableRegVal,
1230 dispatcherStats.queueStats[i].callbackCnt);
1231
1232 if (resetStats)
1233 {
1234 dispatcherStats.queueStats[i].enableCount =
1235 dispatcherStats.queueStats[i].disableCount =
1236 dispatcherStats.queueStats[i].callbackCnt = 0;
1237 }
1238 }
1239 }
1240#else
1241 IX_QMGR_LOG0("Livelock Prevention statistics are only collected in debug mode\n");
1242#endif
1243}
1244
1245void
1246ixQMgrPeriodicDone (void)
1247{
1248 UINT32 i = 0;
1249 UINT32 ixQMgrLockKey = 0;
1250
1251 /*
1252 * for the lower queues
1253 */
1254 for (i=0; i <= IX_QMGR_MAX_LOW_QUE_TABLE_INDEX; i++)
1255 {
1256 /*
1257 * check for sporadics
1258 */
1259 if (IX_QMGR_TYPE_REALTIME_SPORADIC == ixQMgrQTypes[i])
1260 {
1261 /*
1262 * enable any sporadics
1263 */
1264 ixQMgrLockKey = ixOsalIrqLock();
1265 ixQMgrAqmIfQInterruptEnable(i);
1266 ixOsalIrqUnlock(ixQMgrLockKey);
1267#ifndef NDEBUG
1268 /*
1269 * Update statistics
1270 */
1271 dispatcherStats.queueStats[i].enableCount++;
1272 dispatcherStats.queueStats[i].notificationEnabled = TRUE;
1273#endif
1274 }
1275 }
1276}
1277IX_STATUS
1278ixQMgrCallbackTypeSet (IxQMgrQId qId,
1279 IxQMgrType type)
1280{
1281 UINT32 ixQMgrLockKey = 0;
1282 IxQMgrType ixQMgrOldType =0;
1283
1284#ifndef NDEBUG
1285 if (!ixQMgrQIsConfigured(qId))
1286 {
1287 return IX_QMGR_Q_NOT_CONFIGURED;
1288 }
1289 if (qId >= IX_QMGR_MIN_QUEUPP_QID)
1290 {
1291 return IX_QMGR_PARAMETER_ERROR;
1292 }
1293 if(!IX_QMGR_DISPATCHER_CALLBACK_TYPE_CHECK(type))
1294 {
1295 return IX_QMGR_PARAMETER_ERROR;
1296 }
1297#endif
1298
1299 ixQMgrOldType = ixQMgrQTypes[qId];
1300 ixQMgrQTypes[qId] = type;
1301
1302 /*
1303 * check if Q has been changed from type SPORADIC
1304 */
1305 if (IX_QMGR_TYPE_REALTIME_SPORADIC == ixQMgrOldType)
1306 {
1307 /*
1308 * previously Q was a SPORADIC, this means that LLP
1309 * might have had it disabled. enable it now.
1310 */
1311 ixQMgrLockKey = ixOsalIrqLock();
1312 ixQMgrAqmIfQInterruptEnable(qId);
1313 ixOsalIrqUnlock(ixQMgrLockKey);
1314
1315#ifndef NDEBUG
1316 /*
1317 * Update statistics
1318 */
1319 dispatcherStats.queueStats[qId].enableCount++;
1320#endif
1321 }
1322
1323 return IX_SUCCESS;
1324}
1325
1326IX_STATUS
1327ixQMgrCallbackTypeGet (IxQMgrQId qId,
1328 IxQMgrType *type)
1329{
1330#ifndef NDEBUG
1331 if (!ixQMgrQIsConfigured(qId))
1332 {
1333 return IX_QMGR_Q_NOT_CONFIGURED;
1334 }
1335 if (qId >= IX_QMGR_MIN_QUEUPP_QID)
1336 {
1337 return IX_QMGR_PARAMETER_ERROR;
1338 }
1339 if(type == NULL)
1340 {
1341 return IX_QMGR_PARAMETER_ERROR;
1342 }
1343#endif
1344
1345 *type = ixQMgrQTypes[qId];
1346 return IX_SUCCESS;
1347}