/****************************************************************************
 * Ralink Tech Inc.
 * Taiwan, R.O.C.
 *
 * (c) Copyright 2002, Ralink Technology, Inc.
 *
 * All rights reserved. Ralink's source code is an unpublished work and the
 * use of a copyright notice does not imply otherwise. This source code
 * contains confidential trade secret material of Ralink Tech. Any attemp
 * or participation in deciphering, decoding, reverse engineering or in any
 * way altering the source code is stricitly prohibited, unless the prior
 * written consent of Ralink Technology, Inc. is obtained.
 ***************************************************************************/

/****************************************************************************

	Abstract:

	All related WMM ACM common (AP/STA) function body.

	History:
		1. 2009/08/26	Sample Lin
			(1) Move ACM_APSD_Ctrl() to ACM_TC_Destroy()
			(2) Do not delete TSPEC if DELTS is not sent to STA when STA
				is in Power Save mode, i.e. UAPSD mode.
			(3) Duplicate action frames to legacy PS queue
				in AP_QueuePsActionPacket if UAPSD of VO is enabled,
				not all ACs are UAPSD mode and STA is in PS mode.
		2. 2009/09/29	Sample Lin
			(1) Improve WMM ACM TCLAS add command in acm_iocl.c.
			(2) No need check timeout if timeout function is disabled in
				ACMP_DataNullHandle().
			(3) No need TCLAS UP check in ACMP_DataNullHandle().
		3. 2009/11/09	Sample Lin
			(1) Add some TODO notes in the fucture.
		4. 2009/12/15	Sample Lin
			(1) Fix bugs in CPU 64bit. Can not use (UINT32)pAd.
		5. 2009/12/22	Sample Lin
			(1) Fix bugs for big-endian mode in acm_extr.h.
		6. 2010/01/07	Sample Lin
			(1) Add function: ACL list. (admit control list)
				Only accept ACM request in the list.
			(2) Add function: Aggregation (AMSDU)
			(3) Fix bug: LenDataId --
				No need to do "LenDataId --" when getting LenDataId.
				EX: Data Size = 64, LenDataId will be 64 >> 5 = 2
				So the tx time of 64B will be same as the tx time of 95B.
				(64+32-1 = 95B)
		7. 2010/01/25	Sample Lin
			(1) Fix bug: Minimum physical rate infinite loop when rate is
				not one of supported rates.
		8. 2010/02/20	Sample Lin
			(1) Fix bug: Minimum physical rate of TSPEC must meet support
				rate list, but the rule is error for 5.5Mbps.
			(2) Fix bug: Bandwidth check for bi-directional TSPEC replacement.
				The problem will occur when the available ACM capacity is less.
			(3) Fix bug: Only station mode, update QBSS Load from AP.
				The problem will only influence the OBSS load element in beacon.
		9. 2010/06/10	Sample Lin
			(1) Fix bug: In RTMPDeQueuePacket, after calling ACMP_MsduClassify,
				if we decide to discard the packet, we also need to do unlock.
		10. 2010/07/07	Sample Lin
			(1) Fix bug: Improve/Add spin lock to protect some resources.
		11. 2010/09/03	Sample Lin
			(1) Fix bug: In AP mode, need to check ERP element for RTS/CTS
				calculation.
		12. 2010/09/10	Sample Lin
			(1) Fix bug: In AP mode, when station sends a TSPEC without correct
				physical rate, we will re-select the wrong rate.
		12. 2010/09/23	Sample Lin
			(1) Add 11n AMSDU and AMPDU TSPEC based on
				WMM_Specification_1.2-wmmac-11n-100622-mgr.doc.

***************************************************************************/

#ifdef RELEASE_EXCLUDE
/*
	Code Size Information in PC (Debian 2.6.18-6-686):
	1. No ACM Module
		text	data	bss		dec		hex		filename
		393045	25088	4188	422321	671b1	os/linux/rt2860ap.ko

		Size = 0, Data = 0

	2. ACM Module (includes Simulation Code & Debug)
		text	data	bss		dec		hex		filename
		448015	25196	31276	504487	7b2a7	os/linux/rt2860ap.ko

		Size = 54970B, Data = 27088B

	3. ACM Module (includes Debug)
		text	data	bss		dec		hex		filename
		438303	25184	31016	494503	78ba7	os/linux/rt2860ap.ko

		Size = 45258B, Data = 26828B

	4. ACM Module
		text	data	bss		dec		hex		filename
		295407	25024	4188	324619	4f40b	os/linux/rt2860ap.ko

		text	data	bss		dec		hex		filename
		331018	25120	31016	387154	5e852	os/linux/rt2860ap.ko

		Size = 35611B, Data = 26828B
*/

/*
	Performance Impact:
		Rate (from window station UI): 270M/270M
		Chariot: 2 uplinks + 2 dnlinks, TCP, high throughput script
		Window Station: v3.0.99.159/ v1.4.1.42/ v0.1.0.146/ v1.1/ v0.19
		Platform: 5VT (as below)

			Processor       : ARM926EJ-Sid(wb) rev 5 (v5l)
			BogoMIPS        : 285.90
			Features        : swp half fastmult edsp java
			CPU implementer : 0x41
			CPU architecture: 5TEJ
			CPU variant     : 0x0
			CPU part        : 0x926
			CPU revision    : 5
			Cache type      : write-back
			Cache clean     : cp15 c7 ops
			Cache lockdown  : format C
			Cache format    : Harvard
			I size          : 16384
			I assoc         : 4
			I line length   : 32
			I sets          : 128
			D size          : 16384
			D assoc         : 4
			D line length   : 32
			D sets          : 128

			Hardware        : 5VT13XX
			Revision        : 0000
			Serial          : 0000000000000000

	1. WMM ACM included (no any ACM of AC is enabled)
		BE throughput impact: 0 ~ 1Mbps (117Mbps)
		(2 uplinks: 44.1M; 2 dnlinks: 14.8M)

	2. WMM ACM included (all ACM of AC are enabled but no any TSPEC)
		BE throughput impact: 0 ~ 1Mbps (117Mbps)

	3. WMM ACM included (ACM of VO/VI are enabled and a bi-VOICE TSPEC)
		BE throughput impact: 1 ~ 2Mbps (116Mbps)

	4. WMM ACM included (all ACM of AC are enabled and a bi-BE TSPEC)
		a. No statistics count/ only ACM when queuing data/ rough tx time
			BE throughput impact: 2 ~ 13Mbps (average 105Mbps)

		b. No statistics count/ only ACM when queuing data/ accurate tx time
			BE throughput impact: 2 ~ 16Mbps (average 102Mbps)
			(Text Size: 445257B - 393045B = 52212B)
			(BSS Size: 4716B - 4188B = 528B)

		c. No statistics count/ ACM when queuing data and tx/ rough tx time
			BE throughput impact: 2 ~ 19Mbps (average 99Mbps)

		d. No statistics count/ ACM when queuing data and tx/ accurate tx time
			BE throughput impact: 2 ~ 23Mbps (average 95Mbps)

		e. Statistics count/ ACM when queuing data and tx/ accurate tx time
			BE throughput impact: 2 ~ 23Mbps (average 95Mbps)
*/
#endif /* RELEASE_EXCLUDE */

#ifdef RELEASE_EXCLUDE
/*
							WMM ACM Design Note

	Protection:
		Any ACMP_xxx function, we must do protection in these functions except
		you can sure no any ACM resouce is accessed.
		We do no need to do protection in other local functions.


	Task:
		ACM_TASK_General
			responsible for multiple BSS ACM function or
			chan utilization monitor function
		ACM_TASK_STM_Check
			responsible for TSPEC timeout monitor function
		ACM_TASK_TC_ReqCheck
			responsible for TSPEC request timeout monitor function (STA-only)


	Management Action Frame Input Flow:
		1. common/action.c		PeerWMMAction
		2. common/acm_comm.c	ACMP_ManagementHandle
								ACM_ActionHandleByQAP or ACM_ActionHandleByQSTA
		3. common/acm_edca.c	ACM_WME_ActionHandle
		4. common/acm_comm.c	ACM_TC_ReqHandle, ACM_TC_RspHandle, or
								ACM_TC_DestroyBy_TS_Info


	TSPEC Request Flow in STA mode:
		ACMP_WME_TC_Request()	: Send a new TSPEC to the AP

		When STA is in Power Save mode, we have two methods to send the TSPEC
		Request frame by ACM_CC_FUNC_PS_MGMT_FME:

		1. Send Request in ACTIVE mode (default)
			(1) ACM_PS_ActiveOn, let STA enters ACTIVE mode temporarily;
			(2) Send the TSPEC Request Frame;
			(3) Wait for the TSPEC Response Frame or timeout;
			(4) ACMP_StaPsCtrlRightReturn, let STA restores to old mode.

			Note: Maybe many TSPEC Request Frames are sent simultaneously so
				ACMP_StaPsCtrlRightReturn is NOT put in ACM_TC_RspHandle.
				We will call it when no any request is pending.

		2. Send Request in Power Save mode (not recommendation in WiFi test plan)
			(1) Set PM bit of 802.11 header in the TSPEC Request Frame;
			(2) Send the TSPEC Request Frame;
			(3) Send any trigger frame or PS-Poll frame to get TSPEC Response
				Frame based on dynamic or static power save mode.

		Note: Currently we do not care about replacement rules defined in
			WiFi test plan so we can send a request with any set (TID vs UP).


	TSPEC Negotiate Flow in STA mode:
		ACMP_TC_Renegotiate()	: Send a negotiation TSPEC to the AP

		Power Save behavior is same as "TSPEC Request Flow in STA mode".

		1. If negotiation fail, we must not delete original TSPEC.
		2. If negotiation success, we must replace original TSPEC with the one.


	TSPEC Response Flow in AP mode:
		ACM_TC_ReqHandle		Handle a TSPEC Request/Negotiation

		1. STA is in ACTIVE mode
			Send the response directly.

		2. STA is in POWER SAVE mode
			(1) If UAPSD of VO is not enabled, queue the response frame in
				Legacy-PS queue.
			(2) If UAPSD of all ACs are enabled, queue the response frame in
				UAPSD queue.
			(3) If UAPSD of VO is enabled, queue the response frame in
				UAPSD and Legacy-PS queue.

		Note: In Spec., we can not put the Power-Save action frame in
			Legacy-PS queue but this is dangerous that it is possible that
			AP can not delete TSPEC actively.


	UAPSD State Check Point: (non-3*3 chips)
		1. AP mode
			STA is in ACTIVE mode:
			(1) Correct Behavior (need hardware support)
				Receive a TSPEC request
				Send a TSPEC response
				Receive the ACK of TSPEC response		==> Change UAPSD state

			(2) Current Behavior
				Receive a TSPEC request
				Send a TSPEC response					==> Change UAPSD state
				Receive the ACK of TSPEC response

			STA is in POWER-SAVE mode:
			(1) Correct Behavior (need hardware support)
				Receive a TSPEC request
				Queue a TSPEC response
				Receive a trigger frame or PS-Poll frame
				Send a TSPEC response
				Receive the ACK of TSPEC response		==> Change UAPSD state

			(2) Current Behavior
				Receive a TSPEC request
				Queue a TSPEC response
				Receive a trigger frame or PS-Poll frame
				Send a TSPEC response					==> Change UAPSD state
				Receive the ACK of TSPEC response

		2. STA mode
			(1) Correct Behavior
				Send a TSPEC request
				Receive a TSPEC response				==> Change UAPSD state

			(2) Current Behavior
				Send a TSPEC request
				Receive a TSPEC response				==> Change UAPSD state

	11N Operation:
		1. We calculate medium time whatever AMPDU or AMSDU in AP device.
			(1) Because we can not predict the MPDU number in a AMPDU or AMSDU.

			(2) When we use AMPDU to do transmission, maybe many bandwidth
				is wasted, but the bandwidth can be for BE/BK traffic.

		2. Parameters
			802.11 MAC header	Header	AES / TKIP Info
			FC					2							bytes
			Duration			2
			Addr 1				6
			Addr 2				6
			Addr 3				6
			Sequence control	2
			QoS control			2
			FCS					4

			Security
			AES / TKIP Header	8		8
			MIC					8		8
			TKIP ICV			0		4
			Total				46		50

			SNAP header			8
			IP header			20
			UDP header			8
			RTP header			12
			Total				48

					Normal GI		Short GI
			MCS		20		40		20		40
			0		6.5		13.5	7.2		15.0			Mbps
			1		13.0	27.0	14.4	30.0
			2		19.5	40.5	21.7	45.0
			3		26.0	54.0	28.9	60.0
			4		39.0	81.0	43.3	90.0
			5		52.0	108.0	57.8	120.0
			6		58.5	121.5	65.0	135.0
			7		65.0	135.0	72.2	150.0
			8		13.0	27.0	14.4	30.0
			9		26.0	54.0	28.9	60.0
			10		39.0	81.0	43.3	90.0
			11		52.0	108.0	57.8	120.0
			12		78.0	162.0	86.7	180.0
			13		104.0	216.0	115.6	240.0
			14		117.0	243.0	130.0	270.0
			15		130.0	270.0	144.4	300.0
			16		19.5	40.5	21.7	45.0
			17		39.0	81.0	43.3	90.0
			18		58.5	121.5	65.0	135.0
			19		78.0	162.0	86.7	180.0
			20		117.0	243.0	130.0	270.0
			21		156.0	324.0	173.3	360.0
			22		175.5	364.5	195.0	405.0
			23		195.0	405.0	216.7	450.0
			24		26.0	54.0	28.9	60.0
			25		52.0	108.0	57.8	120.0
			26		78.0	162.0	86.7	180.0
			27		104.0	216.0	115.6	240.0
			28		156.0	324.0	173.3	360.0
			29		208.0	432.0	231.1	480.0
			30		234.0	486.0	260.0	540.0
			31		260.0	540.0	288.9	600.0

									802.11b	802.11g	802.11a	802.11n-1SS	units
			Preamble				72		16		16		36			us
			Greenfield Preamble		24									us
			Long Preamble			144									us
			PLCP header				24		4		4					us
			Long PLCP				48									us
			Signal Extension		0		6		0					us
			SIFS					10		10		16					us
			Slot					20		20		9					us
			DIFS					50		50		34		same as a/g	us
			EIFS					268		99		89		same as a/g	us
			ACK						14		14		14		same as a/g	bytes
			CTS						14		14		14					bytes
			RTS						20		20		20					bytes
			802.11 MAC				46		46		46					bytes
			A-MSDU header			14	bytes (does not include padding)
			A-MPDU header			4	bytes (does not include padding)
			Block ACK				32	bytes (compressed bitmap)

			ht-sig		8	us
			ht-gf-stf	8	us
			ht-ltf1		4/8	us (4 more us in GF mode)
			ht-lft2		4	us
			ht-stf		4	us
			l-sig		4	us
			l-stf		8	us
			l-ltf		8	us
			mm			36	us
			gf			24	us

								<< Raw ACK Times >>
			802.11b								
			Long Preamble						Short Preamble
			Length | Rate	11	5.5	2	1		11	5.5	2	1		Units
			14				200	210	250	300		110	120	150	210 	us

			802.11a
			Length | Rate	54	48	36	24	18	12	9	6			Units
			14	 			28	28	28	28	28	32	36	44 			us

			802.11g
			Length | Rate	54	48	36	24	18	12	9	6			Units
			14	 			34	34	34	34	34	38	42	50 			us

								<< CTS+SIFS Times >>
			802.11b
			Long Preamble						Short Preamble
			Length | Rate	11	5.5	2	1		11	5.5	2	1		Units
			14	 			212 222 258 314 	116 126 162 218 	us

			802.11a
			Length | Rate	54	48	36	24	18	12	9	6
			14	 			44	44	44	44	44	48	52	60 			us

			802.11g
			Length | Rate	54	48	36	24	18	12	9	6
			14	 			44	44	44	44	44	48	52	60 			us
									
								<< RTS+SIFS Times >>
			802.11b
			Long Preamble						Short Preamble
			Length | Rate	11	5.5	2	1		11	5.5	2	1		Units
			20				217	231	282	362		121	135	186	266 	us

			802.11a
			Length | Rate	54	48	36	24	18	12	9	6
			20	 			44	44	44	44	48	52	60	68 			us

			802.11g								
			Length | Rate	54	48	36	24	18	12	9	6
			20	 			44	44	44	44	48	52	60	68 			us

		3. Fomula
*/
#endif /* RELEASE_EXCLUDE */


#include "rt_config.h"

//#define PERFORMANCE_IMPACT_TEST /* only for test */
//#define WMM_ACM_FUNC_DEBUG /* only for function hang debug */

#ifdef WMM_ACM_SUPPORT

/* IEEE802.11E related include files */
#include "acm_extr.h" /* used for other modules */
#include "acm_comm.h" /* used for edca/wmm */
#include "acm_edca.h" /* used for edca/wmm */


#ifdef WMM_ACM_FUNC_DEBUG
#define WMM_ACM_FUNC_NAME_PRINT(__pMsg)	\
	ACMR_DEBUG(ACMR_DEBUG_ERR, ("acm_func> %s: %s\n", __FUNCTION__, __pMsg));

#else
#define WMM_ACM_FUNC_NAME_PRINT(__pMsg)
#endif /* WMM_ACM_FUNC_DEBUG */

/* ----- Extern Variable ----- */
/* other WLAN modules */
extern VOID BA_MaxWinSizeReasign(
	IN PRTMP_ADAPTER	pAd,
	IN MAC_TABLE_ENTRY  *pEntryPeer,
	OUT UCHAR			*pWinSize);


/* from EDCA module (acm_edca.c) */
extern UCHAR gEDCA_UP_AC[];		/* EDCA Priority vs. AC */
extern UCHAR gEDCA_AC_UP[];		/* EDCA AC vs. Priority */
extern UCHAR gEDCA_UP_DSCP[];	/* DSCP vs. Priority */


/* ----- Private Variable ----- */

/* TCLAS related */
UCHAR gTCLAS_Elm_Len[3] = {	ACM_TCLAS_TYPE_WME_ETHERNET_LEN,
							ACM_TCLAS_TYPE_WME_IP_V4_LEN,
							ACM_TCLAS_TYPE_WME_8021DQ_LEN };

UCHAR gAcmTestFlag = 0; /* used for self-test */

#ifdef RELEASE_EXCLUDE
/* Frame TX Rate and TX Time */
/* we pre-calculate transmission time here so we can use the pre-known time to
	get the frame tx time on the fly */
#endif /* RELEASE_EXCLUDE */
static const UCHAR gAcmRateLegacy[4] =
	{	ACM_RATE_11M, ACM_RATE_5_5M, ACM_RATE_2M, ACM_RATE_1M};
static const UCHAR gAcmRateG[8] =
	{	ACM_RATE_54M, ACM_RATE_48M, ACM_RATE_36M, ACM_RATE_24M,
		ACM_RATE_18M, ACM_RATE_12M, ACM_RATE_9M, ACM_RATE_6M};

/* cck & ofdm MCS */
#define ACM_RATE_UNIT		((UINT32)100000)	/* 100000bps */
#define ACM_CCK_LPM_MIN_MCS		0
#define ACM_CCK_LPM_MAX_MCS		3
#define ACM_CCK_SPM_MIN_MCS		8
#define ACM_CCK_SPM_MAX_MCS		11

UINT8 gAcmMCS_CCK[2][4][2] =
	{
		/* b mode, [2]: long preamble and short preamble */
		/* unit: 100000bps */
		{ { 0, 10 }, { 1, 20 }, { 2, 55 }, { 3, 110 } },
		{ { 8, 10 }, { 9, 20 }, { 10, 55 }, { 11, 110 } }
	};

UINT16 gAcmMCS_OFDM[8][2] =
	{
		/* g mode */
		/* unit: 100000bps */
		{ 0, 60 }, { 1, 90 }, { 2, 120 }, { 3, 180 },
		{ 4, 240 }, { 5, 360 }, { 6, 480 }, { 7, 540 }
	};

#ifndef ACM_CC_FUNC_AUX_TX_TIME
/* use approximation method to calculate packet transmission time */

#ifdef RELEASE_EXCLUDE
/* tx time for frame body, length = 1 ~ 1536 */
#endif /* RELEASE_EXCLUDE */
static UINT16 gAcmTxTimeBody[ACM_RATE_MAX_NUM][ACM_PRE_TIME_DATA_SIZE_NUM];

#ifdef RELEASE_EXCLUDE
/* tx time for cts-self, rts/cts, header, ack */
/*
	1st []: rate id is such as ACM_RATE_ID_1M, ...
	2nd []: 0 (long preamble), 1 (short preamble)
	3rd []: 0 (rate id) 1 (cts-self tx time) 2 (rts/cts tx time)
			3 (header time) 4 ack tx time
*/
#endif /* RELEASE_EXCLUDE */
static UINT16 gAcmTxTimeOthers[ACM_RATE_MAX_NUM][2][5];
#endif /* ACM_CC_FUNC_AUX_TX_TIME */

#ifdef ACM_CC_FUNC_11N
#ifdef RELEASE_EXCLUDE
/* [0] for 20 or 40MHz; [1] for Regular or Short GI; [2] for MCS */
#endif /* RELEASE_EXCLUDE */
static const UINT16 gAcmMCS_HT[2][2][32] =
	{
		/* 20MHz */
		{
			/* Regular GI */
			/* MCS0 ~ MCS31: (unit 100000bps) */
			{
				 65,  130,   195,  260,  390,  520, 585,  650,  130,  260,
				390,  520,   780, 1040, 1170, 1300, 195,  390,  585,  780,
				1170, 1560, 1755, 1950,  260,  520, 780, 1040, 1560, 2080,
				2340, 2600
			},

			/* Short GI */
			/* MCS0 ~ MCS31: (unit 100000bps) */
			{
				 72,   144,  217,  289,  433,  578, 650,  722,  144,  289,
				433,   578,  867, 1156, 1300, 1444, 217,  433,  650,  867,
				1300, 1733, 1950, 2167, 2890,  578, 867, 1156, 1733, 2311,
				2600, 2889
			},
		},

		/* 40MHz */
		{
			/* Regular GI */
			/* MCS0 ~ MCS31: (unit 100000bps) */
			/*
				In G mode, also have 54Mbps. when your minimum physical rate
				is 54Mbps, we will regard the rate as non-11n rate.
			*/
			{
				 135,  270,  405,  540,  810, 1080, 1215, 1350,  270,  540,
				 810, 1080, 1620, 2160, 2430, 2700,  405,  810, 1215, 1620,
				2430, 3240, 3645, 4050,  540, 1080, 1620, 2160, 3240, 4320,
				4860, 5400
			},

			/* Short GI */
			/* MCS0 ~ MCS31: (unit 100000bps) */
			{
				 150,  300,  450,  600,  900, 1200, 1350, 1500,  300,  600,
				 900, 1200, 1800, 2400, 2700, 3000,  450,  900, 1350, 1800,
				2700, 3600, 4050, 4500,  600, 1200, 1800, 2400, 3600, 4800,
				5400, 6000
			},
		},
	};

#ifdef RELEASE_EXCLUDE
/* reference to Table20-29 ~ Table20-35 of Draft802.11n_D3.07.pdf */
#endif /* RELEASE_EXCLUDE */
static const UINT16 gAcmRateNdbps[2][32] =
	{
		/* MCS0 ~ MCS31 */
		/* 20MHz */
		{
			 26,  52,  78, 104, 156, 208, 234,  260,
			 52, 104, 156, 208, 312, 416, 468,  520,
			 78, 156, 234, 312, 468, 624, 702,  780,
			104, 208, 312, 416, 624, 832, 936, 1040
		},

		/* MCS0 ~ MCS31 */
		/* 40MHz */
		{
			 54, 108, 162, 216,  324,  432,  486,  540,
			108, 216, 324, 432,  648,  864,  972, 1080,
			162, 324, 486, 648,  972, 1296, 1458, 1620,
			216, 432, 648, 864, 1296, 1728, 1944, 2160
		},
	};

#ifdef RELEASE_EXCLUDE
/* reference to Table20-29 ~ Table20-35 of Draft802.11n_D3.07.pdf */
/* where 1:mean Nes = 2 for the MCS; 0:mean Nes=1 for the MCS */
/* [0] for 20MHz, [1] for 40MHz */
#endif /* RELEASE_EXCLUDE */
static const UINT32 gAcmRateNes[2] = { 0x00000000, 0xF0E00000 };

#ifndef ACM_CC_FUNC_AUX_TX_TIME

#ifdef RELEASE_EXCLUDE
/* tx time for header+frame body, length = 1 ~ 1536 */
/*
	[2]: 20 or 20/40MHz
	[2]: regular or short GI
	[ACM_RATE_MAX_NUM_HT]: 0 ~ 31
	[ACM_PRE_TIME_DATA_SIZE_NUM]: 0 ~
	[2]: 0 (tx time includes preamble, data, block ack)
		 1 (only header+data tx time)
		 2 (only data tx time)
*/
#endif /* RELEASE_EXCLUDE */

static UINT16 gAcmTxTimeBodyHT[2][2][ACM_RATE_MAX_NUM_HT][ACM_PRE_TIME_DATA_SIZE_NUM][3];

/* tx time for block ack whatever 20/40 or GI */
static UINT16 gAcmTxTimeOthersHT;

#endif /* ACM_CC_FUNC_AUX_TX_TIME */

#endif /* ACM_CC_FUNC_11N */

/* for memory allocation/free test purpose */
#ifdef ACM_MEMORY_TEST
UINT32 gAcmMemAllocNum = 0;
UINT32 gAcmMemFreeNum = 0;
#endif /* ACM_MEMORY_TEST */




/* =========================== Global Function (AP) ========================= */

/*
========================================================================
Routine Description:
	Initialize the ACM Module.

Arguments:
	pAd				- WLAN control block pointer
	FlgIsAcm0Enable	- the ACM flag for AC0
	FlgIsAcm1Enable	- the ACM flag for AC1
	FlgIsAcm2Enable	- the ACM flag for AC2
	FlgIsAcm3Enable	- the ACM flag for AC3
	FlgVb			- the variable bandwidth flag

Return Value:
	ACM_RTN_OK		- init OK
	ACM_RTN_FAIL	- init fail

Note:
	FlgIsAcm0Enable ~ FlgIsAcm3Enable and FlgVb are valid only for QAP mode.
========================================================================
*/
ACM_FUNC_STATUS ACMP_Init(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	UCHAR					FlgIsAcm0Enable,
	ACM_PARAM_IN	UCHAR					FlgIsAcm1Enable,
	ACM_PARAM_IN	UCHAR					FlgIsAcm2Enable,
	ACM_PARAM_IN	UCHAR					FlgIsAcm3Enable,
	ACM_PARAM_IN	UCHAR					FlgVb)
{
	ACM_CTRL_PARAM *pEdcaParam;


	/* allocate ACM Control Block memory */
	ACMR_MEM_ALLOC(ACMR_ADAPTER_DB, sizeof(ACM_CTRL_BLOCK), (VOID *));
	if (ACMR_ADAPTER_DB == NULL)
		return ACM_RTN_FAIL;
	/* End of if */
	ACMR_MEM_ZERO(ACMR_ADAPTER_DB, sizeof(ACM_CTRL_BLOCK));

	/* init tasklets */
	/* Note: pAd can not be casted to non-ULONG, ex: (UINT32)pAd */
	ACMR_TASK_INIT(pAd, ACMR_CB->TaskletTspecReqCheck,
					ACM_TASK_TC_ReqCheck, pAd, "ACM_TCREQ");

	ACMR_TASK_INIT(pAd, ACMR_CB->TaskletStreamAliveCheck,
					ACM_TASK_STM_Check, pAd, "ACM_AVCK");

	/* init timers */
	ACMR_TIMER_INIT(pAd, ACMR_CB->TimerTspecReqCheck,
					ACMP_TR_TC_ReqCheck, pAd);

	ACMR_TIMER_INIT(pAd, ACMR_CB->TimerStreamAliveCheck,
					ACMP_TR_STM_Check, pAd);

	/* init other parameters */
	pEdcaParam = &ACMR_CB->EdcaCtrlParam;

	/* enable channel busy time calculation */
	pEdcaParam->FlgIsChanUtilEnable = 1;
	ACMR_CHAN_BUSY_DETECT_ENABLE(pAd);

#ifdef CONFIG_AP_SUPPORT
	if (ACMR_IS_AP_MODE(pAd))
	{
		/* init available ACM time */
		ACMR_AVAIL_ACM_TIME_UPDATE(pAd, 0);
	} /* End of if */
#endif /* CONFIG_AP_SUPPORT */

	/* init chan utilization */
	pEdcaParam->ChanUtil = 0;

	/* init mininum time for total EDCA streams and AC0/1 streams */
	/* use default value */
	pEdcaParam->CP_MinNu = ACM_MIN_CP_NU_DEFAULT;
	pEdcaParam->CP_MinDe = ACM_MIN_CP_DE_DEFAULT;

	/* use default value */
	pEdcaParam->BEK_MinNu = ACM_MIN_BEK_NU_DEFAULT;
	pEdcaParam->BEK_MinDe = ACM_MIN_BEK_DE_DEFAULT;

	/* default TSPEC can change UAPSD settings */
	pEdcaParam->FlgIsTspecUpasdEnable = 1;

	/* init Downgrade function (default: DISABLE) */
	ACMR_MEM_SET(
				pEdcaParam->DowngradeAcNum,
				ACM_DOWNGRADE_DISABLE,
				sizeof(pEdcaParam->DowngradeAcNum));

#ifdef CONFIG_AP_SUPPORT
	if (ACMR_IS_AP_MODE(pAd))
	{
		/* init ACM flag in QAP; QSTA will follow the ACM indication in beacon */
		pEdcaParam->FlgAcmStatus[0] = FlgIsAcm0Enable;
		pEdcaParam->FlgAcmStatus[1] = FlgIsAcm1Enable;
		pEdcaParam->FlgAcmStatus[2] = FlgIsAcm2Enable;
		pEdcaParam->FlgAcmStatus[3] = FlgIsAcm3Enable;

		ACMR_AC_ACM_CTRL(pAd,
						FlgIsAcm0Enable,
						FlgIsAcm1Enable,
						FlgIsAcm2Enable,
						FlgIsAcm3Enable);

#ifdef RELEASE_EXCLUDE
		/* dynamic ATL: init DATL */
		pEdcaParam->FlgDatl = FlgVb;
#endif /* RELEASE_EXCLUDE */

#ifdef ACM_CC_FUNC_ACL
		/* init link list */
		ACMR_CB->ACL_IsEnabled = FALSE;
		ACMR_LIST_INIT(&ACMR_CB->ACL_List);
#endif /* ACM_CC_FUNC_ACL */
	} /* End of if */
#endif /* CONFIG_AP_SUPPORT */

#ifdef RELEASE_EXCLUDE
	/* dynamic ATL: init DATL */
	pEdcaParam->DatlBwMin[ACM_EDCA_VO_AC_QUE_ID] = ACM_DATL_BW_MIN_VO;
	pEdcaParam->DatlBwMax[ACM_EDCA_VO_AC_QUE_ID] = ACM_DATL_BW_MAX_VO;
	pEdcaParam->DatlBwMin[ACM_EDCA_VI_AC_QUE_ID] = ACM_DATL_BW_MIN_VI;
	pEdcaParam->DatlBwMax[ACM_EDCA_VI_AC_QUE_ID] = ACM_DATL_BW_MAX_VI;
	pEdcaParam->DatlBwMin[ACM_EDCA_BE_AC_QUE_ID] = ACM_DATL_BW_MIN_BE;
	pEdcaParam->DatlBwMax[ACM_EDCA_BE_AC_QUE_ID] = ACM_DATL_BW_MAX_BE;
	pEdcaParam->DatlBwMin[ACM_EDCA_BK_AC_QUE_ID] = ACM_DATL_BW_MIN_BK;
	pEdcaParam->DatlBwMax[ACM_EDCA_BK_AC_QUE_ID] = ACM_DATL_BW_MAX_BK;
#endif /* RELEASE_EXCLUDE */

	/* init Transmission Time value for different packet size */
	ACM_TX_TimeCalPre(pAd);

	/* init cmd */
	ACM_CMD_Init(pAd);

#ifdef CONFIG_STA_SUPPORT
	if (ACMR_IS_STA_MODE(pAd))
	{
		/* backup original UAPSD state */
		ACMR_UAPSD_BACKUP(pAd);
	} /* End of if */
#endif /* CONFIG_STA_SUPPORT */

	/* allow to handle TSPEC request */
	ACM_MR_TSPEC_ALLOW();

	/* activate a general timer */
	ACMR_TASK_INIT(pAd, ACMR_CB->TaskletGeneral,
					ACM_TASK_General, (ULONG)pAd, "ACM_OTHER");

	ACMR_TIMER_INIT(pAd, ACMR_CB->TimerGeneral,
					ACMP_TR_TC_General, (ULONG)pAd);

#if defined(ACM_CC_FUNC_MBSS) || defined(ACM_CC_FUNC_CHAN_UTIL_MONITOR)
	ACMR_TIMER_ENABLE(ACMR_CB->FlgTimerGeneralEnable,
						ACMR_CB->TimerGeneral,
						ACM_TIMER_GENERAL_PERIOD_TIMEOUT);
#endif /* ACM_CC_FUNC_MBSS || ACM_CC_FUNC_CHAN_UTIL_MONITOR */

#ifdef CONFIG_AP_SUPPORT
	if (ACMR_IS_AP_MODE(pAd))
	{
#ifdef ACM_CC_FUNC_CHAN_UTIL_MONITOR
		/* backup original AIFSN of AC */
		ACMR_AIFSN_DEFAULT_GET(pAd, ACMR_CB->CU_MON_AifsnAp,
								ACMR_CB->CU_MON_AifsnBss);
#endif /* ACM_CC_FUNC_CHAN_UTIL_MONITOR */
	} /* End of if */
#endif /* CONFIG_AP_SUPPORT */

	return ACM_RTN_OK;
} /* End of ACMP_Init */


/*
========================================================================
Routine Description:
	Release the ACM Resource.

Arguments:
	pAd				- WLAN control block pointer

Return Value:
	ACM_RTN_OK		- release OK
	ACM_RTN_FAIL	- release fail

Note:
	Only used in module remove.
========================================================================
*/
ACM_FUNC_STATUS ACMP_Release(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd)
{
	/* sanity check */
	if (ACMR_CB == NULL)
		return ACM_RTN_OK;
	/* End of if */

	/* release all resources */
	ACMR_TIMER_DISABLE(ACMR_CB->FlgStreamAliveCheckEnable,
						ACMR_CB->TimerStreamAliveCheck);
	ACMR_TIMER_DISABLE(ACMR_CB->FlgTspecReqCheckEnable,
						ACMR_CB->TimerTspecReqCheck);
	ACMR_TIMER_DISABLE(ACMR_CB->FlgTimerGeneralEnable,
						ACMR_CB->TimerGeneral);

#ifdef CONFIG_AP_SUPPORT
	if (ACMR_IS_AP_MODE(pAd))
	{
#ifdef ACM_CC_FUNC_ACL
		ACM_ACL_EMPTY(pAd);
#endif /* ACM_CC_FUNC_ACL */
	} /* End of if */
#endif /* CONFIG_AP_SUPPORT */

	ACM_TC_ReleaseAll(pAd);
	ACM_CMD_Release(pAd);

	ACMR_MEM_FREE(ACMR_ADAPTER_DB);

	ACMR_ADAPTER_DB = NULL;

#ifdef ACM_MEMORY_TEST
	ACMR_DEBUG(ACMR_DEBUG_ERR,
				("acm_msg> ACM_MEM_Alloc_Num = %d\n", gAcmMemAllocNum));
	ACMR_DEBUG(ACMR_DEBUG_ERR,
				("acm_msg> ACM_MEM_Free_Num  = %d\n", gAcmMemFreeNum));
#endif /* ACM_MEMORY_TEST */

	return ACM_RTN_OK;
} /* End of ACMP_Release */


/*
========================================================================
Routine Description:
	Get bandwidth information.

Arguments:
	pAd				- WLAN control block pointer
	*pInfo			- the bandwidth information

Return Value:
	ACM_RTN_OK		- get ok
	ACM_RTN_FAIL	- get fail

Note:
========================================================================
*/
ACM_FUNC_STATUS ACMP_BandwidthInfoGet(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC			pAd,
	ACM_PARAM_OUT	ACM_BANDWIDTH_INFO			*pInfo)
{
	ACM_CTRL_PARAM *pEdcaParam;
	ULONG SplFlags;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* sanity check */
	if ((pAd == NULL) || (pInfo == NULL))
	{
		ACMR_DEBUG(ACMR_DEBUG_TRACE,
					("acm_msg> Input is NULL! ControlInfomationGet()\n"));
		return ACM_RTN_FAIL;
	} /* End of if */

	/* get management semaphore */
	ACM_TSPEC_SEM_LOCK_CHK_RTN(pAd, SplFlags, LabelSemErr, ACM_RTN_FAIL);

	/* total ACM time = EDCA ACM time + HCCA ACM time (HCCA not support) */
	pEdcaParam = &(ACMR_CB->EdcaCtrlParam);
	pInfo->AcmUsedTime = pEdcaParam->AcmTotalTime;

#ifdef ACM_CC_FUNC_MBSS
	/* MBSS time */
	pInfo->MbssTotalUsedTime = ACMR_CB->MbssTotalUsedTime;
#endif /* ACM_CC_FUNC_MBSS */

	/* EDCA AC ACM time */
	pInfo->AcUsedTime = pEdcaParam->AcmTotalTime;

	/* undetermined TSPEC number */
	pInfo->NumReqLink = ACMR_CB->TspecListReq.TspecNum;

	/* determined EDCA link number */
	pInfo->NumAcLinkUp = pEdcaParam->LinkNumUp;
	pInfo->NumAcLinkDn = pEdcaParam->LinkNumDn;
	pInfo->NumAcLinkDi = pEdcaParam->LinkNumDi;
	pInfo->NumAcLinkBi = pEdcaParam->LinkNumBi;

	/* channel utilization & busy time */
#ifdef CONFIG_AP_SUPPORT
	if (ACMR_IS_AP_MODE(pAd))
	{
		pEdcaParam->StationCount = ACMR_STATION_COUNT_GET(pAd);
		pEdcaParam->ChanUtil = ACMR_CHAN_UTIL_GET(pAd);
	} /* End of if */
#endif /* CONFIG_AP_SUPPORT */

	pInfo->StationCount = pEdcaParam->StationCount;
	pInfo->ChanUtil = pEdcaParam->ChanUtil;
	pInfo->AvalAdmCap = pEdcaParam->AvalAdmCap;

	ACMR_CHAN_BUSY_GET(pAd, &pInfo->ChanBusyTime);

	/* release semaphore */
	ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);
	return ACM_RTN_OK;

LabelSemErr:
	ACMR_SEM_FAIL_PRINT();
	return ACM_RTN_FAIL;
} /* End of ACMP_BandwidthInfoGet */


#ifdef CONFIG_STA_SUPPORT
/*
========================================================================
Routine Description:
	Set bandwidth information.

Arguments:
	pAd				- WLAN control block pointer
	StationCount	- station count of the associated BSS
	ChanUtil		- channel utilization of the associated BSS
	AvalAdmCap		- available admission capability of the associated BSS

Return Value:
	ACM_RTN_OK		- set ok
	ACM_RTN_FAIL	- set fail

Note:
========================================================================
*/
ACM_FUNC_STATUS ACMP_BandwidthInfoSet(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	UINT16					StationCount,
	ACM_PARAM_IN	UINT8					ChanUtil,
	ACM_PARAM_IN	UINT16					AvalAdmCap)
{
	ACM_CTRL_PARAM *pEdcaParam;
	ULONG SplFlags;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* sanity check */
	if (pAd == NULL)
	{
		ACMR_DEBUG(ACMR_DEBUG_TRACE,
					("acm_msg> Input is NULL! ControlInfomationSet()\n"));
		return ACM_RTN_FAIL;
	} /* End of if */

	if (!ACMR_IS_PORT_SECURE(pAd))
		return ACM_RTN_FAIL;
	/* End of if */

	/* get management semaphore */
	ACM_TSPEC_SEM_LOCK_CHK_RTN(pAd, SplFlags, LabelSemErr, ACM_RTN_FAIL);

	/* total ACM time = EDCA ACM time + HCCA ACM time (HCCA not support) */
	pEdcaParam = &(ACMR_CB->EdcaCtrlParam);

	pEdcaParam->StationCount = StationCount;
	pEdcaParam->ChanUtil = ChanUtil; /* unit: 1/255 */
	pEdcaParam->AvalAdmCap = AvalAdmCap; /* unit: 32us */

	/* release semaphore */
	ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);
	return ACM_RTN_OK;

LabelSemErr:
	ACMR_SEM_FAIL_PRINT();
	return ACM_RTN_FAIL;
} /* End of ACMP_BandwidthInfoSet */
#endif /* CONFIG_STA_SUPPORT */


/*
========================================================================
Routine Description:
	Check if the BE packet is needed to release.

Arguments:
	pAd				- WLAN control block pointer
	*pCdb			- the destination QSTA
	QueIdx			- 0 ~ 3 (AC0 ~ AC3)
	*pQueueHeader	- the software queue header
	pMbuf			- the packet expected to send out

Return Value:
	ACM_RTN_OK		- release it
	ACM_RTN_FAIL	- do not release it

Note:
	If we can find a TSPEC for the BE packet, we will search a non-TSPEC
	packet in the BE software queue and release it.
========================================================================
*/
ACM_FUNC_STATUS ACMP_BE_IsReallyToReleaseWhenQueFull(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	ACMR_STA_DB				*pCdb,
	ACM_PARAM_IN	UCHAR					QueIdx,
	ACM_PARAM_IN	ACMR_QUEUE_HEADER		*pQueueHeader,
	ACM_PARAM_IN	ACMR_MBUF				*pMbuf)
{
#ifdef ACM_CC_FUNC_BE_BW_CTRL
	ACM_FUNC_STATUS Status;
	UINT32 QueueType;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* check if the packet is BE and ACM of BE is enabled */
	if ((QueIdx == ACM_EDCA_BE_AC_QUE_ID) &&
		(ACMR_CB->EdcaCtrlParam.FlgAcmStatus[ACM_EDCA_BE_AC_QUE_ID]))
	{
		ACMR_PKT_QOS_TYPE_SET(pMbuf, 0);

		/* check if any TSPEC is built and calculate the tx time */
		Status = ACMP_DataPacketQueue(pAd, pCdb, pMbuf, 0, &QueueType);

		if ((Status == ACM_RTN_OK) &&
			(ACMP_BE_QueueFullHandle(pAd, pCdb, pMbuf) == ACM_RTN_OK))
		{
			/*
				Medium time is enough and another non-TSPEC packet is released
				so we can enqueue the TSPEC packet.
			*/
			return ACM_RTN_FAIL;
		} /* End of if */
	} /* End of if */
#endif /* ACM_CC_FUNC_BE_BW_CTRL */

	return ACM_RTN_OK;
} /* End of ACMP_BE_IsReallyToReleaseWhenQueFull */


/*
========================================================================
Routine Description:
	Handle the event when BE software queue is full.

Arguments:
	pAd				- WLAN control block pointer
	*pCdb			- the destination QSTA
	pMbuf			- the packet expected to send out

Return Value:
	ACM_RTN_OK		- handle ok, a non-TSPEC packet is released
	ACM_RTN_FAIL	- handle fail, no non-TSPEC packet is released

Note:
	If the ACM of BE is enabled and the bandwidth is saturated, we will
	protect the bandwidth for BE traffic with TSPEC.

	Two cases:
	1. (only for AP) Origin priority is BE but no any BE TSPEC is built
		for the station, we do NOT need to protect it.
	2. (for AP & STA) Origin priority is not BE but no any non-BE TSPEC
		is built for the station, we will translate it to BE traffic,
		but we do NOT need to protect it.
========================================================================
*/
ACM_FUNC_STATUS ACMP_BE_QueueFullHandle(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	ACMR_STA_DB				*pCdb,
	ACM_PARAM_IN	ACMR_MBUF				*pMbuf)
{
#ifdef ACM_CC_FUNC_BE_BW_CTRL
	WMM_ACM_FUNC_NAME_PRINT("IN");

	if (pCdb->ACM_NumOfOutTspecInAc[ACM_EDCA_BE_AC_QUE_ID] > 0)
	{
		/* we only need to do protection when BE TSPEC exists */
		if (RTMP_GET_PACKET_TX_TIME(pMbuf) > 0)
		{
			/* the packet is not downgraded to BE so we handle it */
			ACMR_QUEUE_HEADER *pQueueHeader;
			ACMR_QUEUE_ENTRY *pQueueEntry, *pQueueEntryPrev;
			ULONG IrqFlags;


			/* try to look for any queued packet without TSPEC in BE queue */
			RTMP_IRQ_LOCK(&pAd->irq_lock, IrqFlags);
			pQueueHeader = &pAd->TxSwQueue[ACM_EDCA_BE_AC_QUE_ID];

			if (pQueueHeader != NULL)
			{
				/* should be here */
				pQueueEntry = pQueueHeader->Head;
				pQueueEntryPrev = pQueueEntry;

				while(pQueueEntry != NULL)
				{
					if (RTMP_GET_PACKET_TX_TIME(\
									QUEUE_ENTRY_TO_PACKET(pQueueEntry)) == 0)
					{
						/* if the packet is not allowed by TSPEC, tx time = 0 */

						/* remove the entry from the queue list */
						pQueueEntryPrev->Next = pQueueEntry->Next;

						if (pQueueEntry == pQueueHeader->Head)
							pQueueHeader->Head = pQueueEntry->Next;
						/* End of if */
						if (pQueueEntry == pQueueHeader->Tail)
							pQueueHeader->Tail = pQueueEntryPrev;
						/* End of if */
						pQueueEntry->Next = NULL;
						pQueueHeader->Number --;

						RTMP_IRQ_UNLOCK(&pAd->irq_lock, IrqFlags);

						/* discard the packet without matched TSPEC */
						RELEASE_NDIS_PACKET(pAd,
											QUEUE_ENTRY_TO_PACKET(pQueueEntry),
											NDIS_STATUS_FAILURE);
						return ACM_RTN_OK;
					} /* End of if */

					pQueueEntryPrev = pQueueEntry;
					pQueueEntry = pQueueEntry->Next;
				} /* End of while */
			} /* End of if */
			RTMP_IRQ_UNLOCK(&pAd->irq_lock, IrqFlags);
		} /* End of if */
	} /* End of if */
#endif /* ACM_CC_FUNC_BE_BW_CTRL */

	return ACM_RTN_FAIL;
} /* End of ACMP_BE_QueueFullHandle */


/*
========================================================================
Routine Description:
	Get current EDCA ACM Information.

Arguments:
	pAd				- WLAN control block pointer
	*pInfo			- the EDCA ACM information

Return Value:
	ACM_RTN_OK		- get ok
	ACM_RTN_FAIL	- get fail

Note:
========================================================================
*/
ACM_FUNC_STATUS ACMP_ControlInfomationGet(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	ACM_CTRL_INFO			*pInfo)
{
	ACM_CTRL_PARAM *pEdcaParam;
	UINT32 IdAcNum;
#ifdef CONFIG_AP_SUPPORT
	INT32 AvailAdmTime;
	UINT32 IdAcOther;
#endif /* CONFIG_AP_SUPPORT */
	ULONG SplFlags;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* sanity check */
	if ((pAd == NULL) || (pInfo == NULL))
	{
		ACMR_DEBUG(ACMR_DEBUG_TRACE,
					("acm_msg> Input is NULL! ControlInfomationGet()\n"));
		return ACM_RTN_FAIL;
	} /* End of if */

	/* init */
	ACMR_MEM_ZERO(pInfo, sizeof(ACM_CTRL_INFO));

	/* get management semaphore */
	ACM_TSPEC_SEM_LOCK_CHK_RTN(pAd, SplFlags, LabelSemErr, ACM_RTN_FAIL);

	/* copy information */
	pEdcaParam = &(ACMR_CB->EdcaCtrlParam);

	for(IdAcNum=0; IdAcNum<ACM_DEV_NUM_OF_AC; IdAcNum++)
	{
		pInfo->FlgIsAcmEnable[IdAcNum] = pEdcaParam->FlgAcmStatus[IdAcNum];
		pInfo->DowngradeAcNum[IdAcNum] = pEdcaParam->DowngradeAcNum[IdAcNum];
		pInfo->AcmOutTime[IdAcNum] = pEdcaParam->AcmOutTime[IdAcNum];
		pInfo->AcmAcTime[IdAcNum] = pEdcaParam->AcmAcTime[IdAcNum];
	} /* End of for */

	pInfo->CP_MinNu = pEdcaParam->CP_MinNu;
	pInfo->CP_MinDe = pEdcaParam->CP_MinDe;
	pInfo->BEK_MinNu = pEdcaParam->BEK_MinNu;
	pInfo->BEK_MinDe = pEdcaParam->BEK_MinDe;

	pInfo->AcmTotalTime = pEdcaParam->AcmTotalTime;
	pInfo->LinkNumUp = pEdcaParam->LinkNumUp;
	pInfo->LinkNumDn = pEdcaParam->LinkNumDn;
	pInfo->LinkNumBi = pEdcaParam->LinkNumBi;
	pInfo->LinkNumDi = pEdcaParam->LinkNumDi;

	pInfo->FlgIsTspecUpasdEnable = pEdcaParam->FlgIsTspecUpasdEnable;
	pInfo->FlgIsTspecTimeoutEnable = pEdcaParam->FlgIsTspecTimeoutEnable;

#ifdef RELEASE_EXCLUDE
	/* dynamic ATL */
	pInfo->FlgDatl = pEdcaParam->FlgDatl;

	for(IdAcNum=0; IdAcNum<ACM_DEV_NUM_OF_AC; IdAcNum++)
	{
		pInfo->DatlBwMin[IdAcNum] = pEdcaParam->DatlBwMin[IdAcNum];
		pInfo->DatlBwMax[IdAcNum] = pEdcaParam->DatlBwMax[IdAcNum];
	} /* End of for */

	ACMR_MEM_COPY(
				pInfo->DatlBorAcBw,
				pEdcaParam->DatlBorAcBw,
				sizeof(pInfo->DatlBorAcBw));
#endif /* RELEASE_EXCLUDE */

#ifdef CONFIG_AP_SUPPORT
	if (ACMR_IS_AP_MODE(pAd))
	{
		/* calculate available admission capability for each AC */
		for(IdAcNum=0; IdAcNum<ACM_DEV_NUM_OF_AC; IdAcNum++)
		{
#ifdef RELEASE_EXCLUDE
			/* dynamic ATL */
			if (pEdcaParam->FlgDatl == TRUE)
			{
				AvailAdmTime = (INT32)pEdcaParam->DatlBwMax[IdAcNum];
				AvailAdmTime *= ACM_TIME_BASE;
				AvailAdmTime /= 100;

				for(IdAcOther=0; IdAcOther<ACM_DEV_NUM_OF_AC; IdAcOther++)
					AvailAdmTime -= (INT32)pEdcaParam->DatlBorAcBw[IdAcNum][IdAcOther];
				/* End of for */

				if (AvailAdmTime < 0)
					AvailAdmTime = 0; /* should not be here */
				/* End of if */

				pInfo->AvalAdmCapAc[IdAcNum] = AvailAdmTime >> 5; /* unit: 32us */
			}
			else
#endif /* RELEASE_EXCLUDE */
			{
				/* not support for the AC */
				pInfo->AvalAdmCapAc[IdAcNum] = pEdcaParam->AvalAdmCap; /* unit: 32us */
			} /* End of if */
		} /* End of for */
	} /* End of if */
#endif /* CONFIG_AP_SUPPORT */

	/* release semaphore */
	ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);
	return ACM_RTN_OK;

LabelSemErr:
	ACMR_SEM_FAIL_PRINT();
	return ACM_RTN_FAIL;
} /* End of ACMP_ControlInfomationGet */


/*
========================================================================
Routine Description:
	Handle something when a QoS data or null frame is received.

Arguments:
	pAd				- WLAN control block pointer
	*pCdb			- the source QSTA
	pHeader			- the WLAN MAC header

Return Value:
	None

Note:
	1. Only for QAP.
	2. The frame shall be uplink.
	3. For EDCA, we shall reset activity timeout for QoS data frames.
	4. In LINUX, the function must be called in a tasklet.
	5. If PktTsid is 0xFF, we will get TSID from pHeader.
========================================================================
*/
VOID ACMP_DataNullHandle(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	ACMR_STA_DB				*pCdb,
	ACM_PARAM_IN	ACMR_WLAN_HEADER		*pHeader)
{
	ACM_CTRL_PARAM *pEdcaParam;
	ACM_ENTRY_INFO *pStaAcmInfo;
	ACM_STREAM **ppStmListIn;
	ACM_STREAM *pStream;
	UINT16 FrmSubType;
	UINT16 QosCtrl;
	UCHAR UP, PktAcId;
	UINT32 IdTidNum;
	ULONG SplFlags;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* sanity check */
	/*
		We will check ACM_NumOfTspecIn of pCdb before calling the function;
		if > 0, at least one TSPEC exists.
	*/
/*	if (!ACMR_IS_ENABLED(pAd))
		return; */
	/* End of if */

	if (ACMR_CB == NULL)
		return;
	/* End of if */

	/* get management semaphore */
	ACM_TSPEC_SEM_LOCK_CHK(pAd, SplFlags, LabelSemErr);

	pEdcaParam = &(ACMR_CB->EdcaCtrlParam);
	if (pEdcaParam->FlgIsTspecTimeoutEnable == 0)
		goto LabelOK; /* no need to do TSPEC timeout check */
	/* End of if */

	/* init */
	QosCtrl = ACMR_FME_QOSCTRL_GET(pHeader);
	FrmSubType = ACMR_FME_SUBTYPE_GET(pHeader);
	pStream = NULL;

	/* reset activity & suspension timeout for QoS Data or QoS Null frames */
	if ((FrmSubType == ACMR_FME_SUB_TYPE_QOS_DATA) ||
		(FrmSubType == ACMR_FME_SUB_TYPE_QOS_NULL))
	{
#ifdef ACM_CC_FUNC_TCLAS
		/*
			TODO: We need to compare packet with all TCLAS to get UP.
			Solution: Currently we use same UP for all TCLAS in a TSPEC.
		*/
		UP = ACM_TID_GET(QosCtrl);
#else
		UP = ACM_TID_GET(QosCtrl);
#endif /* ACM_CC_FUNC_TCLAS */

		if (ACM_IS_EDCA_STREAM(UP))
		{
			/* EDCA stream */

			/* translate UP to AC ID */
			PktAcId = ACM_MR_EDCA_AC(UP);

			/* try to find the IN TSPEC by AC ID */
			pStaAcmInfo = ACMR_STA_ACM_PARAM_INFO(pCdb);
			ppStmListIn = (ACM_STREAM **)pStaAcmInfo->pAcStmIn;

			for(IdTidNum=0; IdTidNum<ACM_STA_TID_MAX_NUM; IdTidNum++)
			{
				if (ppStmListIn[IdTidNum] != NULL)
				{
					if (PktAcId == ACM_MR_EDCA_AC(ppStmListIn[IdTidNum]->UP))
					{
						/* only one IN TSPEC for a AC */
						pStream = ppStmListIn[IdTidNum];
						break;
					} /* End of if */
				} /* End of if */
			} /* End of for */

			/* update its timeout timers if exists */
			if (pStream != NULL)
			{
				ACM_TSPEC *pTspec = pStream->pTspec;


				if (pTspec->InactivityInt != ACM_TSPEC_INACTIVITY_DISABLE)
					pStream->InactivityCur = pStream->pTspec->InactivityInt;
				/* End of if */

#ifdef ACM_CC_FUNC_HCCA
				if (pTspec->SuspensionInt != ACM_TSPEC_SUSPENSION_DISABLE)
					pStream->SuspensionCur = pStream->pTspec->SuspensionInt;
				/* End of if */
#endif /* ACM_CC_FUNC_HCCA */
			} /* End of if */
		} /* End of if */
	} /* End of if */

LabelOK:
	/* release semaphore */
	ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);
	return;

LabelSemErr:
	ACMR_SEM_FAIL_PRINT();
	return;
} /* End of ACMP_DataNullHandle */


/*
========================================================================
Routine Description:
	Check if the packet can be queued to the packet queue of the AC.

Arguments:
	pAd					- WLAN control block pointer
	*pCdb				- the peer device
	*pMbuf				- the packet
	FlgIsForceToHighAc	- 1: force the packet to AC3
	*pQueueType			- the new packet queue type
							(ACMR_QID_AC_BE ~ ACMR_QID_AC_VO)

Return Value:
	ACM_RTN_OK			- classify successfully
	ACM_RTN_FAIL		- do not allow to send the packet
	ACM_RTN_NO_ACM		- the ACM of the AC is disabled
========================================================================
*/

#ifdef RELEASE_EXCLUDE
/*
Note:
	Used the function to enqueue your WLAN packets.

	WMM ACM Test Plan 4.1.3 APUT Operation with Legacy STAs

	1. When one WMM ACM station and one WMM legacy station connects to
		our AP, and WMM ACM STA creates a TSPEC for dnlink.

	2. Send traffics from AP to two stations with priority VO.

	3. We need to enqueue VO packets for WMM ACM station to VO queue
		and enqueue the VO packets for WMM legacy station to BE queue.

	4. Or if we enqueue all VO packets to the VO queue and partition packets
		to different queue when Dequeue packets, a problem will occur as below:

		a. When the traffic from AP to WMM legacy station saturates the
			bandwidth;
		b. We will drop VO packets because the VO queue is full;
		c. And we will drop VO packets for WMM ACM station but it is not
			correct because WMM ACM station has created a TSPEC for the
			VO traffic.  So We need to protect the VO traffic of WMM ACM STA.

	TODO: When any packet is belong to other station or any packet is belong
		to other different priority, the AMPDU sum time must be reset.
*/
#endif /* RELEASE_EXCLUDE */

#ifdef WMM_ACM_PKT_NUM_DEBUG
/* global variables but only for debug */
UINT32 WMM_ACM_NumOfPkt[WMM_ACM_DEBUG_TIME_MAX_NUM_REC][4]; /* for 4 AC */
UINT32 WMM_ACM_NumOfPktId = 0, WMM_ACM_TimeRecId = 0, WMM_ACM_TimeId = 0;
UINT32 WMM_ACM_Time[WMM_ACM_DEBUG_TIME_MAX_NUM_REC][4]; /* for 4 AC */
BOOLEAN WMM_ACM_IsTimeValid = TRUE;
#endif /* WMM_ACM_PKT_NUM_DEBUG */

ACM_FUNC_STATUS ACMP_DataPacketQueue(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	ACMR_STA_DB				*pCdb,
	ACM_PARAM_IN	ACMR_MBUF				*pMbuf,
	ACM_PARAM_IN	UCHAR					FlgIsForceToHighAc,
	ACM_PARAM_OUT	UINT32					*pQueueType)
{
	ACM_STREAM **ppAcmStmList;
	ACM_STREAM *pStream;
	ACM_TS_INFO *pTsInfo;
	ACM_ENTRY_INFO *pStaAcmInfo;
	ACM_CTRL_PARAM *pEdcaParam;
	ACM_STATISTICS *pStats;

	UINT32 PktLen, LenFrag, LenLastFrag, NumFrag;
	UCHAR  FlgIsFindTspec;
	UINT32 PktAcId, TxTime;
	UCHAR  UP, TSID;
	UINT32 TxQueueType;
	UCHAR  AccessPolicy, AckPolicy;
	UINT32 IdTidNum;
	ULONG  SplFlags;

#ifdef ACM_CC_FUNC_TCLAS
	ACM_TCLAS *pTclas, TclasIpClass;
	UINT32 IdTclasNum;
	UCHAR  TclasBitmap;

	UCHAR *pPkt; /* same as pMbuf */
	UCHAR *pAddrSrc, *pAddrDst;
	UINT16 Type; /* ethernet frame type/len */
	UINT16 VlanTag;
	UCHAR  FlgIsMatchTspec, FlgIsIpPkt;
#endif /* ACM_CC_FUNC_TCLAS */


	WMM_ACM_FUNC_NAME_PRINT("IN");

    /* sanity check */
	RTMP_SET_PACKET_TX_TIME(pMbuf, 0);

	*pQueueType = ACMR_QID_AC_BE;

    if (pCdb == NULL)
	{
		/* *pQueueType is useless */
        return ACM_RTN_NO_ACM;
    } /* End of if */

	/* we will check before calling the function to speed up */
/*	if (ACMP_IsAnyACEnabled(pAd) != ACM_RTN_OK)
		return ACM_RTN_NO_ACM; */
	/* End of if */

	/* init */
	PktLen = ACMR_WLAN_LEN_GET(pMbuf);

#ifdef ACM_CC_FUNC_TCLAS
	pPkt = (UCHAR *)ACMR_WLAN_PKT_GET(pMbuf);

	pAddrDst = (UCHAR *)pPkt;
	pAddrSrc = (UCHAR *)(pPkt + ACM_ETH_DA_ADDR_LEN);
	Type = *(UINT16 *)(pPkt + ACM_ETH_DA_ADDR_LEN + ACM_ETH_SA_ADDR_LEN);

	TclasIpClass.ClassifierMask = 0;

	VlanTag			= 0xFFFF;
	FlgIsIpPkt		= 0;
	FlgIsMatchTspec	= 0;
#endif /* ACM_CC_FUNC_TCLAS */

	pStream			= NULL;
	FlgIsFindTspec	= 0;
	TSID			= 0xff; /* default: no TSID information */
	UP				= 0xff;
	LenFrag			= 0;
	LenLastFrag		= 0;
	NumFrag			= 0;
	TxTime			= 33; /* minimum tx time, > 1 unit = 32us */

	if (FlgIsForceToHighAc == 1)
		TSID = 0x07; /* force to use AC3 */
	else
	{
		if (ACMR_PKT_QOS_TYPE_GET(pMbuf) == ACM_QOS_TYPE_NULL)
			TSID = ACMR_PKT_UP_GET(pMbuf);
		/* End of if */
	} /* End of if */

	ACMR_PKT_MARK_MIN_PHY_MODE(pMbuf, ACMR_PHY_NONE);
	ACMR_PKT_MARK_MIN_PHY_MCS(pMbuf, 0);

	/* get management semaphore */
	ACM_TSPEC_IRQ_LOCK_CHK_RTN(pAd, SplFlags, LabelSemErr, ACM_CLSFY_NOT_ALLOW);

	pStaAcmInfo = ACMR_STA_ACM_PARAM_INFO(pCdb);
	pEdcaParam = &(ACMR_CB->EdcaCtrlParam);
	pStats = &pEdcaParam->Stats;

#ifdef ACM_CC_FUNC_TCLAS
	/* check TCLAS in WMM streams */
	if (FlgIsForceToHighAc == 0)
	{
		ppAcmStmList = (ACM_STREAM **)pStaAcmInfo->pAcStmOut;

		for(IdTidNum=0; IdTidNum<ACM_STA_TID_MAX_NUM; IdTidNum++)
		{
			pStream = ppAcmStmList[IdTidNum];

			if ((pStream == NULL) || (pStream->pTclas == NULL))
				continue; /* no TSPEC exists, check next one */
			/* End of if */

			for(IdTclasNum=0; IdTclasNum<ACM_TSPEC_TCLAS_MAX_NUM; IdTclasNum++)
			{
				if (pStream->pTclas[IdTclasNum] == NULL)
				{
					IdTclasNum = ACM_TSPEC_TCLAS_MAX_NUM;
					break; /* no other TCLAS */
				} /* End of if */

				pTclas = pStream->pTclas[IdTclasNum];
				TclasBitmap = pTclas->ClassifierMask;
				FlgIsMatchTspec = 0;

				switch(pTclas->ClassifierType)
				{
					case ACM_TCLAS_TYPE_ETHERNET:
						if (TclasBitmap & 0x01)
						{
							/* check source address */
							if (!(AMR_IS_SAME_MAC(pAddrSrc,
										pTclas->Clasifier.Ethernet.AddrSrc)))
							{
								break; /* compare fail */
							} /* End of if */
						} /* End of if */

						if (TclasBitmap & 0x02)
						{
							/* check destination address */
							if (!(AMR_IS_SAME_MAC(pAddrDst,
										pTclas->Clasifier.Ethernet.AddrDst)))
							{
								break; /* compare fail */
							} /* End of if */
						} /* End of if */

						if (TclasBitmap & 0x04)
						{
							/* check type */
							if (Type != pTclas->Clasifier.Ethernet.Type)
								break; /* compare fail */
							/* End of if */
						} /* End of if */

						FlgIsMatchTspec = 1;
						break;

					case ACM_TCLAS_TYPE_IP_V4:
						if (TclasIpClass.ClassifierMask == 0)
						{
							/* get IP info. from the frame at first loop */
							TclasIpClass.ClassifierMask = 1;

							if (ACM_TCLAS_IP_INFO_Get(pPkt,
												&TclasIpClass) == ACM_RTN_OK)
							{
								FlgIsIpPkt = 1;
							} /* End of if */
						} /* End of if */

						if (FlgIsIpPkt == 0)
							break; /* the frame is not a IP packet */
						/* End of if */

						if ((TclasBitmap & 0x01) &&
							(pTclas->Clasifier.IPv4.Version != \
										TclasIpClass.Clasifier.IPv4.Version))
						{
							break; /* compare Version fail */
						} /* End of if */

						if ((TclasBitmap & 0x02) &&
							(pTclas->Clasifier.IPv4.IpSource != \
										TclasIpClass.Clasifier.IPv4.IpSource))
						{
							break; /* compare source IP fail */
						} /* End of if */

						if ((TclasBitmap & 0x04) &&
							(pTclas->Clasifier.IPv4.IpDest != \
										TclasIpClass.Clasifier.IPv4.IpDest))
						{
							break; /* compare destination IP fail */
						} /* End of if */

						if ((TclasBitmap & 0x08) &&
							(pTclas->Clasifier.IPv4.PortSource != \
										TclasIpClass.Clasifier.IPv4.PortSource))
						{
							break; /* compare source port fail */
						} /* End of if */

						if ((TclasBitmap & 0x10) &&
							(pTclas->Clasifier.IPv4.PortDest != \
										TclasIpClass.Clasifier.IPv4.PortDest))
						{
							break; /* compare destination port fail */
						} /* End of if */

						if ((TclasBitmap & 0x20) &&
							(pTclas->Clasifier.IPv4.DSCP != \
										TclasIpClass.Clasifier.IPv4.DSCP))
						{
							break; /* compare DSCP fail */
						} /* End of if */

						if ((TclasBitmap & 0x40) &&
							(pTclas->Clasifier.IPv4.Protocol != \
										TclasIpClass.Clasifier.IPv4.Protocol))
						{
							break; /* compare protocol fail */
						} /* End of if */

						FlgIsMatchTspec = 1;
						break;

					case ACM_TCLAS_TYPE_8021DQ:
						if (VlanTag == 0xFFFF)
						{
							/* only get VLAN tag once at first loop */
							ACM_TCLAS_VLAN_INFO_Get(pPkt, &VlanTag);
						} /* End of if */

						if ((TclasBitmap & 0x01) &&
							(VlanTag != 0xFFFF) &&
							(pTclas->Clasifier.IEEE8021Q.TagType != \
																	VlanTag))
						{
							break; /* compare TAG Type fail */
						} /* End of if */

						FlgIsMatchTspec = 1;
						break;
				} /* End of switch */

				if (FlgIsMatchTspec == 1)
				{
					if (pStream->TclasProcessing == \
												ACM_WME_TCLAS_PROCESSING_ONE)
					{
						/* find a matched TCLAS so is the TS */
						FlgIsFindTspec = 1;
						break;
					} /* End of if */
				}
				else
				{
					if (pStream->TclasProcessing == \
												ACM_WME_TCLAS_PROCESSING_ALL)
						break; /* all TCLAS must be matched so not the TS */
					/* End of if */
				} /* End of if */
			} /* End of for */

			if ((FlgIsMatchTspec == 1) &&
				(IdTclasNum == ACM_TSPEC_TCLAS_MAX_NUM))
			{
				FlgIsFindTspec = 1; /* all TCLASS are matched */
			} /* End of if */

			if (FlgIsFindTspec == 1)
				break; /* the frame belongs to the stream */
			/* End of if */
		} /* End of for */
	} /* End of if */
#endif /* ACM_CC_FUNC_TCLAS */

	/* check if any TSPEC with TCLAS number = 0 exists */
	if ((FlgIsFindTspec == 0) &&
		(FlgIsForceToHighAc == 0) &&
		((pStream == NULL) ||
		 pStream->pTclas[0] == NULL))
	{
		/*
			Though, TCLAS number = 0, but maybe the medium time of a
			TSPEC != 0, search the TSPEC.
		*/

		/* check if the packet is QoS Null frame */
		if (!(ACMR_PKT_QOS_TYPE_GET(pMbuf) == ACM_QOS_TYPE_NULL))
		{
			/* the packet is NOT QoS Null frame */

			/* the condition to find the TSPEC is 'same AC ID' */
			TSID = ACM_TSID_Get(pAd, pMbuf);
			PktAcId = ACM_MR_EDCA_AC(TSID);

			/* check if the ACM of the AC is enabled */
			if (pEdcaParam->FlgAcmStatus[PktAcId] != ACM_FLG_FUNC_ENABLED)
			{
				/* *pQueueType is useless */
				goto LabelErrNoACM;
			} /* End of if */

			/* check if the UP matches any out TSPEC with TCLAS number = 0 */
			ppAcmStmList = (ACM_STREAM **)pStaAcmInfo->pAcStmOut;

			for(IdTidNum=0; IdTidNum<ACM_STA_TID_MAX_NUM; IdTidNum++)
			{
				pStream = ppAcmStmList[IdTidNum];

				if (pStream == NULL)
					continue; /* no TSPEC exists, check next one */
				/* End of if */

				if (pStream->pTclas[0] == NULL)
				{
					/* find the TSPEC without TCLAS */
					if (PktAcId == ACM_MR_EDCA_AC(pStream->pTspec->TsInfo.UP))
					{
						/* find the TSPEC */
						FlgIsFindTspec = 1;
						break;
					} /* End of if */
				} /* End of if */
			} /* End of for */
		} /* End of if */
	} /* End of if */

	if ((FlgIsFindTspec == 1) &&
		(pStream != NULL) &&
		(FlgIsForceToHighAc == 0))
	{
		/* at least one TSPEC matches the packet */
		pTsInfo = &pStream->pTspec->TsInfo;
		AccessPolicy = pTsInfo->AccessPolicy;
		AckPolicy = pTsInfo->AckPolicy;
		PktAcId = pStream->AcmAcId;

		/* refresh inactivity & suspension timeout for the TS */
		if (pStream->pTspec->InactivityInt != ACM_TSPEC_INACTIVITY_DISABLE)
			pStream->InactivityCur = pStream->pTspec->InactivityInt;
		/* End of if */

#ifdef ACM_CC_FUNC_HCCA
		if (pStream->pTspec->SuspensionInt != ACM_TSPEC_SUSPENSION_DISABLE)
			pStream->SuspensionCur = pStream->pTspec->SuspensionInt;
		/* End of if */
#endif /* ACM_CC_FUNC_HCCA */
	}
	else
	{
		/* no TSPEC is matched */
		AccessPolicy = ACM_ACCESS_POLICY_EDCA;
		AckPolicy = ACM_ACK_POLICY_NORMAL;

		if (ACMR_PKT_QOS_TYPE_GET(pMbuf) == ACM_QOS_TYPE_NULL)
		{
			/* use the TID of the QoS Null packet */
			PktAcId = ACM_MR_EDCA_AC(ACMR_PKT_UP_GET(pMbuf));
		}
		else
			PktAcId = ACM_MR_EDCA_AC(TSID);
		/* End of if */
	} /* End of if */


	/* here, we have got the PktAcId */

#ifdef ACM_CC_FUNC_SOFT_ACM

	/*
		Test Note:
		When you want to test a TSPEC with 208B/83Kbps, you must not
		send packets with 1514B to do the test.
		Because we have about 51 packets with 208B and we will have
		51 * (11g preamble time + sifs + ack time), if you send packets
		with 1514B, these extra times will be used to send packets with
		1514B, you will not see the 83kbps throughput and you will see
		higher throughput.
	*/

	ACM_TG_CMT_USED_TIME_PASS_CRITERIA;

	/* check whether tx time > TXOP limit for AC2 & AC3 */

	if (pStream != NULL)
	{
		/* no TSPEC is found so no need to calculate the tx time */
		UINT64 Timestamp, TimeOffset;
		UINT32 TimeAllowed;


		/* get 64-bit Timestamp */
		ACMR_TIMESTAMP_GET(pAd, Timestamp);

#ifdef ACM_CC_FUNC_11N
		if (ACMR_IS_HT_RATE_USED(pCdb))
		{
			/* the station uses HT rate */
			TxTime = ACM_TX_TimeCalOnFlyHT(
											pAd,
											pCdb,
											pStream,
											Timestamp,
											PktLen,
											ACMR_CLIENT_MCS_GET(pCdb),
											0);
		}
		else
#endif /* ACM_CC_FUNC_11N */
		{
			UCHAR FlgIsRtsEnable, FlgIsCtsEnable;

			FlgIsRtsEnable = ACMR_RTS_FLAG_GET(pAd, pMbuf);

			/* priority of RTS/CTS is over CTS-self */
			if (FlgIsRtsEnable == 0)
				FlgIsCtsEnable = ACMR_CTS_FLAG_GET(pAd, pMbuf);
			else
				FlgIsCtsEnable = 0;
			/* End of if */

			TxTime = ACM_TX_TimeCalOnFly(
											pAd,
											pCdb,
											PktLen,
											ACM_Rate_Mapping(pAd, pCdb),
											FlgIsCtsEnable,
											FlgIsRtsEnable,
											ACMR_STA_IS_SPREAMBLE(pAd, pCdb),
											0);
		}

		if (TxTime > 0x0000FFFF)
		{
			ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_err> Tx Time > 0xFFFF us!\n"));
		} /* End of if */

		RTMP_SET_PACKET_TX_TIME(pMbuf, (UINT16)TxTime);
		RTMP_SET_PACKET_STM_TSID(pMbuf, pStream->pTspec->TsInfo.TSID);
		/* no need to mark direction, must be output TSPEC */

		/* translate MediumTime (unit: 32us) to us */
		ACMR_ALLOWED_TIME_GET(pStream, TimeAllowed);

#ifdef ACM_CC_FUNC_11N_AGG
		/*
			For AMSDU, we will calculate used time based on non-AMSDU so
			we need to enlarge the allow time here; Or many packets will
			be dropped here.
			Because when we queue the packet, we do not yet know if we can do
			AMSDU for it. We will do AMSDU in MSDU_Classify(), not here.
		*/
		if (ACMR_11N_IS_AMSDU_USED(pCdb, pMbuf))
			TimeAllowed *= 2; /* 2: rough estimation, maybe need to enlarge */
		/* End of if */
#endif /* ACM_CC_FUNC_11N_AGG */

		/* use signed INT64 to avoid round-up problem */
		TimeOffset = (UINT64)((INT64)Timestamp - \
								(INT64)pStream->TxTimestampMarkEnqueue);

		if (TimeOffset >= ACM_TIME_BASE)
		{
			/* the first packet in next second */
			pStream->TxTimestampMarkEnqueue = Timestamp;
#ifdef ACM_CC_FUNC_11N
			pStream->TxTimestampEnqueueHT = Timestamp;
#endif /* ACM_CC_FUNC_11N */

			/* reset the used time */
			if (pStream->AcmUsedTimeEnqueue > TimeAllowed)
				pStream->AcmUsedTimeEnqueue -= TimeAllowed;
			else
				pStream->AcmUsedTimeEnqueue = 0;
			/* End of if */

			/* reset aggregation parameters */
			pStream->TxAmpduNumEnqueueHT = 0;
			pStream->TxTimeEnqueueHT = 0;

#ifdef WMM_ACM_PKT_NUM_DEBUG
			++WMM_ACM_NumOfPktId;
			if (WMM_ACM_NumOfPktId >= WMM_ACM_DEBUG_TIME_MAX_NUM_REC)
				WMM_ACM_NumOfPktId = 0;
			/* End of if */
			WMM_ACM_NumOfPkt[WMM_ACM_NumOfPktId][PktAcId] = 0;

			++WMM_ACM_TimeRecId;

			if (WMM_ACM_TimeRecId == 5)
				NdisZeroMemory(WMM_ACM_Time, sizeof(WMM_ACM_Time));
			/* End of if */

			WMM_ACM_IsTimeValid = TRUE;
#endif /* WMM_ACM_PKT_NUM_DEBUG */
		}
		else
		{
#ifndef PERFORMANCE_IMPACT_TEST
			/* during a second */
			if (pStream->AcmUsedTimeEnqueue > TimeAllowed)
			{
				/* can not tx the packet to the AC so check downgrade flag */
				RTMP_SET_PACKET_TX_TIME(pMbuf, 0);

				if (pEdcaParam->DowngradeAcNum[PktAcId] != \
														ACM_DOWNGRADE_DISABLE)
				{
					TxQueueType = ACM_TxQueueTypeGet( \
										pEdcaParam->DowngradeAcNum[PktAcId]);

					ACM_STATS_COUNT_INC(pStats->Downgrade[PktAcId]);

					/*
						In WMM spec., A.2 Use of Admission Control and
						Downgrading

						(3)	The MSDU is sent using a different UP.  The UP is
						changed to map to a lower AC that does not require
						admission control.  The UP has to be changed prior to
						calculating the MIC, assignment of the TSC and mixing
						the keys. Changing of the UP is performed outside the
						MAC and is out of the scope of this spec.

						In an AC for which the admission control mandatory
						flag is set to 1, a WMM AP should use option (3).
					*/

					/* need to change the UP of QoS Control field */
					TSID = gEDCA_AC_UP[pEdcaParam->DowngradeAcNum[PktAcId]];
					ACMR_PKT_MARK_UP(pMbuf, TSID);

					goto LabelOK;
				} /* End of if */

				ACM_STATS_COUNT_INC(pStats->DropByAdmittedTime);

#ifdef WMM_ACM_PKT_NUM_DEBUG
				WMM_ACM_IsTimeValid = FALSE;
#endif /* WMM_ACM_PKT_NUM_DEBUG */

				/* downgrade function is disabled so discarding the packet */
				goto LabelErr;
			} /* End of if */
#endif /* PERFORMANCE_IMPACT_TEST */
		} /* End of if */

		/* accumulate used time */
#ifdef WMM_ACM_PKT_NUM_DEBUG
		if (WMM_ACM_TimeRecId == 5)
		{
			if (WMM_ACM_TimeId >= WMM_ACM_DEBUG_TIME_MAX_NUM_REC)
				WMM_ACM_TimeId = 0;
			/* End of if */
			WMM_ACM_Time[WMM_ACM_TimeId][PktAcId] = TxTime;

			WMM_ACM_TimeId ++;
		} /* End of if */

		WMM_ACM_NumOfPkt[WMM_ACM_NumOfPktId][PktAcId] ++;
#endif /* WMM_ACM_PKT_NUM_DEBUG */

		pStream->AcmUsedTimeEnqueue += TxTime;
	}
	else
	{
		/* No TSPEC is found so Tx Time = 0 (default) */
	} /* End of if */
#endif /* ACM_CC_FUNC_SOFT_ACM */

	/* assign AC ID or TS ID to the frame */
	if ((FlgIsFindTspec == 1) &&
		(pStream != NULL) &&
		(FlgIsForceToHighAc == 0))
	{
		/* re-set user priority to packet information, used by WLAN module */
		TxQueueType = pStream->TxQueueType;

		if (TSID == 0xff)
			TSID = ACM_TSID_Get(pAd, pMbuf);/* get TSID by UP or DSCP */
		/* End of if */

		ACMR_PKT_MARK_UP(pMbuf, TSID);
		ACMR_PKT_MARK_MIN_PHY_MODE(pMbuf, pStream->PhyModeMin);
		ACMR_PKT_MARK_MIN_PHY_MCS(pMbuf, pStream->McsMin);
	}
	else
	{
		/*
			Can not find any matched TCLAS/TSPEC so classify the packet to
			the queue by UP or DSCP (EDCA stream).
		*/

		if (!(ACMR_PKT_QOS_TYPE_GET(pMbuf) == ACM_QOS_TYPE_NULL))
		{
			/* for non-QoS Null frame */
			if (TSID == 0xff)
				TSID = ACM_TSID_Get(pAd, pMbuf);/* get TSID by UP or DSCP */
			/* End of if */

			PktAcId = ACM_MR_EDCA_AC(TSID);
		} /* End of if */

		if ((PktAcId != ACM_EDCA_BK_AC_QUE_ID) &&
			(pEdcaParam->FlgAcmStatus[PktAcId] == ACM_FLG_FUNC_ENABLED) &&
			(FlgIsForceToHighAc == 0))
		{
			/*
				The ACM for the AC is enabled but we can not get any TSPEC
				for the packet so we shall send the frame to AC0 (BE) queue
				except BK traffic.
			*/

			TSID = 0; /* use BE */
			PktAcId = ACM_EDCA_BE_AC_QUE_ID;

#ifdef CONFIG_STA_SUPPORT
			if (ACMR_IS_STA_MODE(pAd))
			{
#ifdef RELEASE_EXCLUDE
				/*
					Since an AP manages the downlink flows using its own rules
					and policies (it does not have to police used time, for
					example), so we still can use BE to transmit traffic even
					the ACM of BE is set.
				*/
#endif /* RELEASE_EXCLUDE */
				if (pEdcaParam->FlgAcmStatus[PktAcId] == ACM_FLG_FUNC_ENABLED)
				{
					ACM_STATS_COUNT_INC(pStats->DropByACM);

					ACMR_DEBUG(ACMR_DEBUG_TRACE,
								("acm_msg> ACM of BE is set!\n"));

					/*
						My god!
						The ACM of BE is also enabled! Discard the packet!
					*/
					goto LabelErr;
				} /* End of if */
			} /* End of if */
#endif /* CONFIG_STA_SUPPORT */

			ACM_STATS_COUNT_INC(pStats->PriorityChange[PktAcId]);
		} /* End of if */

		/* send the frame the the AC */
		pStream = NULL; /* no TSPEC is found */
		TxQueueType = ACM_TxQueueTypeGet(PktAcId);

		/* re-set user priority to packet information, used by WLAN module */
		ACMR_PKT_MARK_UP(pMbuf, TSID);
	} /* End of if */

LabelOK:
	ACM_TSPEC_IRQ_UNLOCK(pAd, SplFlags, LabelSemErr);
	*pQueueType = TxQueueType;
	return ACM_RTN_OK;

LabelErrNoACM:
	ACM_TSPEC_IRQ_UNLOCK(pAd, SplFlags, LabelSemErr);
	return ACM_RTN_NO_ACM;

LabelErr:
	ACM_TSPEC_IRQ_UNLOCK(pAd, SplFlags, LabelSemErr);
	return ACM_RTN_FAIL;

LabelSemErr:
	ACMR_SEM_FAIL_PRINT();
	return ACM_RTN_NO_ACM;
} /* End of ACMP_DataPacketQueue */


#ifdef RELEASE_EXCLUDE
/*
========================================================================
Routine Description:
	Enable or disable Dynamic ATL function.

Arguments:
	pAd				- WLAN control block pointer
	FlgIsEnable		- 1: enable; 0: disable
	*pDatlBwMin		- new minimum bandwidth threshold
	*pDatlBwMax		- new maximum bandwidth threshold

Return Value:
	None

Note:
	if you dont want to change bandwidth threshold, you can input NULL.
	pDatlBwMin = NULL or pDatlBwMax = NULL
========================================================================
*/
VOID ACMP_DatlCtrl(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	UCHAR					FlgIsEnable,
	ACM_PARAM_IN	UCHAR					*pDatlBwMin,
	ACM_PARAM_IN	UCHAR					*pDatlBwMax)
{
	ACM_CTRL_PARAM *pEdcaParam;
	UINT32 IdAcNum, IdAcNumOther;
	ULONG SplFlags;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* sanity check */
	if (!ACMR_IS_ENABLED(pAd))
	{
		ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> WMM ACM is disabled!\n"));
		return;
	} /* End of if */

	/* first delete all TSPEC */
	ACMP_TC_DeleteAll(pAd);

	/* get management semaphore */
	ACM_TSPEC_SEM_LOCK_CHK(pAd, SplFlags, LabelSemErr);

	/*
		Assign new minimum and maximum bandwidth threshold
		and clear all borrowing bandwidth.
	*/
	pEdcaParam = &(ACMR_CB->EdcaCtrlParam);

	/* change DATL flag */
	pEdcaParam->FlgDatl = FlgIsEnable;

	for(IdAcNum=0; IdAcNum<ACM_DEV_NUM_OF_AC; IdAcNum++)
	{
		if (pDatlBwMin != NULL)
			pEdcaParam->DatlBwMin[IdAcNum] = pDatlBwMin[IdAcNum];
		/* End of if */

		if (pDatlBwMax != NULL)
			pEdcaParam->DatlBwMax[IdAcNum] = pDatlBwMax[IdAcNum];
		/* End of if */

		for(IdAcNumOther=0; IdAcNumOther<ACM_DEV_NUM_OF_AC; IdAcNumOther++)
			pEdcaParam->DatlBorAcBw[IdAcNum][IdAcNumOther] = 0;
		/* End of for */
	} /* End of for */

	/* release semaphore */
	ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);

	ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> DATL reset!\n"));
	return;

LabelSemErr:
	ACMR_SEM_FAIL_PRINT();
	return;
} /* End of ACMP_DatlCtrl */
#endif /* RELEASE_EXCLUDE */


#ifdef CONFIG_AP_SUPPORT
/*
========================================================================
Routine Description:
	Append the QBSS Load element to the beacon frame.

Arguments:
	pAd				- WLAN control block pointer
	*pPkt			- the beacon frame

Return Value:
	the element total length

Note:
========================================================================
*/
UINT32 ACMP_Element_QBSS_LoadAppend(
 	ACM_PARAM_IN		ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN_OUT	UCHAR					*pPkt)
{
	ACM_ELM_QBSS_LOAD QBSS_Load, *pLoad = &QBSS_Load;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* sanity check */
	if (!ACMR_IS_ENABLED(pAd))
		return 0;
	/* End of if */

	/* copy information */
	pLoad->ElementId = ACM_ELM_QBSS_LOAD_ID;
	pLoad->Length = ACM_ELM_QBSS_LOAD_LEN;

	pLoad->StationCount = ACMR_STA_CUR_COUNT(pAd);
	pLoad->ChanUtil = ACMR_CHAN_UTILIZATION_GET(pAd);
	pLoad->AvalAdmCap = 0;

	/* copy the QBSS element to the frame, pPkt */
	ACMR_MEM_COPY(pPkt, (UCHAR *)pLoad, sizeof(ACM_ELM_QBSS_LOAD));

	return (ACM_ELM_ID_LEN_SIZE + ACM_ELM_QBSS_LOAD_LEN);
} /* End of ACMP_Element_QBSS_LoadAppend */
#endif /* CONFIG_AP_SUPPORT */


/*
========================================================================
Routine Description:
	Reset current ACM Flag for each AC.

Arguments:
	pAd				- WLAN control block pointer
	FlgIsAcm0Enable	- the ACM flag for AC0
	FlgIsAcm1Enable	- the ACM flag for AC1
	FlgIsAcm2Enable	- the ACM flag for AC2
	FlgIsAcm3Enable	- the ACM flag for AC3

Return Value:
	None

Note:
========================================================================
*/
VOID ACMP_EnableFlagReset(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	UCHAR					FlgIsAcm0Enable,
	ACM_PARAM_IN	UCHAR					FlgIsAcm1Enable,
	ACM_PARAM_IN	UCHAR					FlgIsAcm2Enable,
	ACM_PARAM_IN	UCHAR					FlgIsAcm3Enable)
{
	UCHAR *pFlgArrayIsAcmEnabled;
	UCHAR FlgIsAllTspecNeedToDelete;
	ULONG SplFlags;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* init */
	FlgIsAllTspecNeedToDelete = 1;

	/* get management semaphore */
	ACM_TSPEC_SEM_LOCK_CHK(pAd, SplFlags, LabelSemErr);

	/* check if we need to delete all TS */
	pFlgArrayIsAcmEnabled = ACMR_CB->EdcaCtrlParam.FlgAcmStatus;

	if ((pFlgArrayIsAcmEnabled[0] == FlgIsAcm0Enable) &&
		(pFlgArrayIsAcmEnabled[1] == FlgIsAcm1Enable) &&
		(pFlgArrayIsAcmEnabled[2] == FlgIsAcm2Enable) &&
		(pFlgArrayIsAcmEnabled[3] == FlgIsAcm3Enable))
	{
		/* same ACM flag for all AC so no need to delete TSPECs */
		FlgIsAllTspecNeedToDelete = 0;
	}
#if 0
	/* maybe NULL TSPEC exists */
	else if ((pFlgArrayIsAcmEnabled[0] == 0) &&
			(pFlgArrayIsAcmEnabled[1] == 0) &&
			(pFlgArrayIsAcmEnabled[2] == 0) &&
			(pFlgArrayIsAcmEnabled[3] == 0))
	{
		/* old ACM flag of all AC are all-zero so no any TSPEC exists */
		FlgIsAllTspecNeedToDelete = 0;
	} /* End of if */
#endif

	/* update new ACM flag */
	pFlgArrayIsAcmEnabled[0] = FlgIsAcm0Enable;
	pFlgArrayIsAcmEnabled[1] = FlgIsAcm1Enable;
	pFlgArrayIsAcmEnabled[2] = FlgIsAcm2Enable;
	pFlgArrayIsAcmEnabled[3] = FlgIsAcm3Enable;

#ifdef CONFIG_STA_SUPPORT
	if (ACMR_IS_STA_MODE(pAd))
	{
		/* update new TSPEC UAPSD function */
		if (ACMR_APSD_CAPABLE_GET(pAd) == TRUE)
			ACM_PS_UapsdCtrl(pAd, 1); /* can change UAPSD in TSPEC */
		else
			ACM_PS_UapsdCtrl(pAd, 0); /* can not change UAPSD in TSPEC */
		/* End of if */
	} /* End of if */
#endif /* CONFIG_STA_SUPPORT */

	/* release semaphore */
	ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);

	ACMR_DEBUG(ACMR_DEBUG_TRACE,
				("acm_msg> Reset ACM flag to %d %d %d %d\n",
				FlgIsAcm0Enable, FlgIsAcm1Enable, FlgIsAcm2Enable, FlgIsAcm3Enable));

#ifdef CONFIG_AP_SUPPORT
	if (ACMR_IS_AP_MODE(pAd))
	{
		ACMR_AC_ACM_CTRL(pAd,
						FlgIsAcm0Enable,
						FlgIsAcm1Enable,
						FlgIsAcm2Enable,
						FlgIsAcm3Enable);
	} /* End of if */
#endif /* CONFIG_AP_SUPPORT */

	/* delete all streams if needed */
	if (FlgIsAllTspecNeedToDelete)
		ACMP_TC_DeleteAll(pAd);
	/* End of if */

#ifdef CONFIG_STA_SUPPORT
	if (ACMR_IS_STA_MODE(pAd))
	{
		/* reset the UAPSD state */
		/* suppose the function should be called in LinkUp() */
		ACMR_UAPSD_BACKUP(pAd);
	} /* End of if */
#endif /* CONFIG_STA_SUPPORT */

	return;

LabelSemErr:
	ACMR_SEM_FAIL_PRINT();
	return;
} /* End of ACMP_EnableFlagReset */


/*
========================================================================
Routine Description:
	Send a broadcast Bandwidth Annonce frame.

Arguments:
	pAd					- WLAN control block pointer
	FlgIsForceToSent	- 1: forece to send the frame

Return Value:
	None

Note:
	1. Carefully! This is a broadcast frame and must be non-encryption.
	2. The frame is only sent by AP. STA only forward it.
	3. AP A <--> AP B <--> AP C
		AP B only need to broadcast its used ACM time, no AP A used time.
		Because AP C does not see AP A.
========================================================================
*/
STATIC VOID ACMP_FrameBwAnnSend(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	UCHAR					FlgIsForceToSent)
{
#ifdef ACM_CC_FUNC_MBSS
#ifdef CONFIG_AP_SUPPORT
	ULONG SplFlags;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* get management semaphore */
	ACM_TSPEC_SEM_LOCK_CHK(pAd, SplFlags, LabelSemErr);

	ACMP_FrameBwAnnSend(pAd, FlgIsForceToSent);

	/* release semaphore */
	ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);

LabelSemErr:
	ACMR_SEM_FAIL_PRINT();
#endif /* CONFIG_AP_SUPPORT */
#endif /* ACM_CC_FUNC_MBSS */

	return;
}


/*
========================================================================
Routine Description:
	Resume the ACM.

Arguments:
	pAd					- WLAN control block pointer

Return Value:
	None

Note:
========================================================================
*/
VOID ACMP_FSM_Resume(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd)
{
	ULONG SplFlags;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	ACM_TSPEC_SEM_LOCK_CHK(pAd, SplFlags, LabelSemErr);

	ACM_MR_TSPEC_ALLOW(pAd);

	ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);
	return;

LabelSemErr:
	ACMR_SEM_FAIL_PRINT();
	return;
} /* End of ACMP_FSM_Resume */


/*
========================================================================
Routine Description:
	Suspend the ACM.

Arguments:
	pAd					- WLAN control block pointer

Return Value:
	None

Note:
	QSTA: No any TSPEC request can be issued.
	QAP: No any TSPEC request can be handled.
========================================================================
*/
VOID ACMP_FSM_Suspend(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd)
{
	ULONG SplFlags;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	ACM_TSPEC_SEM_LOCK_CHK(pAd, SplFlags, LabelSemErr);

	ACM_MR_TSPEC_DISALLOW(pAd);

	ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);
	return;

LabelSemErr:
	ACMR_SEM_FAIL_PRINT();
	return;
} /* End of ACMP_FSM_Suspend */


/*
========================================================================
Routine Description:
	Get the device entry of MAC.

Arguments:
	pAd				- WLAN control block pointer
	pDevMac			- the entry MAC

Return Value:
	TRUE			- WMM is enabled
	FALSE			- WMM is disabled

Note:
========================================================================
*/
ACMR_STA_DB *ACMP_InfoStaEntryGet(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd,
	ACM_PARAM_IN	UCHAR				*pDevMac)
{
#ifdef CONFIG_AP_SUPPORT
	if (ACMR_IS_AP_MODE(pAd))
		return MacTableLookup(pAd, pDevMac);
	/* End of if */
#endif /* CONFIG_AP_SUPPORT */

#ifdef CONFIG_STA_SUPPORT
	if (ACMR_IS_STA_MODE(pAd))
	{
		/* only one entry in QSTA, i.e. associated QAP */
		return (IS_ENTRY_CLIENT(&(pAd->MacTab.Content[BSSID_WCID])) ?	\
					(&pAd->MacTab.Content[BSSID_WCID]) : NULL);
	} /* End of if */
#endif /* CONFIG_STA_SUPPORT */

	return NULL; /* impossible */
} /* End of ACMP_InfoStaEntryGet */


/*
========================================================================
Routine Description:
	Return TRUE if the ACM of all AC are enabled.

Arguments:
	pAd				- WLAN control block pointer

Return Value:
	ACM_RTN_OK		- the ACM of all AC is enabled
	ACM_RTN_FAIL	- the ACM of one AC is disabled

Note:
========================================================================
*/
ACM_FUNC_STATUS ACMP_IsAllACEnabled(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd)
{
	UCHAR FlgAcmStatus[ACM_DEV_NUM_OF_AC];
	ULONG SplFlags;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* copy ACM enabled flag first */
	ACM_TSPEC_SEM_LOCK_CHK_RTN(pAd, SplFlags, LabelSemErr, ACM_RTN_FAIL);

	ACMR_MEM_COPY(FlgAcmStatus,
				ACMR_CB->EdcaCtrlParam.FlgAcmStatus,
				sizeof(ACMR_CB->EdcaCtrlParam.FlgAcmStatus));

	ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);

	/* check ACM enabled flag */
	if ((FlgAcmStatus[ACM_EDCA_BE_AC_QUE_ID] == 0) ||
		(FlgAcmStatus[ACM_EDCA_BK_AC_QUE_ID] == 0) ||
		(FlgAcmStatus[ACM_EDCA_VI_AC_QUE_ID] == 0) ||
		(FlgAcmStatus[ACM_EDCA_VO_AC_QUE_ID] == 0))
	{
		/* at least one AC ACM is disabled */
		return ACM_RTN_FAIL;
	} /* End of if */

	return ACM_RTN_OK;

LabelSemErr:
	ACMR_SEM_FAIL_PRINT();
	return ACM_RTN_FAIL;
} /* End of ACMP_IsAllACEnabled */


/*
========================================================================
Routine Description:
	Return TRUE if the ACM of any AC is enabled.

Arguments:
	pAd				- WLAN control block pointer

Return Value:
	ACM_RTN_OK		- the ACM of any AC is enabled
	ACM_RTN_FAIL	- the ACM of all AC is disabled

Note:
========================================================================
*/
ACM_FUNC_STATUS ACMP_IsAnyACEnabled(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd)
{
	UCHAR FlgAcmStatus[ACM_DEV_NUM_OF_AC];
	ULONG SplFlags;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* copy ACM enabled flag first */
	ACM_TSPEC_IRQ_LOCK_CHK_RTN(pAd, SplFlags, LabelSemErr, ACM_RTN_FAIL);

	*(UINT32 *)(FlgAcmStatus) = *(UINT32 *)(ACMR_CB->EdcaCtrlParam.FlgAcmStatus);

	ACM_TSPEC_IRQ_UNLOCK(pAd, SplFlags, LabelSemErr);

	/* check ACM enabled flag */
	if (*(UINT32 *)(FlgAcmStatus) == 0)
	{
		/* all AC ACM are disabled */
		return ACM_RTN_FAIL;
	} /* End of if */

	return ACM_RTN_OK;

LabelSemErr:
	ACMR_SEM_FAIL_PRINT();
	return ACM_RTN_FAIL;
} /* End of ACMP_IsAnyACEnabled */


/*
========================================================================
Routine Description:
	Return TRUE if the frame is Bandwidth Announce Action Frame.

Arguments:
	pAd				- WLAN control block pointer
	*pMbuf			- the frame

Return Value:
	ACM_RTN_OK		- Yes
	ACM_RTN_FAIL	- No

Note:
========================================================================
*/
ACM_FUNC_STATUS ACMP_IsBwAnnounceActionFrame(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	VOID					*pMbuf)
{
#ifdef ACM_CC_FUNC_MBSS
	ACM_BW_ANN_FRAME *pFrameAnn;
	UCHAR *pActFrame;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	pActFrame = (UCHAR *)pMbuf + ACMR_FME_LEG_HEADER_SIZE;
	pFrameAnn = (ACM_BW_ANN_FRAME *)pActFrame;

	if ((pFrameAnn->Category == ACM_CATEGORY_WME) &&
		(pFrameAnn->Action == ACM_ACTION_WME_BW_ANN))
	{
		return ACM_RTN_OK;
	} /* End of if */

	return ACM_RTN_FAIL;
#else

	return ACM_RTN_FAIL;
#endif /* ACM_CC_FUNC_MBSS */
} /* End of ACMP_IsBwAnnounceActionFrame */


/*
========================================================================
Routine Description:
	Check if the action frame is the DELTS frame.

Arguments:
	*pMblk			- the action frame

Return Value:
	TRUE			- Yes
	FALSE			- No

Note:
========================================================================
*/
BOOLEAN ACMP_IsDeltsFrame(
	ACM_PARAM_IN	UCHAR					*pMblk)
{
	UCHAR *pActFrame;
	UCHAR Category, Action;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* points to the action frame WLAN header */
	pActFrame = (UCHAR *)pMblk;

	/* get Category & Action field */
	Category = *pActFrame;
	Action = *(pActFrame+1);

	/* checking */
	if (Category == ACM_CATEGORY_WME)
	{
		if (Action == ACM_ACTION_WME_TEAR_DOWN)
			return TRUE;
		/* End of if */
	} /* End of if */

	return FALSE;
} /* End of ACMP_IsDeltsFrame */


/*
========================================================================
Routine Description:
	Check if the packet needs to do ACM.

Arguments:
	pAd				- WLAN control block pointer
	*pCdb			- the destination QSTA
	UP				- user priority

Return Value:
	TRUE			- Yes
	FALSE			- No

Note:
	When the ACM of AC is set, the packet is needed to do ACM.

	Only used in transmission path.
========================================================================
*/
BOOLEAN ACMP_IsNeedToDoAcm(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	ACMR_STA_DB				*pCdb,
	ACM_PARAM_IN	UCHAR					UP)
{
	UINT8 AcId;
	ULONG SplFlags;


	/* init */
	WMM_ACM_FUNC_NAME_PRINT("IN");

	AcId = ACM_MR_EDCA_AC(UP);

	/* check */
	ACM_TSPEC_IRQ_LOCK_CHK_RTN(pAd, SplFlags, LabelSemErr, FALSE);

	if (ACMR_CB->EdcaCtrlParam.FlgAcmStatus[AcId])
	{
		ACM_TSPEC_IRQ_UNLOCK(pAd, SplFlags, LabelSemErr);
		return TRUE;
	} /* End of if */

	ACM_TSPEC_IRQ_UNLOCK(pAd, SplFlags, LabelSemErr);
	return FALSE;

LabelSemErr:
	ACMR_SEM_FAIL_PRINT();
	return FALSE;
} /* End of ACMP_IsNeedToDoAcm */


/*
========================================================================
Routine Description:
	Check if the action frame is the ADDTS Response frame.

Arguments:
	*pMblk			- the action frame

Return Value:
	TRUE			- Yes
	FALSE			- No

Note:
========================================================================
*/
BOOLEAN ACMP_IsResponseFrame(
	ACM_PARAM_IN	UCHAR					*pMblk)
{
	UCHAR *pActFrame;
	UCHAR Category, Action;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* points to the action frame WLAN header */
	pActFrame = (UCHAR *)pMblk;

	/* get Category & Action field */
	Category = *pActFrame;
	Action = *(pActFrame+1);

	/* checking */
	if (Category == ACM_CATEGORY_WME)
	{
		if (Action == ACM_ACTION_WME_SETUP_RSP)
			return TRUE;
		/* End of if */
	} /* End of if */

	return FALSE;
} /* End of ACMP_IsResponseFrame */


/*
========================================================================
Routine Description:
	Handle the management action frame.

Arguments:
	pAd				- WLAN control block pointer
	*pCdb			- the source QSTA
	SubType			- the subtype of the frame
	pMblk			- the received frame
	PhyRate			- the physical tx rate for the frame

Return Value:
	ACM_RTN_OK		- pMblk is released or forwarded
	ACM_RTN_FAIL	- handle ok and pMblk is not released

Note:
========================================================================
*/
ACM_FUNC_STATUS ACMP_ManagementHandle(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	ACMR_STA_DB				*pCdb,
	ACM_PARAM_IN	UINT32					SubType,
	ACM_PARAM_IN	UCHAR					*pMblk,
	ACM_PARAM_IN	UINT32					PktLen,
	ACM_PARAM_IN	UINT32					PhyRate)
{
	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* sanity check */

	/*
		We can accept request due to ps mode change only
		so we do NOT use ACMR_IS_ENABLED(pAd) here.
	*/
	if (!ACMR_SANITY_CHECK(pAd))
	{
		ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> discard ACM action frame!\n"));
		return ACM_RTN_FAIL;
	} /* End of if */

	if (SubType != ACMR_SUBTYPE_ACTION)
	{
		ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> drop non ACM action frame!\n"));
		return ACM_RTN_FAIL; /* not ACTION frame */
	} /* End of if */

	/* QAP mode */
#ifdef CONFIG_AP_SUPPORT
	if (ACMR_IS_AP_MODE(pAd))
		ACM_ActionHandleByQAP(pAd, pCdb, pMblk, PktLen, PhyRate);
	/* End of if */
#endif /* CONFIG_AP_SUPPORT */

	/* QSTA mode */
#ifdef CONFIG_STA_SUPPORT
	if (ACMR_IS_STA_MODE(pAd))
		ACM_ActionHandleByQSTA(pAd, pCdb, pMblk, PktLen);
	/* End of if */
#endif /* CONFIG_STA_SUPPORT */

	return ACM_RTN_OK;
} /* End of ACMP_ManagementHandle */


/*
========================================================================
Routine Description:
	Classify the QoS frame to a AC queue.

Arguments:
	pAd					- WLAN control block pointer
	*pCdb				- the source QSTA
	pMbuf				- the received frame
	QueueTypeCur		- the current used queue type
	FlgIsForceToHighAc	- 1: force the packet to AC3

Return Value:
	Queue Type: AC0 ~ AC3
	not AC0 ~ AC3: can not transmit

Note:
	1. Suppose the Tx Rate is not changed between ACMP_DataPacketQueue()
		and ACMP_MsduClassify().
	2. We re-do ACM control here because maybe OS delay between
		ACMP_DataPacketQueue() and ACMP_MsduClassify().
========================================================================
*/
#ifdef WMM_ACM_PKT_NUM_DEBUG
/* global variables but only for debug */
UINT32 WMM_ACM_TimeAmsdu[WMM_ACM_DEBUG_TIME_MAX_NUM_REC][2];
UINT32 WMM_ACM_TimeAmsduId = 0, WMM_ACM_TimeAmsduRecId = 0;
#endif /* WMM_ACM_PKT_NUM_DEBUG */

UINT32 ACMP_MsduClassify(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	ACMR_STA_DB				*pCdb,
	ACM_PARAM_IN	ACMR_MBUF				*pMbuf,
	ACM_PARAM_IN	UINT32					QueueTypeCur,
	ACM_PARAM_IN	UCHAR					FlgIsForceToHighAc,
	ACM_PARAM_IN	UCHAR					AggType,
	ACM_PARAM_IN	UCHAR					AggId)
{
#ifdef ACM_CC_FUNC_SOFT_ACM
#ifdef ACM_CC_FUNC_QUE_TX_CTRL
	ACM_STREAM *pStream;
	ACM_ENTRY_INFO *pStaAcmInfo;
	ACM_CTRL_PARAM *pEdcaParam;
	ACM_STATISTICS *pStats;

	UCHAR *pPkt; /* same as pMbuf */
	UINT32 PktLen;
	UINT32 TxTime;
	UCHAR  TSID;
#ifndef PERFORMANCE_IMPACT_TEST
	UINT32 PktAcId;
	UINT32 TxQueueType;
#endif /* PERFORMANCE_IMPACT_TEST */
	ULONG  SplFlags;


	WMM_ACM_FUNC_NAME_PRINT("IN");

    /* sanity check */
    if (pCdb == NULL)
        return QueueTypeCur;
    /* End of if */

	/* init */
	TxTime = RTMP_GET_PACKET_TX_TIME(pMbuf);
	if (TxTime == 0)
		return QueueTypeCur;
	/* End of if */

	pPkt = (UCHAR *)ACMR_WLAN_PKT_GET(pMbuf);
	PktLen = ACMR_WLAN_LEN_GET(pMbuf);

	if (AggType == ACMR_AGG_RALINK)
	{
		/* TODO */
	} /* End of if */

	/* get management semaphore */
	ACM_TSPEC_IRQ_LOCK_CHK_RTN(pAd, SplFlags, LabelSemErr, ACM_CLSFY_NOT_ALLOW);

	pEdcaParam = &(ACMR_CB->EdcaCtrlParam);
	pStats = &pEdcaParam->Stats;

	/* get TSPEC */
	pStaAcmInfo = ACMR_STA_ACM_PARAM_INFO(pCdb);
	TSID = RTMP_GET_PACKET_STM_TSID(pMbuf);
	pStream = (ACM_STREAM *)pStaAcmInfo->pAcStmOut[TSID];

#ifdef ACM_CC_FUNC_11N_AGG
	/* aggregation check */
	if ((pStream != NULL) &&
		(pStream->pTspec->TsInfo.AckPolicy == ACM_ACK_POLICY_NORMAL))
	{
		/*
			WLAN Header + A-MSDU Subframe 1 + A-MSDU Subframe 2 + ...

			A-MSDU Subframe is as below:
				DA(6) + SA(6) + Length(2) + MSDU + Padding (0~3)

			Nothing to do for 1st A-MSDU.
		*/
		if ((AggType == ACMR_AGG_AMSDU) && (AggId > 1))
		{
			/* re-calculate TX time */
#ifdef ACM_CC_FUNC_AUX_TX_TIME
			ACM_TX_TimeCalHT(pAd, NULL,
								PktLen+FRM_LENGTH_AGG_AMSDU_HDR, /* body len */
								McsId,					/* MCS Index */
								ACMR_IS_2040_STA(pCdb),	/* 20 or 20/40 MHz */
								0,				/* regular or short GI */
								0,				/* rts/cts */
								0,				/* no ack */
								1,				/* no AMPDU or fist pkt in AMPDU */
								0xFFFFFFFF,		/* txop limit */
								0,				/* do not care */
								NULL,			/* no data tx time */
								NULL,			/* wlan header tx time */
								NULL,			/* ack tx time */
								NULL, 			/* data+hdr only tx time */
								&TxTime);		/* data only tx time */
#else
			UINT32 LenDataId;
			UINT32 McsId;

			McsId = ACMR_CLIENT_MCS_GET(pCdb);

			LenDataId = (PktLen+FRM_LENGTH_AGG_AMSDU_HDR);
			LenDataId >>= ACM_PRE_TIME_DATA_SIZE_OFFSET;

#if 0
		if (LenDataId > 0)
			LenDataId --; /* use smaller length */
			/* End of if */
#endif /* 0 */

			if (LenDataId >= ACM_PRE_TIME_DATA_SIZE_NUM)
				LenDataId = ACM_PRE_TIME_DATA_SIZE_NUM-1;
			/* End of if */

			/* data time */
			TxTime = gAcmTxTimeBodyHT\
								[ACMR_IS_2040_STA(pCdb)][0][McsId][LenDataId][2];
#endif /* ACM_CC_FUNC_AUX_TX_TIME */
		} /* End of if */
	} /* End of if */
#endif /* ACM_CC_FUNC_11N_AGG */

	/* check if tx time > TXOP limit for AC2 & AC3 */
	if (pStream != NULL)
	{
		UINT64 Timestamp, TimeOffset;
		UINT32 TimeAllowed;


		/* get 64-bit Timestamp */
		ACMR_TIMESTAMP_GET(pAd, Timestamp);

		/* translate MediumTime (unit: 32us) to us */
		ACMR_ALLOWED_TIME_GET(pStream, TimeAllowed);

		TimeOffset = (UINT64)((INT64)Timestamp - \
								(INT64)pStream->TxTimestampMarkTransmit);

		if (TimeOffset >= ACM_TIME_BASE)
		{
			/* the first packet in next second */
			pStream->TxTimestampMarkTransmit = Timestamp;

			/* reset the used time */
			if (pStream->AcmUsedTimeTransmit > TimeAllowed)
				pStream->AcmUsedTimeTransmit -= TimeAllowed;
			else
				pStream->AcmUsedTimeTransmit = 0;
			/* End of if */

#ifdef WMM_ACM_PKT_NUM_DEBUG
			WMM_ACM_TimeAmsduRecId ++;

			if (WMM_ACM_TimeAmsduRecId == 5)
				NdisZeroMemory(WMM_ACM_TimeAmsdu, sizeof(WMM_ACM_TimeAmsdu));
			/* End of if */
#endif /* WMM_ACM_PKT_NUM_DEBUG */
		}
		else
		{
#ifndef PERFORMANCE_IMPACT_TEST
			/* during a second */
			if (pStream->AcmUsedTimeTransmit > TimeAllowed)
			{
				/* can not tx the packet to the AC so check downgrade flag */
				PktAcId = pStream->AcmAcId;

				if (ACMR_CB->EdcaCtrlParam.DowngradeAcNum[PktAcId] != \
														ACM_DOWNGRADE_DISABLE)
				{
					TxQueueType = ACM_TxQueueTypeGet( \
							ACMR_CB->EdcaCtrlParam.DowngradeAcNum[PktAcId]);

					ACM_STATS_COUNT_INC(pStats->Downgrade[PktAcId]);

					/* need to change the UP of QoS Control field */
					TSID = gEDCA_AC_UP[pEdcaParam->DowngradeAcNum[PktAcId]];
					ACMR_PKT_MARK_UP(pMbuf, TSID);

					goto LabelDowngrade;
				} /* End of if */

				ACM_STATS_COUNT_INC(pStats->DropByAdmittedTime);

				/* downgrade function is disabled so discarding the packet */
				goto LabelDiscard;
			} /* End of if */
#endif /* PERFORMANCE_IMPACT_TEST */

#ifdef WMM_ACM_PKT_NUM_DEBUG
			if (WMM_ACM_TimeAmsduRecId == 5)
			{
				WMM_ACM_TimeAmsdu[WMM_ACM_TimeAmsduId][0] = TxTime;
				WMM_ACM_TimeAmsdu[WMM_ACM_TimeAmsduId++][1] = \
												pStream->AcmUsedTimeTransmit;

				if (WMM_ACM_TimeAmsduId >= WMM_ACM_DEBUG_TIME_MAX_NUM_REC)
					WMM_ACM_TimeAmsduId = 0;
				/* End of if */
			} /* End of if */
#endif /* WMM_ACM_PKT_NUM_DEBUG */
		} /* End of if */

		/* accumulate used time */
		pStream->AcmUsedTimeTransmit += TxTime;
	} /* End of if */

	/* release semaphore */
	ACM_TSPEC_IRQ_UNLOCK(pAd, SplFlags, LabelSemErr);
	return QueueTypeCur;

LabelDowngrade:
	ACM_TSPEC_IRQ_UNLOCK(pAd, SplFlags, LabelSemErr);
	return TxQueueType;

LabelDiscard:
	ACM_TSPEC_IRQ_UNLOCK(pAd, SplFlags, LabelSemErr);
	return ACM_CLSFY_NOT_ALLOW;

LabelSemErr:
	ACMR_SEM_FAIL_PRINT();
	return ACM_CLSFY_NOT_ALLOW;
#else

	/* no ACM in packet dequeue */
	return QueueTypeCur;
#endif /* ACM_CC_FUNC_QUE_TX_CTRL */

#else

	/* no hardware ACM */
	return QueueTypeCur;
#endif /* ACM_CC_FUNC_SOFT_ACM */
} /* End of ACMP_MsduClassify */


/*
========================================================================
Routine Description:
	Get new adjust parameters for non-ACM AC.

Arguments:
	pAd				- WLAN control block pointer
	*pEdcaParam		- the parameters

Return Value:
	ACM_RTN_OK		- get ok
	ACM_RTN_FAIL	- get fail

Note:
	Only for QAP.
========================================================================
*/
VOID ACMP_NonAcmAdjustParamUpdate(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	UCHAR					*pEdcaParam)
{
#ifdef CONFIG_AP_SUPPORT
#ifdef ACM_CC_FUNC_CHAN_UTIL_MONITOR
	ULONG  SplFlags;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	if (!ACMR_SANITY_CHECK(pAd))
		return;
	/* End of if */

	if (pEdcaParam == NULL)
		return;
	/* End of if */

	ACM_TSPEC_IRQ_LOCK_CHK(pAd, SplFlags, LabelSemErr);

	if (ACMR_CB->CU_MON_FlgChangeNeed != 0)
	{
		/* update WMM Parameters to AP CSR */
		EDCA_AC_CFG_STRUC CsrCfgAc0, CsrCfgAc1, CsrCfgAc2, CsrCfgAc3;
		AIFSN_CSR_STRUC CsrAifsn;
		UCHAR AIFSN[ACM_DEV_NUM_OF_AC];


		ACMR_MEM_COPY(AIFSN, ACMR_CB->CU_MON_AifsnAp, ACM_DEV_NUM_OF_AC);

		CsrAifsn.field.Aifsn0 = AIFSN[0];
		CsrAifsn.field.Aifsn1 = AIFSN[1];
		CsrAifsn.field.Aifsn2 = AIFSN[2];
		CsrAifsn.field.Aifsn3 = AIFSN[3];

		RTMP_IO_WRITE32(pAd, WMM_AIFSN_CFG, CsrAifsn.word);

		RTMP_IO_READ32(pAd, EDCA_AC0_CFG, &CsrCfgAc0.word);
		CsrCfgAc0.field.Aifsn = AIFSN[0];
		RTMP_IO_WRITE32(pAd, EDCA_AC0_CFG, CsrCfgAc0.word);

		RTMP_IO_READ32(pAd, EDCA_AC1_CFG, &CsrCfgAc1.word);
		CsrCfgAc1.field.Aifsn = AIFSN[1];
		RTMP_IO_WRITE32(pAd, EDCA_AC1_CFG, CsrCfgAc1.word);

		RTMP_IO_READ32(pAd, EDCA_AC2_CFG, &CsrCfgAc2.word);
		CsrCfgAc2.field.Aifsn = AIFSN[2];
		RTMP_IO_WRITE32(pAd, EDCA_AC2_CFG, CsrCfgAc2.word);

		RTMP_IO_READ32(pAd, EDCA_AC3_CFG, &CsrCfgAc3.word);
		CsrCfgAc3.field.Aifsn = AIFSN[3];
		RTMP_IO_WRITE32(pAd, EDCA_AC3_CFG, CsrCfgAc3.word);
	} /* End of if */

	/* update to BSS beacon */
	ACMR_MEM_COPY(pEdcaParam, ACMR_CB->CU_MON_AifsnBss, ACM_DEV_NUM_OF_AC);
	ACMR_CB->CU_MON_FlgChangeNeed = 0;

	ACM_TSPEC_IRQ_UNLOCK(pAd, SplFlags, LabelSemErr);
	return;

LabelSemErr:
	ACMR_SEM_FAIL_PRINT();
	return;
#endif /* ACM_CC_FUNC_CHAN_UTIL_MONITOR */
#endif /* CONFIG_AP_SUPPORT */
} /* End of ACMP_NonAcmAdjustParamUpdate */


/*
========================================================================
Routine Description:
	Get current number of input TSPEC for the UP.

Arguments:
	*pCdb			- the QSTA
	UP				- the UP

Return Value:
	Number of TSPEC

Note:
========================================================================
*/
UINT32 ACMP_NumOfAcTspecInGet(
	ACM_PARAM_IN		ACMR_STA_DB			*pCdb,
	ACM_PARAM_IN		UCHAR				UP)
{
	UINT32 AcId = ACM_MR_EDCA_AC(UP);


	/* ACM_NumOfInTspecInAc[] is a array, skip to protect to speed up */
	return pCdb->ACM_NumOfInTspecInAc[AcId];
} /* End of ACMP_NumOfAcTspecInGet */

/*
========================================================================
Routine Description:
	Get current number of output TSPEC for the UP.

Arguments:
	*pCdb			- the QSTA
	UP				- the UP

Return Value:
	Number of TSPEC

Note:
========================================================================
*/
UINT32 ACMP_NumOfAcTspecOutGet(
	ACM_PARAM_IN		ACMR_STA_DB			*pCdb,
	ACM_PARAM_IN		UCHAR				UP)
{
	UINT32 AcId = ACM_MR_EDCA_AC(UP);


	/* ACM_NumOfOutTspecInAc[] is a array, skip to protect to speed up */
	return pCdb->ACM_NumOfOutTspecInAc[AcId];
} /* End of ACMP_NumOfAcTspecOutGet */


/*
========================================================================
Routine Description:
	Check if the current tx PHY Mode and MCS > minimum PHY Mode and MCS.

Arguments:
	pAd				- WLAN control block pointer
	*pMbuf			- the frame expected to transmit
	FlgIs2040		- 1: the packet uses 40MHz
	FlgIsShortGI	- 1: the packet uses Short GI
	PhyMode			- the PHY Mode expected to use
	Mcs				- the MCS expected to use

Return Value:
	ACM_RTN_OK		- current Mode & MCS is allowed
	ACM_RTN_FAIL	- current Mode & MCS is not allowed

Note:
========================================================================
*/
ACM_FUNC_STATUS ACMP_PacketPhyModeMCSCheck(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	ACMR_MBUF				*pMbuf,
	ACM_PARAM_IN	UCHAR					FlgIs2040,
	ACM_PARAM_IN	UCHAR					FlgIsShortGI,
	ACM_PARAM_IN	UCHAR					PhyMode,
	ACM_PARAM_IN	UCHAR					Mcs)
{
#ifdef RELEASE_EXCLUDE
	/*
		MCS Information:

		CCK PHY Mode (0)		1M	2M	5.5M	11M
			Short Preamble MCS:	0	1	2		3
			Long Preamble MCS:	8	9	10		11

		OFDM PHY Mode (1)		6M	9M	12M	18M	24M	36M	48M	54M
			MCS:				0	1	2	3	4	5	6	7

		HT PHY Mode (2)			reference to gAcmMCS_HT[]
	*/
#endif /* RELEASE_EXCLUDE */

	UINT32 RateMin, Rate;
	UCHAR PhyModeMin, McsMin;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* get minimum rate information from the packet */
	ACMR_PKT_MIN_PHY_MODE_GET(pMbuf, PhyModeMin);

	if (PhyModeMin == ACMR_PHY_NONE)
	{
		/* no minimum rate requirement for the packet, do NOT need to check */
		return ACM_RTN_OK;
	} /* End of if */

	/* init */
	ACMR_PKT_MIN_PHY_MCS_GET(pMbuf, McsMin);

	/* get minimum physical rate */
	switch(PhyModeMin)
	{
		case ACMR_PHY_CCK:
			if (McsMin <= ACM_CCK_LPM_MAX_MCS)
			{
				/* long preamble */
				RateMin = gAcmMCS_CCK[0][McsMin][1];
			}
			else if (McsMin <= ACM_CCK_SPM_MAX_MCS)
			{
				/* short preamble */
				RateMin = gAcmMCS_CCK[1][McsMin - ACM_CCK_SPM_MIN_MCS][1];
			}
			else
				RateMin = gAcmMCS_CCK[0][0][1];
			/* End of if */
			break;

		case ACMR_PHY_OFDM:
			RateMin = gAcmMCS_OFDM[McsMin][1];
			break;

#ifdef ACM_CC_FUNC_11N
		case ACMR_PHY_HT:
			/* always use regular GI */
			RateMin = gAcmMCS_HT[FlgIs2040][0][McsMin];
			break;
#endif /* ACM_CC_FUNC_11N */

		default:
			/* no minimum rate requirement for the packet, do NOT need to check */
			return ACM_RTN_OK;
	} /* End of switch */

	/* get current physical rate */
	switch(PhyMode)
	{
		case ACMR_PHY_CCK:
			if (Mcs <= ACM_CCK_LPM_MAX_MCS)
			{
				/* long preamble */
				Rate = gAcmMCS_CCK[0][Mcs][1];
			}
			else if (Mcs <= ACM_CCK_SPM_MAX_MCS)
			{
				/* short preamble */
				Rate = gAcmMCS_CCK[1][Mcs - ACM_CCK_SPM_MIN_MCS][1];
			}
			else
				Rate = gAcmMCS_CCK[0][0][1];
			/* End of if */
			break;

		case ACMR_PHY_OFDM:
			Rate = gAcmMCS_OFDM[Mcs][1];
			break;

#ifdef ACM_CC_FUNC_11N
		case ACMR_PHY_HT:
			Rate = gAcmMCS_HT[FlgIs2040][FlgIsShortGI][Mcs];
			break;
#endif /* ACM_CC_FUNC_11N */

		default:
			/* no minimum rate requirement for the packet, do NOT need to check */
			return ACM_RTN_OK;
	} /* End of switch */

	/* check if current used rate is not smaller than the minimum rate */
	if (Rate < RateMin)
		return ACM_RTN_FAIL;
	/* End of if */

	return ACM_RTN_OK;
} /* End of ACMP_PacketPhyModeMCSCheck */


/*
========================================================================
Routine Description:
	Return power save right to system.

Arguments:
	pAd				- WLAN control block pointer

Return Value:
	None

Note:
	1. Only for STATION mode.
	2. We will return PS right when no any pending ADDTS request frame.
========================================================================
*/
VOID ACMP_StaPsCtrlRightReturn(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd)
{
#ifdef CONFIG_STA_SUPPORT
#ifndef ACM_CC_FUNC_PS_MGMT_FME
	ACM_STREAM *pStreamReq;
	ULONG SplFlags;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* init */
	ACM_TSPEC_SEM_LOCK_CHK(pAd, SplFlags, LabelSemErr);
	pStreamReq = ACMR_CB->TspecListReq.pHead;
	ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);

	/* only return the PS right when no any requested TSPEC is pending */
	if (pStreamReq == NULL)
	{
		/* no any requested TSPEC exists */
		ACMR_STA_PS_MODE_RECOVER(pAd);
	} /* End of if */
	return;

LabelSemErr:
	ACMR_SEM_FAIL_PRINT();
#endif /* ACMR_STA_PS_MODE_RECOVER */
#endif /* CONFIG_STA_SUPPORT */

	return;
} /* End of ACMP_StaPsCtrlRightReturn */


#ifdef CONFIG_AP_SUPPORT
/*
========================================================================
Routine Description:
	Used to signal WMM ACM Non-ACM TSPEC response support in the AP.

Arguments:
	pAd				- WLAN control block pointer
	*pElementWme	- the WMM Parameter IE

Return Value:
	None

Note:
	Used in QAP.

	Now that we've agreed that WMM-AC APs shall respond to/accept TSPEC
	requests on non-ACM ACs, how about setting b6 of the "Reserved" octet
	in the WMM Parameter IE .
========================================================================
*/
VOID ACMP_NullTspecSupportSignal(
	ACM_PARAM_IN		ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN_OUT	UCHAR					*pElementWme)
{
	if (!ACMR_SANITY_CHECK(pAd))
		return;
	/* End of if */

	pElementWme[9] |= 0x40; /* bit 6 in reserved field */
} /* End of ACMP_NullTspecSupportSignal */


/*
========================================================================
Routine Description:
	Update UAPSD states after ADDTS Response or DELTS is sent out.

Arguments:
	pAd				- WLAN control block pointer
	*pCdb			- the QSTA
	*pActFrameBody	- the action frame

Return Value:
	None

Note:
	Used in QAP.
========================================================================
*/
ACM_EXTERN VOID ACMP_PsRspDeltsSentOutHandle(
	ACM_PARAM_IN		ACMR_PWLAN_STRUC	pAd,
	ACM_PARAM_IN		ACMR_STA_DB			*pCdb,
	ACM_PARAM_IN_OUT	UCHAR				*pActFrameBody)
{
	ACM_WME_NOT_FRAME *pNotFrame;
	ACM_STREAM **ppAcmStmList, *pStream;
	UINT32 TID, Direction;
	UCHAR AcId, UP;
	ULONG SplFlags;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* sanity check */
	if ((pCdb == NULL) || (!ACMR_SANITY_CHECK(pAd)))
		return;
	/* End of if */

	/* get TSID & Direction */
	pNotFrame = (ACM_WME_NOT_FRAME *)pActFrameBody;

	if (pNotFrame->Category != ACM_CATEGORY_WME)
		return; /* not WME action frame */
	/* End of if */

	/* DELTS */
	if (pNotFrame->Action == ACM_ACTION_WME_TEAR_DOWN)
	{
		ACM_TS_INFO TsInfo;

		/* get TID from the DELTS frame */
		ACMR_MEM_ZERO(&TsInfo, sizeof(TsInfo));
		TsInfo.TSID = pNotFrame->ElmTspec.Tspec.TsInfo.TID;

		/* handle the DELTS frame */
		ACM_TSPEC_IRQ_LOCK_CHK(pAd, SplFlags, LabelSemErr);
		ACM_DeltsFrameACK(pAd, ACMR_CLIENT_MAC(pCdb), (UCHAR *)&TsInfo, 0);
		ACM_TSPEC_IRQ_UNLOCK(pAd, SplFlags, LabelSemErr);

		ACMP_FrameBwAnnSend(pAd, FALSE);
		return; /* handle DELTS ok for power-save station */
	} /* End of if */

	/* ADDTS Response */
	if (pNotFrame->StatusCode != WLAN_STATUS_CODE_WME_ACM_ACCEPTED)
		return; /* only care about SUCCESS */
	/* End of if */

	Direction = pNotFrame->ElmTspec.Tspec.TsInfo.Direction;

	ACM_TSPEC_IRQ_LOCK_CHK(pAd, SplFlags, LabelSemErr);

	/* check if we need to update UAPSD state after the RESPONSE is sent out */
	if ((Direction == ACM_DIRECTION_BIDIREC_LINK) ||
		(Direction == ACM_DIRECTION_DOWN_LINK))
	{
		ppAcmStmList = (ACM_STREAM **)ACM_StationTspecListGet(
						pAd, ACMR_CLIENT_MAC(pCdb), ACM_PEER_TSPEC_OUTPUT_GET);
	}
	else
	{
		ppAcmStmList = (ACM_STREAM **)ACM_StationTspecListGet(
						pAd, ACMR_CLIENT_MAC(pCdb), ACM_PEER_TSPEC_INPUT_GET);
	} /* End of if */

	if (ppAcmStmList == NULL)
		goto LabelOK;
	/* End of if */

	TID = pNotFrame->ElmTspec.Tspec.TsInfo.TID;
	pStream = ppAcmStmList[TID];

	if ((pStream != NULL) && (pStream->FlgUapsdHandleNeed))
	{
		/* we need to reset UAPSD state for the PS TSPEC */
		UP = pNotFrame->ElmTspec.Tspec.TsInfo.UP;

		ACMR_DEBUG(ACMR_DEBUG_TRACE,
					("acm_msg> ADDTS Rsp is sent! (TID=%d, DIR=%d, UP=%d)\n",
					TID, Direction, UP));

		AcId = ACM_MR_EDCA_AC(UP);

		ACM_APSD_Ctrl(pAd, pCdb, AcId,
						pStream->pTspec->TsInfo.Direction,
						1, pStream->pTspec->TsInfo.APSD);

		pStream->FlgUapsdHandleNeed = 0; /* handle ok */
	} /* End of if */

LabelOK:
	/* release semaphore */
	ACM_TSPEC_IRQ_UNLOCK(pAd, SplFlags, LabelSemErr);
    return;

LabelSemErr:
	ACMR_SEM_FAIL_PRINT();
	return;
} /* End of ACMP_PsRspDeltsSentOutHandle */


/*
========================================================================
Routine Description:
	Handle the resource allocation.

Arguments:
	pAd				- WLAN control block pointer
	*pCdb			- the source QSTA
	*pBufRscReq		- the buffer which includes the TSPEC request
	*pBufRscRsp		- the buffer where we can put TSPEC response
	*pBufRspLen		- the respone frame length

Return Value:
	ACM_RTN_OK		- handle ok
	ACM_RTN_FAIL	- handle fail

Note:
	1. Used in QAP.
	2. Currently only TSPEC element for request/response, no TCLAS.
========================================================================
*/
ACM_EXTERN ACM_FUNC_STATUS ACMP_ResourceAllocate(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd,
	ACM_PARAM_IN	ACMR_STA_DB			*pCdb,
	ACM_PARAM_IN	UCHAR				*pBufRscReq,
	ACM_PARAM_OUT	UCHAR				*pBufRscRsp,
	ACM_PARAM_OUT	UINT32				*pBufRspLen)
{
	ACM_ELM_WME_TSPEC *pElmWmeTspec;
	ACM_TCLAS *pTclas[ACM_TSPEC_TCLAS_MAX_NUM];
	ACM_TSPEC Tspec;
	UINT32 TclasNum;
	UCHAR TclasProcessing;
	UCHAR StatusCode;
	UINT16 MediumTime;
	ACM_FUNC_STATUS RtnCode;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* sanity check */
	pElmWmeTspec = (ACM_ELM_WME_TSPEC *)pBufRscReq;

	if ((pElmWmeTspec->ElementId != ACM_ELM_WME_ID) ||
		(pElmWmeTspec->OUI[0] != ACM_WME_OUI_0) ||
		(pElmWmeTspec->OUI[1] != ACM_WME_OUI_1) ||
		(pElmWmeTspec->OUI[2] != ACM_WME_OUI_2) ||
		(pElmWmeTspec->OUI_Type != ACM_WME_OUI_TYPE) ||
		(pElmWmeTspec->OUI_SubType != ACM_WME_OUI_SUBTYPE_TSPEC) ||
		(pElmWmeTspec->Version != ACM_WME_OUI_VERSION))
	{
		ACMR_DEBUG(ACMR_DEBUG_TRACE,
					("acm_msg> Not WME TSPEC! ACMP_ResourceAllocate()\n"));
		goto label_fail; /* sanity check fail */
	} /* End of if */

	/* translate WME TSPEC to 11e TSPEC */
	/* skip 4B Category, action, DialogToken, & StatusCode */
	if (ACM_WME_11E_TSPEC_TCLAS_Translate(
										pBufRscReq,
										4+sizeof(ACM_ELM_WME_TSPEC),
										&Tspec,
										pTclas,
										&TclasNum,
										&TclasProcessing) != ACM_RTN_OK)
	{
		ACMR_DEBUG(ACMR_DEBUG_ERR,
					("acm_err> Translate TSPEC fail! "
					"ACMP_ResourceAllocate()\n"));
		goto label_fail; /* translate fail */
	} /* End of if */

	/* handle the request */
	RtnCode = ACM_TC_ReqHandle(
							pAd, pCdb, ACM_STREAM_TYPE_WIFI,
							0, &Tspec,
							TclasNum, pTclas,
							TclasProcessing,
							0, &StatusCode, &MediumTime);

	if (RtnCode != ACM_RTN_OK)
	{
		ACMR_DEBUG(ACMR_DEBUG_TRACE,
					("acm_msg> A WME Setup request is not allowed %d! "
					"ACMP_ResourceAllocate()\n", RtnCode));
		goto label_fail; /* allocate fail */
	}
	else
		pElmWmeTspec->Tspec.MediumTime = MediumTime;
	/* End of if */

	/* copy response content */
	ACMR_MEM_COPY(pBufRscRsp, pElmWmeTspec, sizeof(ACM_ELM_WME_TSPEC));
	*pBufRspLen = sizeof(ACM_ELM_WME_TSPEC);

	/* send a broadcast private ACTION frame to advise used ACM time in AP */
	ACMP_FrameBwAnnSend(pAd, FALSE);

	return ACM_RTN_OK;

label_fail:
	/* copy response content */
	ACMR_MEM_COPY(pBufRscRsp, pElmWmeTspec, sizeof(ACM_ELM_WME_TSPEC));
	*pBufRspLen = sizeof(ACM_ELM_WME_TSPEC);

	return ACM_RTN_FAIL;
} /* End of ACMP_ResourceAllocate */


/*
========================================================================
Routine Description:
	Update the UAPSD state based on current all TSPECs.

Arguments:
	pAd				- WLAN control block pointer
	*pCdb			- the QSTA

Return Value:
	None

Note:
	Used in QAP.

	Use the function after reassociation request.

	Because TSPEC is not deleted after reassociation request, we need
	to update new UAPSD state based on these TSPEC and recover to some
	static settings in reassociation frame after any TSPEC is deleted.
========================================================================
*/
VOID ACMP_UAPSD_StateUpdate(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	ACMR_STA_DB				*pCdb)
{
	ACM_STREAM **ppAcmStmList;
	ACM_TS_INFO *pTsInfo;
	UCHAR DirectionId[2] = \
					{ ACM_PEER_TSPEC_OUTPUT_GET, ACM_PEER_TSPEC_INPUT_GET };
	UINT32 IdTidNum, IdLinkNum;
	UCHAR AcId, Direction, UP, APSD;
	ULONG SplFlags;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* sanity check */
	if ((pCdb == NULL) || (!ACMR_SANITY_CHECK(pAd)))
		return;
	/* End of if */

	/* get management semaphore */
	ACM_TSPEC_IRQ_LOCK_CHK(pAd, SplFlags, LabelSemErr);

	/* check all output and input streams for the peer device */
	for(IdLinkNum=0; IdLinkNum<2; IdLinkNum++)
	{
		ppAcmStmList = (ACM_STREAM **)ACM_StationTspecListGet(
						pAd, ACMR_CLIENT_MAC(pCdb), DirectionId[IdLinkNum]);
		if (ppAcmStmList == NULL)
			break;
		/* End of if */

		for(IdTidNum=0; IdTidNum<ACM_STA_TID_MAX_NUM; IdTidNum++)
		{
			if (ppAcmStmList[IdTidNum] != NULL)
			{
				pTsInfo = &(ppAcmStmList[IdTidNum]->pTspec->TsInfo);

				Direction = pTsInfo->Direction;
				UP = pTsInfo->UP;
				APSD = pTsInfo->APSD;

				AcId = ACM_MR_EDCA_AC(UP);

				if (Direction == ACM_DIRECTION_BIDIREC_LINK)
				{
					pCdb->bAPSDCapablePerAC[AcId] = APSD;
					pCdb->bAPSDDeliverEnabledPerAC[AcId] = APSD;
				}
				else if (Direction == ACM_DIRECTION_DOWN_LINK)
				{
					pCdb->bAPSDDeliverEnabledPerAC[AcId] = APSD;
				}
				else if (Direction == ACM_DIRECTION_UP_LINK)
				{
					pCdb->bAPSDCapablePerAC[AcId] = APSD;
				} /* End of if */
			} /* End of if */
		} /* End of for */
	} /* End of while */

	/* release semaphore */
	ACM_TSPEC_IRQ_UNLOCK(pAd, SplFlags, LabelSemErr);

    if ((pCdb->bAPSDDeliverEnabledPerAC[QID_AC_BE] == 1) &&
        (pCdb->bAPSDDeliverEnabledPerAC[QID_AC_BK] == 1) &&
        (pCdb->bAPSDDeliverEnabledPerAC[QID_AC_VI] == 1) &&
        (pCdb->bAPSDDeliverEnabledPerAC[QID_AC_VO] == 1))
    {
        /* all AC are U-APSD delivery-enabled */
        pCdb->bAPSDAllAC = 1;

		ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> all AC are UAPSD!\n"));
    }
    else
    {
        /* at least one AC is not U-APSD delivery-enabled */
        pCdb->bAPSDAllAC = 0;

		ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> not all AC are UAPSD!\n"));
    } /* End of if */

    return;

LabelSemErr:
	ACMR_SEM_FAIL_PRINT();
	return;
} /* End of ACMP_UAPSD_StateUpdate */
#endif /* CONFIG_AP_SUPPORT */


/*
========================================================================
Routine Description:
	Get ACM related statistics counts.

Arguments:
	pAd				- WLAN control block pointer
	*pStats			- the statistics counts

Return Value:
	TRUE			- get ok
	FALSE			- get fail

Note:
========================================================================
*/
BOOLEAN ACMP_StatisticsGet(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_OUT	ACM_STATISTICS			*pStats)
{
	ACM_CTRL_PARAM *pEdcaParam;
	ULONG SplFlags;


	/* sanity check for WMM */
	if (!ACMR_SANITY_CHECK(pAd))
		return FALSE;
	/* End of if */

	/* get management semaphore */
	ACM_TSPEC_SEM_LOCK_CHK_RTN(pAd, SplFlags, LabelSemErr, FALSE);

	pEdcaParam = &(ACMR_CB->EdcaCtrlParam);
	*pStats = pEdcaParam->Stats;

	/* release semaphore */
	ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);
	return TRUE;

LabelSemErr:
	ACMR_SEM_FAIL_PRINT();
	return FALSE;
} /* End of ACMP_StatisticsGet */


/*
========================================================================
Routine Description:
	Delete a QSTA due to deauthentication or deassociation, etc.

Arguments:
	pAd				- WLAN control block pointer
	*pCdb			- the QSTA

Return Value:
	None

Note:
	1. Used in QAP and QSTA.
	2. Before any entry deletion, you must call the function to release TS first.
========================================================================
*/
VOID ACMP_StationDelete(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	ACMR_STA_DB				*pCdb)
{
	ACM_ENTRY_INFO *pStaAcmInfo;
	ACM_STREAM *pStream;
	UINT32 IdTidNum;
	ULONG SplFlags;


	WMM_ACM_FUNC_NAME_PRINT("IN");

#ifdef CONFIG_STA_SUPPORT
	if (ACMR_IS_STA_MODE(pAd))
	{
		/* sanity check for WMM */
		if (!ACMR_SANITY_CHECK(pAd))
			return;
		/* End of if */

		/* recover UAPSD state */

		/*
			Note: must put before ACMR_IS_ENABLED() because we can change PS mode
			without ACM bit set.
		*/
		pAd->CommonCfg.bAPSDAC_VO = \
							pAd->CommonCfg.bACMAPSDBackup[ACM_EDCA_VO_AC_QUE_ID];
		pAd->CommonCfg.bAPSDAC_VI = \
							pAd->CommonCfg.bACMAPSDBackup[ACM_EDCA_VI_AC_QUE_ID];
		pAd->CommonCfg.bAPSDAC_BK = \
							pAd->CommonCfg.bACMAPSDBackup[ACM_EDCA_BK_AC_QUE_ID];
		pAd->CommonCfg.bAPSDAC_BE = \
							pAd->CommonCfg.bACMAPSDBackup[ACM_EDCA_BE_AC_QUE_ID];

#if 0
		/*
			2009/07/23
			We can not reset bAPSDCapable to 0.
			The flag is a static flag from RT28xxSTA.dat
			We can not modify it in any time.

			Or after AP disassociates us, MaxSPLength is always 0 in next
			association request frame due to pAd->CommonCfg.bAPSDCapable = 0 of
			MlmeAssocReqAction().
		*/
		if ((pAd->CommonCfg.bAPSDAC_VO == FALSE) &&
			(pAd->CommonCfg.bAPSDAC_VI == FALSE) &&
			(pAd->CommonCfg.bAPSDAC_BE == FALSE) &&
			(pAd->CommonCfg.bAPSDAC_BK == FALSE))
		{
			pAd->CommonCfg.bAPSDCapable = FALSE;
		}
		else
			pAd->CommonCfg.bAPSDCapable = TRUE;
		/* End of if */
#endif
	} /* End of if */
#endif /* CONFIG_STA_SUPPORT */

	/* sanity check for ACM */
	if ((pCdb == NULL) || (!ACMR_SANITY_CHECK(pAd)))
		return;
	/* End of if */

    ACMR_DEBUG(ACMR_DEBUG_TRACE,
				("acm_msg> Station associates or is deleted! "
                "ACMP_StationDelete()\n"));

	/* get management semaphore */
	ACM_TSPEC_SEM_LOCK_CHK(pAd, SplFlags, LabelSemErr);

	/* free all requested TSPECs for the device entry if exists */
	ACM_TC_ReqDeviceFree(pAd, pCdb);

	/* free all TSPECs silently without sending DELTS frames */
	pStaAcmInfo = ACMR_STA_ACM_PARAM_INFO(pCdb);

	for(IdTidNum=0; IdTidNum<ACM_STA_TID_MAX_NUM; IdTidNum++)
	{
		/* for OUT stream */
		pStream = (ACM_STREAM *)(pStaAcmInfo->pAcStmOut[IdTidNum]);

		if (pStream != NULL)
			ACM_TC_Discard(pAd, pStream);
		/* End of if */

		pStaAcmInfo->pAcStmOut[IdTidNum] = NULL;

		/* for IN stream */
		pStream = (ACM_STREAM *)pStaAcmInfo->pAcStmIn[IdTidNum];

		if (pStream != NULL)
			ACM_TC_Discard(pAd, pStream);
		/* End of if */

		pStaAcmInfo->pAcStmIn[IdTidNum] = NULL;
	} /* End of for */

	/* free the peer device record ever reserving bandwidth */
	ACM_PeerDeviceDel(pAd, ACMR_CLIENT_MAC(pCdb));

	/* release semaphore */
	ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);

	/* announce new bandwidth */
	ACMP_FrameBwAnnSend(pAd, FALSE);
	return;

LabelSemErr:
	ACMR_SEM_FAIL_PRINT();
	return;
} /* End of ACMP_StationDelete */


/*
========================================================================
Routine Description:
	Clear failed stream information.

Arguments:
	pAd			- WLAN control block pointer

Return Value:
	None

Note:
========================================================================
*/
VOID ACMP_StreamFailClear(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd)
{
	ACM_TSPEC_REQ_LIST *pTspecFreedList;
	ULONG SplFlags;


	/* sanity check */
	if (!ACMR_IS_ENABLED(pAd))
	{
	    ACMR_DEBUG(ACMR_DEBUG_TRACE,
					("acm_msg> WMM ACM is disabled! StreamFailClear()\n"));
		return;
	} /* End of if */

	/* get management semaphore */
	ACM_TSPEC_SEM_LOCK_CHK(pAd, SplFlags, LabelSemErr);

	/* free "all" failed stream records */
	pTspecFreedList = &ACMR_CB->TspecListFail;
	ACM_LIST_ALL_FREE(pAd, pTspecFreedList);

	/* release semaphore */
	ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);
	return;

LabelSemErr:
	ACMR_SEM_FAIL_PRINT();
	return;
} /* End of ACMP_StreamFailClear */


/*
========================================================================
Routine Description:
	Get some streams information.

Arguments:
	pAd				- WLAN control block pointer
	Category		-	ACM_STM_CATEGORY_REQ,
						ACM_STM_CATEGORY_ACT,
						ACM_SM_CATEGORY_PEER,
						ACM_STM_CATEGORY_ERR
	Type			- ACM_ACCESS_POLICY_EDCA
	*pNumStm		- the number of streams you want, must > 0
	*pStaMac		- the QSTA MAC
	*pStreamBuf		- the stream information buffers

Return Value:
	ACM_RTN_OK		- get ok
	ACM_RTN_FAIL	- no more stream

Note:
	1. if pStream->pTspec == NULL, the function will not copy
		TSPEC information.
	2. if pStream->pTclas[i] == NULL, the function will not
		copy TCLAS information.
	3. If you want to get all stream information, you shall call
		ACMP_StreamNumGet() first.
========================================================================
*/
ACM_FUNC_STATUS ACMP_StreamsGet(
	ACM_PARAM_IN		ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN		UINT32					Category,
	ACM_PARAM_IN		UINT32					Type,
	ACM_PARAM_IN_OUT	UINT32					*pNumStm,
	ACM_PARAM_IN		UCHAR					*pStaMac,
	ACM_PARAM_OUT		ACM_STREAM_INFO			*pStreamBuf)
{
	ACM_PEER_DEV_LIST *pAcmDevList;
	ACM_STREAM *pStream, **ppAcmStmList;
	UINT32 NumWant, NumActualGot;
	UINT32 IdTidNum, IdLinkNum;
	UCHAR DirectionId[2] = \
					{ ACM_PEER_TSPEC_OUTPUT_GET, ACM_PEER_TSPEC_INPUT_GET };
	UCHAR MAC[ACM_MAC_ADDR_LEN];
	ULONG SplFlags;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* sanity check */
	if ((*pNumStm) == 0)
		return ACM_RTN_FAIL;
	/* End of if */

	/* init */
	pStream = NULL;
	NumWant = *pNumStm;
	NumActualGot = 0;

	/* get management semaphore */
	ACM_TSPEC_SEM_LOCK_CHK_RTN(pAd, SplFlags, LabelSemErr, ACM_RTN_FAIL);

	/* copy stream information */
	switch(Category)
	{
		case ACM_SM_CATEGORY_REQ: /* requested list */
			pStream = ACMR_CB->TspecListReq.pHead;

			while((pStream != NULL) && (NumActualGot < NumWant))
			{
				ACM_STM_InfoCopy(&pStreamBuf[NumActualGot++], pStream);
				pStream = pStream->pNext;
			} /* End of while */
			break;

		case ACM_SM_CATEGORY_ACT: /* output links of 'all' peers */
			pAcmDevList = NULL;

			while(1)
			{
				/* get next device */
				if (ACM_PeerDeviceGetNext(pAd, &pAcmDevList, MAC) != ACM_RTN_OK)
					break;
				/* End of if */

				/* copy output and input TS information */
				ppAcmStmList = (ACM_STREAM **)ACM_StationTspecListGet(
										pAd, MAC, ACM_PEER_TSPEC_OUTPUT_GET);

				if (ppAcmStmList == NULL)
					continue;
				/* End of if */

				for(IdTidNum=0; IdTidNum<ACM_STA_TID_MAX_NUM; IdTidNum++)
				{
					if (NumActualGot >= NumWant)
						break;
					/* End of if */

					if (ppAcmStmList[IdTidNum] != NULL)
					{
						ACM_STM_InfoCopy(&pStreamBuf[NumActualGot++],
										ppAcmStmList[IdTidNum]);
					} /* End of if */
				} /* End of for */
			}  /* End of while */
			break;

		case ACM_SM_CATEGORY_PEER: /* input and output links of a peer */
			/* 2 links: input and output TSPEC */
			for(IdLinkNum=0; IdLinkNum<2; IdLinkNum++)
			{
				ppAcmStmList = (ACM_STREAM **)ACM_StationTspecListGet(
										pAd, pStaMac, DirectionId[IdLinkNum]);

				if (ppAcmStmList == NULL)
					break;;
				/* End of if */

				for(IdTidNum=0; IdTidNum<ACM_STA_TID_MAX_NUM; IdTidNum++)
				{
					if (NumActualGot >= NumWant)
						break;
					/* End of if */

					if (ppAcmStmList[IdTidNum] != NULL)
					{
						if ((IdLinkNum == 1) &&
							(ppAcmStmList[IdTidNum]->pTspec->TsInfo.Direction == \
													ACM_DIRECTION_BIDIREC_LINK))
						{
							/* for bidirectional link, only copy one */
							continue;
						} /* End of if */

						ACM_STM_InfoCopy(&pStreamBuf[NumActualGot++],
										ppAcmStmList[IdTidNum]);
					} /* End of if */
				} /* End of for */
			} /* End of for */
			break;

		case ACM_SM_CATEGORY_ERR: /* failed list */
			pStream = ACMR_CB->TspecListFail.pHead;

			while((pStream != NULL) && (NumActualGot < NumWant))
			{
				ACM_STM_InfoCopy(&pStreamBuf[NumActualGot++], pStream);
				pStream = pStream->pNext;
			} /* End of while */
			break;

		default:
			goto LabelErr;
	} /* End of switch */

	/* release semaphore */
	ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);

	/* return actual got number */
	*pNumStm = NumActualGot;
	return ACM_RTN_OK;

LabelErr:
	/* release semaphore */
	ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);
	return ACM_RTN_FAIL;

LabelSemErr:
	ACMR_SEM_FAIL_PRINT();
	return ACM_RTN_FAIL;
} /* End of ACMP_StreamsGet */


/*
========================================================================
Routine Description:
	Get the number of streams.

Arguments:
	pAd				- WLAN control block pointer
	Category		-	ACM_STM_CATEGORY_REQ,
						ACM_STM_CATEGORY_ACT,
						ACM_SM_CATEGORY_PEER,
						ACM_STM_CATEGORY_ERR
	Type			- ACM_ACCESS_POLICY_EDCA
	*pStaMac		- the QSTA MAC

Return Value:
	current number of streams

Note:
========================================================================
*/
UINT32 ACMP_StreamNumGet(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	UINT32					Category,
	ACM_PARAM_IN	UINT32					Type,
	ACM_PARAM_IN	UCHAR					*pStaMac)
{
	ACM_PEER_DEV_LIST *pAcmDevList;
	ACM_STREAM *pStream, **ppAcmStmList;
	UINT32 NumStream, IdTidNum, IdLinkNum;
	UCHAR DirectionId[2] = \
					{ ACM_PEER_TSPEC_OUTPUT_GET, ACM_PEER_TSPEC_INPUT_GET };
	UCHAR MAC[ACM_MAC_ADDR_LEN];
	ULONG SplFlags;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* init */
	NumStream = 0;

	/* get management semaphore */
	ACM_TSPEC_SEM_LOCK_CHK_RTN(pAd, SplFlags, LabelSemErr, 0);

	switch(Category)
	{
		case ACM_SM_CATEGORY_REQ: /* requested list */
			pStream = ACMR_CB->TspecListReq.pHead;

			while(pStream != NULL)
			{
				NumStream ++;
				pStream = pStream->pNext;
			} /* End of while */
			break;

		case ACM_SM_CATEGORY_ACT: /* output links of all peers */
			pAcmDevList = NULL;

			while(1)
			{
				/* get next device */
				if (ACM_PeerDeviceGetNext(pAd, &pAcmDevList, MAC) != ACM_RTN_OK)
					break;
				/* End of if */

				/* copy output TS information */
				ppAcmStmList = (ACM_STREAM **)ACM_StationTspecListGet(
										pAd, MAC, ACM_PEER_TSPEC_OUTPUT_GET);

				if (ppAcmStmList == NULL)
					continue;
				/* End of if */

				for(IdTidNum=0; IdTidNum<ACM_STA_TID_MAX_NUM; IdTidNum++)
				{
					if (ppAcmStmList[IdTidNum] != NULL)
						NumStream++;
					/* End of if */
				} /* End of for */
			}  /* End of while */
			break;

		case ACM_SM_CATEGORY_PEER: /* input and output links of a peer */
			for(IdLinkNum=0; IdLinkNum<2; IdLinkNum++)
			{
				ppAcmStmList = (ACM_STREAM **)ACM_StationTspecListGet(
										pAd, pStaMac, DirectionId[IdLinkNum]);

				if (ppAcmStmList == NULL)
					break;
				/* End of if */

				for(IdTidNum=0; IdTidNum<ACM_STA_TID_MAX_NUM; IdTidNum++)
				{
					if (ppAcmStmList[IdTidNum] != NULL)
					{
						if ((IdLinkNum == 1) &&
							(ppAcmStmList[IdTidNum]->pTspec->TsInfo.Direction == \
													ACM_DIRECTION_BIDIREC_LINK))
						{
							/* for bidirectional link, only copy once */
							continue;
						} /* End of if */

						NumStream++;
					} /* End of if */
				} /* End of for */
			} /* End of for */
			break;

		case ACM_SM_CATEGORY_ERR: /* failed list */
			pStream = ACMR_CB->TspecListFail.pHead;

			while(pStream != NULL)
			{
				NumStream ++;
				pStream = pStream->pNext;
			} /* End of while */
			break;
	} /* End of switch */

	/* release semaphore */
	ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);

	return NumStream;

LabelSemErr:
	ACMR_SEM_FAIL_PRINT();
	return 0;
} /* End of ACMP_StreamNumGet */


/*
========================================================================
Routine Description:
	Delete all activated TSPECs.

Arguments:
	pAd				- WLAN control block pointer

Return Value:
	None

Note:
	1. Send a DELTS to the QSTA or QAP.
	2. Insert the activated TSPEC to the requested list.
	3. The TSPEC will be moved to the failed list when DELTS ACK
		is received or retry count is reached.
	4. Can not used in disassociation.
========================================================================
*/
VOID ACMP_TC_DeleteAll(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd)
{
	ACM_STREAM **ppAcmStmList, *pStream;
	ACMR_LIST StreamList;
	ACM_PEER_DEV_LIST *pAcmDevList;
	UINT32 IdTidNum, IdLinkNum;
	UCHAR DirectionId[2] = \
					{ ACM_PEER_TSPEC_OUTPUT_GET, ACM_PEER_TSPEC_INPUT_GET };
	UCHAR MAC[ACM_MAC_ADDR_LEN];
	ULONG SplFlags;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* init */
	pAcmDevList = NULL;
	ACMR_LIST_INIT(&StreamList);

	/* get management semaphore */
	ACM_TSPEC_IRQ_LOCK_CHK(pAd, SplFlags, LabelSemErr);

	/* clear all requested TSPEC if exists */
	ACM_TC_ReqAllFree(pAd);

	/* delete all output and input links for all device entries */
	while(1)
	{
		/* get next device entry */
		if (ACM_PeerDeviceGetNext(pAd, &pAcmDevList, MAC) != ACM_RTN_OK)
			break; /* no other device */
		/* End of if */

		/* delete all input and output streams */
		for(IdLinkNum=0; IdLinkNum<2; IdLinkNum++)
		{
			ppAcmStmList = (ACM_STREAM **)ACM_StationTspecListGet(
											pAd, MAC, DirectionId[IdLinkNum]);
			if (ppAcmStmList == NULL)
				break;
			/* End of if */

			for(IdTidNum=0; IdTidNum<ACM_STA_TID_MAX_NUM; IdTidNum++)
			{
				pStream = ppAcmStmList[IdTidNum];

				if (pStream != NULL)
				{
#ifdef CONFIG_AP_SUPPORT
					if (ACMR_IS_AP_MODE(pAd))
						pStream->Cause = ACM_TC_CAUSE_DELETED_BY_QAP;
					/* End of if */
#endif /* CONFIG_AP_SUPPORT */

#ifdef CONFIG_STA_SUPPORT
					if (ACMR_IS_STA_MODE(pAd))
						pStream->Cause = ACM_TC_CAUSE_DELETED_BY_QSTA;
					/* End of if */
#endif /* CONFIG_STA_SUPPORT */

					if (ACM_TC_Delete(pAd, pStream) == TRUE)
					{
						ACMR_LIST_ALLOC_AND_INSERT_TO_TAIL(pAd, &StreamList, pStream);
					} /* End of if */

					/* empty the record */
					ppAcmStmList[IdTidNum] = NULL;
				} /* End of if */
			} /* End of for */
		} /* End of for */
	} /* End of while */

	/* free all backup device entries */
	ACM_PeerDeviceAllFree(pAd);

	/* release semaphore */
	ACM_TSPEC_IRQ_UNLOCK(pAd, SplFlags, LabelSemErr);

	/* send a DELTS frame for each TSPEC */
	while(1)
	{
		pStream = (ACM_STREAM *)ACMR_LIST_REMOVE_FRM_HEAD(&StreamList);
		if (pStream == NULL)
			break;
		/* End of if */

		ACM_DELTS_SEND(pAd, pStream->pCdb, pStream, LabelSemErr);

		/* isolate the stream */
		pStream->pPrev = NULL;
		pStream->pNext = NULL;

		/* free it */
		ACM_TC_Free(pAd, pStream);
	}
	return;

LabelSemErr:
	ACMR_SEM_FAIL_PRINT();
	return;
} /* End of ACMP_TC_DeleteAll */


/*
========================================================================
Routine Description:
	Delete a TSPEC silently.

Arguments:
	pAd				- WLAN control block pointer
	*pMacPeer		- the MAC of peer
	TID				- the TID of the TSPEC

Return Value:
	TRUE			- find it and delete it ok
	FALSE			- do not find it or delete it fail

Note:
	For QAP, the pMacPeer means the MAC of a station;
	For QSTA, the pMacPeer means the MAC of associated AP;

	No DELTS frame is sent.
========================================================================
*/
BOOLEAN ACMP_TC_DeleteOneSilently(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	UCHAR					*pMacPeer,
	ACM_PARAM_IN	UCHAR					TID)
{
	ACM_STREAM *pStream;
	ACM_TS_INFO TsInfo;
	ULONG SplFlags;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* init */
	TsInfo.TSID = TID;

	/* get management semaphore */
	ACM_TSPEC_IRQ_LOCK_CHK_RTN(pAd, SplFlags, LabelSemErr, FALSE);

	/* find the request */
	pStream = ACM_TC_Find(pAd, pMacPeer, &TsInfo);
	if (pStream == NULL)
	{
		ACMR_DEBUG(ACMR_DEBUG_ERR,
					("acm_msg> can not find the stream (TID=%d)!\n", TID));
		goto LabelNotFound;
	} /* End of if */

	/* delete the stream */
#ifdef CONFIG_AP_SUPPORT
	if (ACMR_IS_AP_MODE(pAd))
		pStream->Cause = TSPEC_CAUSE_DELETED_BY_QAP;
	/* End of if */
#endif /* CONFIG_AP_SUPPORT */

#ifdef CONFIG_STA_SUPPORT
	if (ACMR_IS_STA_MODE(pAd))
		pStream->Cause = TSPEC_CAUSE_DELETED_BY_QSTA;
	/* End of if */
#endif /* CONFIG_STA_SUPPORT */

	ACM_TC_Destroy(pAd, pStream, 0);

	/* release semaphore */
	ACM_TSPEC_IRQ_UNLOCK(pAd, SplFlags, LabelSemErr);
	return TRUE;

LabelNotFound:
	ACM_TSPEC_IRQ_UNLOCK(pAd, SplFlags, LabelSemErr);
	return FALSE;

LabelSemErr:
	ACMR_SEM_FAIL_PRINT();
	return FALSE;
} /* End of ACMP_TC_DeleteOneSilently */


/*
========================================================================
Routine Description:
	Move it to the fail list.

Arguments:
	pAd					- WLAN control block pointer
	*pDevMac			- the QSTA sends the DELTS frame
	*pTsInfo			- the TS Info
	FlgIsFromSta		- 1: destroy from QSTA; 0: destroy from QAP

Return Value:
	ACM_RTN_OK			- destroy ok
	ACM_RTN_FAIL		- destroy fail
	ACM_RTN_SEM_GET_ERR	- get semaphore fail

Note:
========================================================================
*/
ACM_FUNC_STATUS ACMP_TC_DestroyBy_TS_Info(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd,
	ACM_PARAM_IN	UCHAR				*pDevMac,
	ACM_PARAM_IN	ACM_TS_INFO			*pTsInfo,
	ACM_PARAM_IN	UCHAR				FlgIsFromSta)
{
	ACM_STREAM *pStream;
	ACMR_STA_DB *pCdb;
	UCHAR StmAcId;
	UCHAR Direction;
	ULONG SplFlags;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* init */
	pCdb = NULL;
	StmAcId = 0;
	Direction = ACM_DIRECTION_UP_LINK;

	/* semaphore protection */
	ACM_TSPEC_SEM_LOCK_CHK_RTN(pAd, SplFlags, LabelSemErr, ACM_RTN_FAIL);

	/* find the stream */
	pStream = ACM_TC_Find(pAd, pDevMac, pTsInfo);

	if (pStream == NULL)
	{
		ACMR_DEBUG(ACMR_DEBUG_ERR,
					("acm_err> DEL a stream but can not find it! "
					"ACM_TC_DestroyBy_TS_Info()\n"));
		goto LabelDestroyOk; /* the stream does NOT exist */
	} /* End of if */

	/*
		Check if the stream is created by the QSTA, only the original QSTA
		can delete the stream, other QSTA can NOT delete.
	*/
	if (!(AMR_IS_SAME_MAC(ACMR_CLIENT_MAC(pStream->pCdb), pDevMac)))
		goto LabelErr;
	/* End of if */

	if (FlgIsFromSta == 1)
		pStream->Cause = TSPEC_CAUSE_DELETED_BY_QSTA;
	else
		pStream->Cause = TSPEC_CAUSE_DELETED_BY_QAP;
	/* End of if */

	pCdb = pStream->pCdb;
	StmAcId = pStream->AcmAcId;
	Direction = pStream->pTspec->TsInfo.Direction;

	ACM_TC_Destroy(pAd, pStream, 0);

LabelDestroyOk:
	ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);

	/* recover UAPSD state */
#if 0
	if (pStream != NULL)
		ACM_APSD_Ctrl(pAd, pCdb, StmAcId, Direction, 0, 0);
	/* End of if */
#endif /* 0 */

	ACMR_DEBUG(ACMR_DEBUG_TRACE,
				("acm_msg> DEL a stream! TC_DestroyBy_TS_Info()\n"));
	return ACM_RTN_OK;

LabelErr:
	ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);
	return ACM_RTN_FAIL;

LabelSemErr:
	ACMR_SEM_FAIL_PRINT();
	return ACM_RTN_SEM_GET_ERR;
} /* End of ACMP_TC_DestroyBy_TS_Info */


#ifdef CONFIG_AP_SUPPORT
/*
========================================================================
Routine Description:
	Enable or disable all TSPEC rejection function.

Arguments:
	pAd				- WLAN control block pointer
	FlgIsEnable		- 1: enable; 0: disable

Return Value:
	None

Note:
	Only for QAP.
========================================================================
*/
VOID ACMP_TC_RejectCtrl(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	UCHAR					FlgIsEnable)
{
	ULONG SplFlags;


	/* get management semaphore */
	ACM_TSPEC_IRQ_LOCK_CHK(pAd, SplFlags, LabelSemErr);

	if (FlgIsEnable)
	{
		ACM_MR_TSPEC_ALLOW();
	}
	else
	{
		/*
			We will not accept any new TSPEC request
			but we will keep old TSPECs.
		*/
		ACM_MR_TSPEC_DISALLOW();
	} /* End of if */

	ACM_TSPEC_IRQ_UNLOCK(pAd, SplFlags, LabelSemErr);
	return;

LabelSemErr:
	ACMR_SEM_FAIL_PRINT();
	return;
} /* End of ACMP_TC_RejectCtrl */
#endif /* CONFIG_AP_SUPPORT */


/*
========================================================================
Routine Description:
	Enable or disable TSPEC timeout function.

Arguments:
	pAd				- WLAN control block pointer
	FlgIsEnable		- 1: enable; 0: disable

Return Value:
	None

Note:
	Only for QAP.
========================================================================
*/
VOID ACMP_TC_TimeoutCtrl(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	UCHAR					FlgIsEnable)
{
	ACM_CTRL_PARAM  *pEdcaParam;
	ULONG  SplFlags;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* get management semaphore */
	ACM_TSPEC_SEM_LOCK_CHK(pAd, SplFlags, LabelSemErr);

	pEdcaParam = &(ACMR_CB->EdcaCtrlParam);
	pEdcaParam->FlgIsTspecTimeoutEnable = FlgIsEnable;

	/* active stream check timer for any stream */
	ACMR_TIMER_ENABLE(ACMR_CB->FlgStreamAliveCheckEnable,
						ACMR_CB->TimerStreamAliveCheck,
						ACM_STREAM_CHECK_OFFSET);

	ACMR_DEBUG(ACMR_DEBUG_TRACE,
				("acm_msg> TSPEC timeout mechanism flag = %d!\n", FlgIsEnable));

	/* release semaphore */
	ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);
	return;

LabelSemErr:
	ACMR_SEM_FAIL_PRINT();
	return;
} /* End of ACMP_TC_TimeoutCtrl */




/* =========================== Global Function (STA) ======================== */

#ifdef CONFIG_STA_SUPPORT
/*
========================================================================
Routine Description:
	Send a renegotiated TSPEC request to the QAP.

Arguments:
	pAd						- WLAN control block pointer
	*pCdb					- our STATION entry
	*pTspecSrc				- the requested TSPEC pointer
	TclasNum				- the number of TCLASS, max 5
	*pTclasSrc				- the requested TCLASS array pointer
	TclasProcessing			- 1: means matching all TCLAS
	StreamType				- the stream type: WME stream

Return Value:
	ACM_RTN_OK				- request is accepted
	ACM_RTN_FAIL			- semaphore lock fail or others
	ACM_RTN_NULL_POINTER	- null pointer
	ACM_RTN_NOT_EXIST		- the old TSPEC does not exist
	ACM_RTN_INVALID_PARAM	- invalid input parameters
	ACM_RTN_SEM_GET_ERR     - get semaphore fail
	ACM_RTN_FATAL_ERR		- can not call the func in error mode
	ACM_RTN_NO_FREE_TS		- no free TS ID or same TSID & Direction
	ACM_RTN_ALLOC_ERR		- TSPEC request structure allocation fail

Note:
	1. Only for non-IBSS QSTA Mode.
	2. DLP is not allowed.
========================================================================
*/
ACM_FUNC_STATUS ACMP_TC_Renegotiate(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	ACMR_STA_DB				*pCdb,
	ACM_PARAM_IN	ACM_TSPEC				*pTspecSrc,
	ACM_PARAM_IN	UINT32					TclasNum,
	ACM_PARAM_IN	ACM_TCLAS				*pTclasSrc,
	ACM_PARAM_IN	UCHAR					TclasProcessing,
	ACM_PARAM_IN	UCHAR					StreamType)
{
	ACM_CTRL_PARAM *pEdcaParam;
	ACM_TCLAS *pTclas;
	ACM_STREAM *pOldStreamIn, *pOldStreamOut, *pOldStreamDiffAc;
	ACM_STREAM *pStreamReq;
	ACM_FUNC_STATUS RtnCode, Status;
	UCHAR UserPriority;
	ULONG SplFlags;
	UCHAR *pFrameBuf;
	UINT32 FrameLen;
#ifdef ACM_CC_FUNC_TCLAS
	UINT32 IdTclasNum;
#endif /* ACM_CC_FUNC_TCLAS */


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* init */
	pTclas = pTclasSrc;
	RtnCode = ACM_RTN_SEM_GET_ERR;
	UserPriority = ACM_UP_UNKNOWN;

#ifndef ACM_CC_FUNC_SPEC_CHANGE_TG
	/* we can send out a TSPEC to change PS mode only */

	/* sanity check if the ACM of all AC is disabled */
	if (ACMP_IsAnyACEnabled(pAd) != ACM_RTN_OK)
		return ACM_RTN_DISALLOW;
	/* End of if */
#endif /* ACM_CC_FUNC_SPEC_CHANGE_TG */

	/* sanity check for input parameters */
	/*
		In WMM spec., TCLAS can be NULL; if NULL, for WME, use UP or DSCP to
		classify MSDUs.
	*/
	if (pTspecSrc == NULL)
		return ACM_RTN_NULL_POINTER;
	/* End of if */

	/* get management semaphore */
	ACM_TSPEC_SEM_LOCK_CHK_RTN(pAd, SplFlags, LabelSemErr, ACM_RTN_FAIL);

	pEdcaParam = &(ACMR_CB->EdcaCtrlParam);

	/* check whether same TSPEC exists in the active links (EDCA or HCCA) */
	UserPriority = ACM_TC_UP_Get(&pTspecSrc->TsInfo, TclasNum, pTclasSrc);

	Status = ACM_TC_RenegotiationCheck(pAd,
									ACMR_CLIENT_MAC(pCdb),
									UserPriority,
									&pTspecSrc->TsInfo,
									&pOldStreamIn,
									&pOldStreamOut,
									&pOldStreamDiffAc);

	if (Status != ACM_RTN_OK)
	{
		/* can not find it in active list so can not re-negotiate it */
		ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> Old TSPEC does not exist!\n"));

		RtnCode = ACM_RTN_NOT_EXIST;
		goto LabelErr;
	} /* End of if */


#ifdef RELEASE_EXCLUDE
	/*
		1. TCLASS number can be 0 when the link is uplink or direct link
			because QAP dont need TCLASS for these links;
		2. TCLASS number can be 0 for EDCA mode and 802.1D/Q frames can be
			classified by TSID;
		3. TCLASS number can not be 0 for HCCA or HCCA+EDCA mode because
			QAP can have max 16 TS queues and frames can not be classified by
			TSID.
	*/
#endif /* RELEASE_EXCLUDE */
	if (pTspecSrc->TsInfo.AccessPolicy != ACM_ACCESS_POLICY_EDCA)
	{
		RtnCode = ACM_RTN_INVALID_PARAM;
		goto LabelErr;
	} /* End of if */

#ifdef ACM_CC_FUNC_TCLAS
	/* maximum TCLASS number is limited */
	if (TclasNum > ACM_TCLAS_MAX_NUM)
	{
		RtnCode = ACM_RTN_INVALID_PARAM;
		goto LabelErr;
	} /* End of if */

	/* check user priority and get the user priority */
	if ((TclasNum > 0) && (pTclas != NULL))
	{
		/* all TCLAS shall have the same user priority */
		for(IdTclasNum=0; IdTclasNum<TclasNum; IdTclasNum++)
		{
			if (pTclas != NULL)
			{
				if (UserPriority == ACM_UP_UNKNOWN)
					UserPriority = pTclas->UserPriority;
				else
				{
					if (pTclas->UserPriority != UserPriority)
					{
						RtnCode = ACM_RTN_INVALID_PARAM;
						goto LabelErr;
					} /* End of if */
				} /* End of if */
			} /* End of if */

			pTclas ++;
		} /* End of for */
	} /* End of if */
#endif /* ACM_CC_FUNC_TCLAS */

#ifndef ACM_CC_FUNC_SPEC_CHANGE_TG
	/* we can send out a TSPEC to change PS mode only */

	/* check if ACM is needed for the AC */
	if (ACMR_CB->EdcaCtrlParam.FlgAcmStatus[ACM_MR_EDCA_AC(UserPriority)] == 0)
	{
		RtnCode = ACM_RTN_DISALLOW;
		goto LabelErr;
	} /* End of if */
#endif /* ACM_CC_FUNC_SPEC_CHANGE_TG */

	/* allocate a TSPEC request structure */
	ACMR_MEM_ALLOC(pStreamReq, sizeof(ACM_STREAM), (ACM_STREAM *));
	if (pStreamReq == NULL)
	{
		RtnCode = ACM_RTN_ALLOC_ERR;
		goto LabelErr;
	} /* End of if */

	/* init the TSPEC request structure: TSPEC, TCLAS, etc. */
	ACMR_MEM_ZERO(pStreamReq, sizeof(ACM_STREAM));

	ACMR_MEM_ALLOC(pStreamReq->pTspec, sizeof(ACM_TSPEC), (ACM_TSPEC *));
	if (pStreamReq->pTspec == NULL)
	{
		ACMR_MEM_FREE(pStreamReq);
		RtnCode = ACM_RTN_ALLOC_ERR;
		goto LabelErr;
	} /* End of if */

	ACM_TSPEC_COPY(pStreamReq->pTspec, pTspecSrc);

	pTclas = pTclasSrc;

#ifdef ACM_CC_FUNC_TCLAS
	if (pTclas != NULL)
	{
		for(IdTclasNum=0; IdTclasNum<TclasNum; IdTclasNum++)
		{
			if (pTclas == NULL)
				break;
			/* End of if */

			if (pStreamReq->pTclas[IdTclasNum] != NULL)
				ACMR_MEM_FREE(pStreamReq->pTclas[IdTclasNum]);
			/* End of if */

			pStreamReq->pTclas[IdTclasNum] = \
							(ACM_TCLAS *)ACMR_MEM_ALLOC(sizeof(ACM_TCLAS));

			if (pStreamReq->pTclas[IdTclasNum] == NULL)
			{
				TclasNum = IdTclasNum+1;
				goto LabelErrAlloc;
			} /* End of if */

			ACM_TCLAS_COPY(pStreamReq->pTclas[IdTclasNum], pTclas);
			pTclas ++;
		} /* End of for */
	} /* End of if */
#endif /* ACM_CC_FUNC_TCLAS */

	pStreamReq->StreamType = StreamType;
	pStreamReq->Status = TSPEC_STATUS_RENEGOTIATING;

	/* we can not send DELTS if no response; we need to keep old TSPEC */
	pStreamReq->TimeoutAction = ACM_TC_TIMEOUT_ACTION_DELTS;

	pStreamReq->TclasProcessing = TclasProcessing;
	pStreamReq->Retry = 0; /* no retry if request timeout */
	ACM_STREAM_CDB_COPY(pStreamReq, pCdb);
	pStreamReq->UP = UserPriority;

	/* timeout unit: 100ms */
	pStreamReq->Timeout = ACM_ADDTS_REQUEST_TIMEOUT;
	pStreamReq->Timeout = pStreamReq->Timeout * 1000 / ACM_TIMEOUT_CHECK_BASE;

	TSPEC_DIALOG_TOKEN_GET(pAd, pStreamReq->DialogToken);

	/* PHY_TSID will be determined after ADDTS response is received */
	pStreamReq->AcmAcId  = ACM_MR_EDCA_AC(pStreamReq->UP);

	/* check if need to recover UAPSD field of TS Info */
	if (!pEdcaParam->FlgIsTspecUpasdEnable)
	{
		/*
			Does not support the modification of the Power Save settings via
			TSPEC.
		*/
		pStreamReq->pTspec->TsInfo.APSD = \
						pAd->CommonCfg.bACMAPSDBackup[pStreamReq->AcmAcId];

		ACMR_DEBUG(ACMR_DEBUG_TRACE,
					("acm_msg> Recover APSD to %d! WME_TC_Request()\n",
					pStreamReq->pTspec->TsInfo.APSD));
	} /* End of if */

	/* timeout unit: 100ms */
	pStreamReq->TimeoutAddts = pStreamReq->Timeout;
	pStreamReq->TimeoutDelts = ACM_DELTS_TIMEOUT / ACM_TIMEOUT_CHECK_BASE;
	pStreamReq->ReNegotiation = 1; /* this is a renegotiation TSPEC */

	/* check inactivity timeout */
	if (pStreamReq->pTspec->InactivityInt == 0)
		pStreamReq->pTspec->InactivityInt = ACM_WME_TSPEC_INACTIVITY_DEFAULT;
	/* End of if */

	/* insert the new requested TSPEC to the request list */
	ACM_TC_ReqInsert(pAd, pStreamReq);

	/* add the check timer */
	ACMR_TIMER_ENABLE(ACMR_CB->FlgTspecReqCheckEnable,
						ACMR_CB->TimerTspecReqCheck,
						ACM_STREAM_CHECK_OFFSET);

	/* make up ADDTS Request frame */
	ACM_ADDREQ_MAKEUP(pAd, pCdb, pFrameBuf, FrameLen, pStreamReq, LabelSemErr);

	/* release semaphore */
	ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);

	/* set PS mode to ACTIVE mode to wait for ADDTS response frame */
	ACM_PS_ActiveOn(pAd);

	/* send ADDTS Request frame */
	ACM_ADDREQ_SEND(pAd, pFrameBuf, FrameLen);

	return ACM_RTN_OK;

LabelErr:
	/* release semaphore */
	ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);
	ACMR_DEBUG(ACMR_DEBUG_ERR,
				("acm_err> Negotiate Fail! TC_Renegotiate()\n"));
	return RtnCode;

LabelSemErr:
	ACMR_SEM_FAIL_PRINT();
	return RtnCode;

#ifdef ACM_CC_FUNC_TCLAS
LabelErrAlloc:
	/* release semaphore */
	ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);

	/* free TCLAS memory */
	for(IdTclasNum=0; IdTclasNum<TclasNum; IdTclasNum++)
	{
		if (pStreamReq->pTclas[IdTclasNum] != NULL)
			ACMR_MEM_FREE(pStreamReq->pTclas[IdTclasNum]);
		/* End of if */
	} /* End of for */

	return ACM_RTN_ALLOC_ERR;
#endif /* ACM_CC_FUNC_TCLAS */
} /* End of ACMP_TC_Renegotiate */


/*
========================================================================
Routine Description:
	Adjust retry count limit automatically.

Arguments:
	pAd				- WLAN control block pointer

Return Value:
	None

Note:
	Only for QSTA.

	Retry count enable:
	(1) Power Save mode;
	(2) No TSPEC;
========================================================================
*/
VOID ACMP_RetryCountCtrl(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd)
{
#ifdef CONFIG_STA_SUPPORT
	ULONG SplFlags;


	ACM_TSPEC_SEM_LOCK_CHK(pAd, SplFlags, LabelSemErr);
	ACM_ASIC_RetryCountCtrl(pAd);
	ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);
	return;

LabelSemErr:
	ACMR_SEM_FAIL_PRINT();
	return;
#endif /* CONFIG_STA_SUPPORT */
} /* End of ACMP_RetryCountCtrl */


/*
========================================================================
Routine Description:
	Enable or disable TSPEC UAPSD function.

Arguments:
	pAd				- WLAN control block pointer
	FlgIsEnable		- 1: enable; 0: disable

Return Value:
	None

Note:
	Only for QSTA.

	If TSPEC UAPSD function is disabled, the UAPSD field of TSPEC will
	be same as the value in station association request frame.
========================================================================
*/
VOID ACMP_TC_UapsdCtrl(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	UCHAR					FlgIsEnable)
{
	ULONG  SplFlags;


	/* get management semaphore */
	ACM_TSPEC_SEM_LOCK_CHK(pAd, SplFlags, LabelSemErr);
	ACM_PS_UapsdCtrl(pAd, FlgIsEnable);
	/* release semaphore */
	ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);
	return;

LabelSemErr:
	ACMR_SEM_FAIL_PRINT();
	return;
} /* End of ACMP_TC_UapsdCtrl */
#endif /* CONFIG_STA_SUPPORT */


/*
========================================================================
Routine Description:
	Timer APIs are provided for WLAN module use.

Arguments:
	ACM_TIMER_API_PARAM

Return Value:
	None

Note:
========================================================================
*/
VOID ACMP_TR_TC_ReqCheck(ACM_TIMER_API_PARAM)
{
	ACM_TR_TC_ReqCheck(ACM_TIMER_API_DATA);
}


VOID ACMP_TR_STM_Check(ACM_TIMER_API_PARAM)
{
	ACM_TR_STM_Check(ACM_TIMER_API_DATA);
}


VOID ACMP_TR_TC_General(ACM_TIMER_API_PARAM)
{
	ACM_TR_TC_General(ACM_TIMER_API_DATA);
}


VOID ACMP_CMD_Timer_Data_Simulation(ACM_TIMER_API_PARAM)
{
	ACM_CMD_Timer_Data_Simulation(ACM_TIMER_API_DATA);
}


#ifdef CONFIG_STA_SUPPORT_SIM
/*
========================================================================
Routine Description:
	Send a TSPEC request to the QAP.

Arguments:
	pAd						- WLAN control block pointer
	*pCdb					- our STATION entry
	*pTspecSrc				- the requested TSPEC pointer
	TclasNum				- the number of TCLASS, max 5
	*pTclasSrc				- the requested TCLASS array pointer
	TclasProcessing			- 1: must match all TCLAS
	StreamType				- the stream type: WME stream

Return Value:
	ACM_RTN_OK				- request is accepted
	ACM_RTN_FAIL			- semaphore lock fail or others
	ACM_RTN_NULL_POINTER	- null pointer
	ACM_RTN_INVALID_PARAM	- invalid input parameters
	ACM_RTN_SEM_GET_ERR		- get semaphore fail
	ACM_RTN_FATAL_ERR		- can not call the func in error mode
	ACM_RTN_NO_FREE_TS		- no free TS ID or same TSID & Direction
	ACM_RTN_ALLOC_ERR		- TSPEC request structure allocation fail
	ACM_RTN_DISALLOW		- the request is not allowed

Note:
	1. Only for non-IBSS Station Mode.
	2. pTclasSrc is limited by ACM_TSPEC_TCLAS_MAX_NUM.
	3. DLP TSPEC is not allowed but DLP is allowed.
	4. *pTspecSrc & *pTclasSrc[] can not be freed in calling function.
	5. For WMM STA, the used TSPEC is the same.

	ACM_TG_CMT_SPEC_UNCLEAR_ON_RESERVED_FIELD
========================================================================
*/
ACM_FUNC_STATUS ACMP_WME_TC_Request(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd,
	ACM_PARAM_IN	ACMR_STA_DB			*pCdb,
	ACM_PARAM_IN	ACM_TSPEC			*pTspecSrc,
	ACM_PARAM_IN	UINT32				TclasNum,
	ACM_PARAM_IN	ACM_TCLAS			*pTclasSrc,
	ACM_PARAM_IN	UCHAR				TclasProcessing,
	ACM_PARAM_IN	UCHAR				StreamType,
	ACM_PARAM_IN	UINT16				DialogToken)
{
	ACM_CTRL_PARAM *pEdcaParam;
	ACM_TCLAS *pTclas;
	ACM_STREAM *pOldStreamIn, *pOldStreamOut, *pOldStreamDiffAc;
	ACM_STREAM *pStreamReq;
	ACM_FUNC_STATUS RtnCode, Status;
	UCHAR  UserPriority;
	ULONG  SplFlags;
	UCHAR *pFrameBuf;
	UINT32 FrameLen;
#ifdef ACM_CC_FUNC_TCLAS
	UINT32 IdTclasNum;
#endif /* ACM_CC_FUNC_TCLAS */


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* init */
	pTclas = pTclasSrc;
	RtnCode = ACM_RTN_OK;
	UserPriority = ACM_UP_UNKNOWN;

	ACM_TSPEC_SEM_LOCK_CHK_RTN(pAd, SplFlags, LabelSemErr, ACM_RTN_FAIL);

	if (!ACM_MR_TSPEC_IS_ALLOWED(pAd))
	{
		ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);
		ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> ACM is not allowed!\n"));
		return ACM_RTN_DISALLOW;
	} /* End of if */

	ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);

	/* check if QSTA is in ASSOCIATION state */
	if (!ACMR_IS_PORT_SECURE(pAd))
	{
		/* QSTA yet associate to the QAP */
		/* QSTA can send ADDTS request only when it associates to a AP */
		ACMR_DEBUG(ACMR_DEBUG_TRACE,
					("acm_err> Station does not yet associate to any AP!\n"));
		return ACM_RTN_FAIL;
	} /* End of if */

#ifdef RELEASE_EXCLUDE
	/*
		"2.2.11 WMM TSPEC Element", WMM_Specification_1-1a-wmmac-070601.doc
		A STA may need to transmit a WMM TSPEC request for an AC that does
		not mandate admission control, e.g for the establishment of the
		triggered power save mode of operation.
	*/
#endif /* RELEASE_EXCLUDE */

#ifndef ACM_CC_FUNC_SPEC_CHANGE_TG
	/* we can send out a TSPEC to change PS mode only */

	/* check if the ACM of all AC is disabled */
	if (ACMP_IsAnyACEnabled(pAd) != ACM_RTN_OK)
	{
		ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> ACM is disabled!\n"));
		return ACM_RTN_DISALLOW;
	} /* End of if */
#endif /* ACM_CC_FUNC_SPEC_CHANGE_TG */

	/* sanity check for input parameters of WME STA */
	if (pTspecSrc == NULL)
		return ACM_RTN_NULL_POINTER;
	/* End of if */

	/* sanity check for tx rate */
	if ((pTspecSrc->MinPhyRate > 0) &&
		(pTspecSrc->MeanDataRate > 0) &&
		(pTspecSrc->MinPhyRate <= pTspecSrc->MeanDataRate))
	{
		/* minimum PHY rate must > mean data rate */
		ACMR_DEBUG(ACMR_DEBUG_TRACE,
					("acm_err> Minimum Phy Rate > Mean Data Rate!\n"));
		return ACM_RTN_INVALID_PARAM;
	} /* End of if */

	if (((pTspecSrc->MinDataRate != 0) &&
		(pTspecSrc->MeanDataRate != 0) &&
		(pTspecSrc->MinDataRate > pTspecSrc->MeanDataRate)) ||
		((pTspecSrc->PeakDataRate != 0) &&
		(pTspecSrc->MeanDataRate != 0) &&
		(pTspecSrc->MeanDataRate > pTspecSrc->PeakDataRate)))
	{
		ACMR_DEBUG(ACMR_DEBUG_TRACE,
					("acm_err> Min/Mean Data Rate > Mean/Peak Data Rate!\n"));
		return ACM_RTN_INVALID_PARAM;
	} /* End of if */

#if 0 /* TODO */
	/* check min phy rate it can not be larger than maximum supported rate */
	ACMR_SUP_RATE_MAX_GET(pAd, pCdb, MaxRate);

	if (pTspecSrc->MinPhyRate > MaxRate)
	{
		ACMR_DEBUG(ACMR_DEBUG_TRACE,
					("acm_err> min phy rate %dbps > "
					"maximum supported rate %dbps!\n",
					pTspecSrc->MinPhyRate, MaxRate));
		return ACM_RTN_INVALID_PARAM;
	} /* End of if */
#endif

#ifdef ACM_CC_FUNC_TCLAS
#ifdef RELEASE_EXCLUDE
	/*
		1. TCLASS number can be 0 when the link is uplink or direct link
			because QAP dont need TCLASS for these links;
		2. TCLASS number can be 0 for EDCA mode and 802.1D/Q frames can be
			classified by TSID;
		3. TCLASS number can not be 0 for HCCA or HCCA+EDCA mode because
			QAP can have max 16 TS queues and frames can not be classified by
			TSID.
	*/
#endif /* RELEASE_EXCLUDE */
	/* maximum TCLASS number is limited */
	if (TclasNum > ACM_TSPEC_TCLAS_MAX_NUM)
		return ACM_RTN_INVALID_PARAM;
	/* End of if */

	/* check user priority and get the user priority */
	if ((TclasNum > 0) && (pTclas != NULL))
	{
		/* all TCLASS shall have the same user priority */
		for(IdTclasNum=0; IdTclasNum<TclasNum; IdTclasNum++)
		{
			if (pTclas != NULL)
			{
				if (UserPriority == ACM_UP_UNKNOWN)
					UserPriority = pTclas->UserPriority;
				else
				{
					if (pTclas->UserPriority != UserPriority)
						return ACM_RTN_INVALID_PARAM;
					/* End of if */
				} /* End of if */
			} /* End of if */

			/* check next TCLAS */
			pTclas ++;
		} /* End of for */
	}
	else
#endif /* ACM_CC_FUNC_TCLAS */
	{
		/* no any TCLAS exists so use the UP of the TS Info */
		UserPriority = pTspecSrc->TsInfo.UP;
	} /* End of if */

	/* get management semaphore */
	ACM_TSPEC_SEM_LOCK_CHK_RTN(pAd, SplFlags, LabelSemErr, ACM_RTN_FAIL);

#ifndef ACM_CC_FUNC_SPEC_CHANGE_TG
	/* we can send out a TSPEC to change PS mode only */

	/* check if ACM is needed for the AC */
	if (ACMR_CB->EdcaCtrlParam.FlgAcmStatus[ACM_MR_EDCA_AC(UserPriority)] == 0)
	{
		RtnCode = ACM_RTN_DISALLOW;
		goto LabelErr;
	} /* End of if */
#endif /* ACM_CC_FUNC_SPEC_CHANGE_TG */

	pEdcaParam = &(ACMR_CB->EdcaCtrlParam);

	/* check the TSID & Direction */
	Status = ACM_TC_RenegotiationCheck(pAd,
									ACMR_CLIENT_MAC(pCdb),
									UserPriority,
									&pTspecSrc->TsInfo,
									&pOldStreamIn,
									&pOldStreamOut,
									&pOldStreamDiffAc);

	if (Status == ACM_RTN_OK)
	{
		ACMR_DEBUG(ACMR_DEBUG_TRACE,
					("acm_msg> Already exist same TS! WME_TC_Request()\n"));
		RtnCode = ACM_RTN_EXIST;
		goto LabelErr;
	} /* End of if */

	/* allocate a TSPEC request structure */
	ACMR_MEM_ALLOC(pStreamReq, sizeof(ACM_STREAM), (ACM_STREAM *));
	if (pStreamReq == NULL)
	{
		RtnCode = ACM_RTN_ALLOC_ERR;
		goto LabelErr;
	} /* End of if */

	/* init the TSPEC request structure */
	ACMR_MEM_ZERO(pStreamReq, sizeof(ACM_STREAM));

	ACMR_MEM_ALLOC(pStreamReq->pTspec, sizeof(ACM_TSPEC), (ACM_TSPEC *));
	if (pStreamReq->pTspec == NULL)
	{
		ACMR_MEM_FREE(pStreamReq);
		RtnCode = ACM_RTN_ALLOC_ERR;
		goto LabelErr;
	} /* End of if */

	*pStreamReq->pTspec = *pTspecSrc;
	pTclas = pTclasSrc;

#ifdef ACM_CC_FUNC_TCLAS
	for(IdTclasNum=0; IdTclasNum<TclasNum; IdTclasNum++)
	{
		ACMR_MEM_ALLOC(pStreamReq->pTclas[IdTclasNum],
						sizeof(ACM_TCLAS), (ACM_TCLAS *));

		if (pStreamReq->pTclas[IdTclasNum] == NULL)
		{
			RtnCode = ACM_RTN_ALLOC_ERR;
			TclasNum = IdTclasNum+1;
			goto LabelErrAlloc;
		} /* End of if */

		*pStreamReq->pTclas[IdTclasNum] = *pTclas;
		pTclas ++; /* move to next TCLAS */
	} /* End of for */
#endif /* ACM_CC_FUNC_TCLAS */

	pStreamReq->StreamType = StreamType;
	pStreamReq->Status = TSPEC_STATUS_REQUEST;
	pStreamReq->TimeoutAction = ACM_TC_TIMEOUT_ACTION_ADDTS_REQ;
	pStreamReq->TclasProcessing = TclasProcessing;
	pStreamReq->Retry = 0; /* no retry */
	ACM_STREAM_CDB_COPY(pStreamReq, pCdb);
	pStreamReq->UP = UserPriority;

	/* timeout unit: 100ms */
	pStreamReq->Timeout = ACM_ADDTS_REQUEST_TIMEOUT;
	pStreamReq->Timeout = pStreamReq->Timeout * 1000 / ACM_TIMEOUT_CHECK_BASE;

	if (DialogToken > 0)
		pStreamReq->DialogToken = DialogToken; /* debug use */
	else
	{
		TSPEC_DIALOG_TOKEN_GET(pAd, pStreamReq->DialogToken);
	} /* End of if */

	pStreamReq->AcmAcId  = ACM_MR_EDCA_AC(pStreamReq->UP);

	/* check if need to recover UAPSD field of TS Info */
	if (!pEdcaParam->FlgIsTspecUpasdEnable)
	{
		/*
			Does not support the modification of the Power Save settings via
			TSPEC.
		*/
		pStreamReq->pTspec->TsInfo.APSD = \
						pAd->CommonCfg.bACMAPSDBackup[pStreamReq->AcmAcId];

		ACMR_DEBUG(ACMR_DEBUG_TRACE,
					("acm_msg> Recover APSD to %d! WME_TC_Request()\n",
					pStreamReq->pTspec->TsInfo.APSD));
	} /* End of if */

	/* timeout unit: 100ms */
	pStreamReq->TimeoutAddts = pStreamReq->Timeout;
	pStreamReq->TimeoutDelts = ACM_DELTS_TIMEOUT/ACM_TIMEOUT_CHECK_BASE;

	/* insert the new requested TSPEC to the request list */
	ACM_TC_ReqInsert(pAd, pStreamReq);

	/* check whether the check timer is enabled */
	ACMR_TIMER_ENABLE(ACMR_CB->FlgTspecReqCheckEnable,
						ACMR_CB->TimerTspecReqCheck,
						ACM_STREAM_CHECK_OFFSET);

	/* make up ADDTS Request frame */
	ACM_ADDREQ_MAKEUP(pAd, pCdb, pFrameBuf, FrameLen, pStreamReq, LabelSemErr);

	/* check inactivity timeout */
	if (pStreamReq->pTspec->InactivityInt == 0)
	{
		/*
			We can request a TSPEC with inactivity timeout = 0;
			but infinite inactivity timeout is not good I think,
			so give a default timeout for the TSPEC.
			After ACM_ADDREQ_MAKEUP()
		*/
		pStreamReq->pTspec->InactivityInt = ACM_WME_TSPEC_INACTIVITY_DEFAULT;
	} /* End of if */

	/* release semaphore */
	ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);

	ACMR_DEBUG(ACMR_DEBUG_TRACE,
				("acm_msg> A request is created successfully! "
				"ACMP_WME_TC_Request()\n"));

#ifdef CONFIG_STA_SUPPORT
	if (ACMR_IS_STA_MODE(pAd))
	{
		/* set PS mode to ACTIVE mode to wait for ADDTS response frame */
		ACM_PS_ActiveOn(pAd);
	} /* End of if */
#endif /* CONFIG_STA_SUPPORT */

	/* send a ADDTS Request frame */
	ACM_ADDREQ_SEND(pAd, pFrameBuf, FrameLen);

	return RtnCode;

LabelErr:
	/* release semaphore */
	ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);
	return RtnCode;

LabelSemErr:
	ACMR_SEM_FAIL_PRINT();
	return ACM_RTN_SEM_GET_ERR;

#ifdef ACM_CC_FUNC_TCLAS
LabelErrAlloc:
	/* release semaphore */
	ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);

	ACMR_MEM_FREE(pStreamReq->pTspec);

	for(IdTclasNum=0; IdTclasNum<TclasNum; IdTclasNum++)
		ACMR_MEM_FREE(pStreamReq->pTclas[IdTclasNum]);
	/* End of for */

	ACMR_MEM_FREE(pStreamReq);
	return RtnCode;
#endif /* ACM_CC_FUNC_TCLAS */
} /* End of ACMP_WME_TC_Request */

#endif /* CONFIG_STA_SUPPORT_SIM */




/* =========================== ASIC Function =========================== */

/*
========================================================================
Routine Description:
	Reset the medium time for the AC.

Arguments:
	pAd				- WLAN control block pointer
	AcId			- the AC ID (0 ~ 3)
	MediumTime		- the new medium time (unit: micro seconds)

Return Value:
	None

Note:
	1. If acm_time == 0, means the ACM for the AC will be disabled.
	2. special mode, when AcId == 0x1234, the settings will be
		applied for all ACs.
========================================================================
*/
STATIC VOID ACM_ASIC_ACM_Reset(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	UINT32					AcId,
	ACM_PARAM_IN	UINT32					MediumTime)
{
	/* currently no ASIC setting to be set */
} /* End of ACM_ASIC_ACM_Reset */


/*
========================================================================
Routine Description:
	Enable ASIC channel busy time calculation.

Arguments:
	pAd				- WLAN control block pointer
	FlgIsEnable		- 1: ENABLE; 0:DISABLE

Return Value:
	None

Note:
	1. If acm_time == 0, means the ACM for the AC will be disabled.
	2. special mode, when AcId == 0x1234, the settings will be
		applied for all ACs.
========================================================================
*/
STATIC VOID ACM_ASIC_ChanBusyEnable(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	UCHAR					FlgIsEnable)
{
	if (FlgIsEnable)
	{
		ACMR_CHAN_BUSY_DETECT_ENABLE(pAd);
	}
	else
	{
		ACMR_CHAN_BUSY_DETECT_DISABLE(pAd);
	} /* End of if */
} /* End of ACM_ASIC_ChanBusyEnable */


/*
========================================================================
Routine Description:
	Get the channel busy time in last TBTT.

Arguments:
	pAd				- WLAN control block pointer

Return Value:
	the channel busy time, unit: miscroseconds

Note:
========================================================================
*/
STATIC UINT32 ACM_ASIC_ChanBusyGet(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd)
{
	UINT32 TimeBusy;


	TimeBusy = 0;
	ACMR_CHAN_BUSY_GET(pAd, &TimeBusy);
	return TimeBusy;
} /* End of ACM_ASIC_ChanBusyGet */


/*
========================================================================
Routine Description:
	Reset the WLAN QOS ASIC EDCA setting.

Arguments:
	pAd					- WLAN control block pointer
	FlgIsDeltsAll		- 1: delete all TSPECs; 0: reserve all TSPECs

Return Value:
	None

Note:
========================================================================
*/
STATIC VOID ACM_ASIC_EDCA_Reset(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	UCHAR					FlgIsDeltsAll)
{
	/* no ASIC settings are needed to be set */

	/* delete all activated TSPEC */
	if (FlgIsDeltsAll == 1)
		ACMP_TC_DeleteAll(pAd);
	/* End of if */
} /* End of ACM_ASIC_EDCA_Reset */


/*
========================================================================
Routine Description:
	Translate TUs into microseconds.

Arguments:
	pAd				- WLAN control block pointer
	TU				- the time unit

Return Value:
	microseconds

Note:
========================================================================
*/
STATIC UINT32 ACM_ASIC_TU_Translate(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	UINT32					TU)
{
	UINT32 Unit;


	/* get a TU */
	Unit = 1024; /* 1024 micro second (us) */

	/* calculate TUs */
	TU *= Unit;
	return TU;
} /* End of ACM_ASIC_TU_Translate */


/*
========================================================================
Routine Description:
	Adjust retry count limit automatically.

Arguments:
	pAd					- WLAN control block pointer

Return Value:
	None

Note:
	Only for QSTA.

	Retry count enable:
	(1) Power Save mode;
	(2) No TSPEC;
========================================================================
*/
VOID ACM_ASIC_RetryCountCtrl(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd)
{
#ifdef CONFIG_STA_SUPPORT
	UINT32 RetryCountOldSettings;
	BOOLEAN FlgIsEnabled;


	/* init */
	FlgIsEnabled = FALSE;
	RetryCountOldSettings = 0xFFFFFFFF;

	ACMR_RETRY_GET(pAd, RetryCountOldSettings);

	if ((ACMR_CB->EdcaCtrlParam.LinkNumUp > 0) ||
		(ACMR_CB->EdcaCtrlParam.LinkNumDn > 0) ||
		(ACMR_CB->EdcaCtrlParam.LinkNumBi > 0) ||
		(ACMR_CB->EdcaCtrlParam.LinkNumDi > 0))
	{
		if (ACMR_IS_IN_ACTIVE_MODE(pAd))
		{
			if (RetryCountOldSettings != 0xFFFFFFFF)
				ACMR_CB->RetryCountOldSettings = RetryCountOldSettings;
			/* End of if */
		}
		else
			FlgIsEnabled = TRUE;
		/* End of if */
	}
	else
		FlgIsEnabled = TRUE;
	/* End of if */

	if (FlgIsEnabled == TRUE)
	{
		ACMR_RETRY_ENABLE(pAd, RetryCountOldSettings);
		ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> Enable retry!\n"));
	}
	else
	{
		ACMR_RETRY_DISABLE(pAd);
		ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> Disable retry %x!\n",
					RetryCountOldSettings));
	} /* End of if */
#endif /* CONFIG_STA_SUPPORT */
} /* End of ACMP_RetryCountCtrl */




/* =========================== OTHER Function =========================== */

/*
========================================================================
Routine Description:
	Handle UAPSD enable or disable.

Arguments:
	pAd					- WLAN control block pointer
	*pCdb				- the client database
	StmAcId				- the AC ID (0 ~ 3)
	FlgTsAdd			- 1: add a TS; 0: delete a TS
	FlgIsApsdEnable		- 1: enable APSD in TSPEC

Return Value:
	None

Note:
	WMM-PS is optional for WMM-AC certification.

	RTMP_IRQ_LOCK will be used in the function so you can not put the function
	in any bottom half lock section.

	When FlgTsAdd == 0, FlgIsApsdEnable is not used.

	An uplink TSPEC plus a downlink TSPEC, or a bi-directional TSPEC with the
	APSD subfield set to 1 and the Schedule subfield set to 0, makes an AC both
	trigger- and delivery-enabled.

	An uplink TSPEC plus a downlink TSPEC, or a bi-directional TSPEC with the
	APSD and the Schedule subfields both set to 0, makes an AC neither
	trigger- nor delivery-enabled.

	ACM_TG_CMT_UAPSD_CHANGED_BY_TSPEC
========================================================================
*/
STATIC VOID ACM_APSD_Ctrl(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	ACMR_STA_DB				*pCdb,
	ACM_PARAM_IN	UCHAR					AcId,
	ACM_PARAM_IN	UCHAR					Direction,
	ACM_PARAM_IN	UCHAR					FlgTsAdd,
	ACM_PARAM_IN	UCHAR					FlgIsApsdEnable)
{
#ifdef CONFIG_AP_SUPPORT
	if (ACMR_IS_AP_MODE(pAd))
	{
#ifdef UAPSD_SUPPORT
#if 0
		QUEUE_HEADER *pPsQueAc;
		PNDIS_PACKET *pPktQued;
		ULONG SplFlags;
#endif /* 0 */
		UCHAR FlgIsTrEnabled, FlgIsDeEnabled;


		WMM_ACM_FUNC_NAME_PRINT("IN");

		/* init new UAPSD state */
		FlgIsTrEnabled = pCdb->bAPSDCapablePerAC[AcId];
		FlgIsDeEnabled = pCdb->bAPSDDeliverEnabledPerAC[AcId];

		ACMR_DEBUG(ACMR_DEBUG_TRACE,
					("acm_msg> ac%d, old ADD=%d, APSD=%d, TR=%d, DE=%d\n",
					AcId, FlgTsAdd, FlgIsApsdEnable, FlgIsTrEnabled,
					FlgIsDeEnabled));

		if (FlgTsAdd)
		{
			/* for addition use */

			/* new TSPEC replaces current one or both */
			if (Direction == ACM_DIRECTION_UP_LINK)
				FlgIsTrEnabled = FlgIsApsdEnable;
			else if (Direction == ACM_DIRECTION_DOWN_LINK)
				FlgIsDeEnabled = FlgIsApsdEnable;
			else
			{
				FlgIsTrEnabled = FlgIsApsdEnable;
				FlgIsDeEnabled = FlgIsApsdEnable;
			} /* End of if */
		}
		else
		{
			/* for deletion use */

			/* deleted TSPEC recovers current one or both */
			if (Direction == ACM_DIRECTION_UP_LINK)
				FlgIsTrEnabled = pCdb->bACMAPSDBackup[AcId];
			else if (Direction == ACM_DIRECTION_DOWN_LINK)
				FlgIsDeEnabled = pCdb->bACMAPSDBackupDeliverEnabled[AcId];
			else
			{
				FlgIsTrEnabled = pCdb->bACMAPSDBackup[AcId];
				FlgIsDeEnabled = pCdb->bACMAPSDBackupDeliverEnabled[AcId];
			} /* End of if */
		} /* End of if */

		ACMR_DEBUG(ACMR_DEBUG_TRACE,
					("acm_msg> ac%d, new ADD=%d, APSD=%d, TR=%d, DE=%d\n",
					AcId, FlgTsAdd, FlgIsApsdEnable, FlgIsTrEnabled,
					FlgIsDeEnabled));

		/* check whether new UAPSD state is same as old UAPSD state */
		if ((FlgIsTrEnabled == pCdb->bAPSDCapablePerAC[AcId]) &&
			(FlgIsDeEnabled == pCdb->bAPSDDeliverEnabledPerAC[AcId]))
		{
			/* new UAPSD state is same as current UAPSD state */
			ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> No change for PS mode!\n"));
			return;
		} /* End of if */

		if (FlgIsTrEnabled)
		{
			ACMR_DEBUG(ACMR_DEBUG_TRACE,
						("acm_msg> AC%d is trigger-enabled AC!\n", AcId));
		} /* End of if */

		if (FlgIsDeEnabled)
		{
			ACMR_DEBUG(ACMR_DEBUG_TRACE,
						("acm_msg> AC%d is delivery-enabled AC!\n", AcId));
		} /* End of if */

		if (!FlgIsTrEnabled && !FlgIsDeEnabled)
		{
			ACMR_DEBUG(ACMR_DEBUG_TRACE,
						("acm_msg> AC%d is legacy-PS AC!\n", AcId));
		} /* End of if */

#if 0 /* let all transient packets wait for timeout, let it simpler */
		/* move UAPSD packets to legacy PS queue */
		if (!FlgIsDeEnabled &&
			pCdb->bAPSDDeliverEnabledPerAC[AcId] &&
			pCdb->UAPSDQueue[AcId].Head)
		{
			RTMP_IRQ_LOCK(&pAd->irq_lock, SplFlags);
			pPsQueAc = &pCdb->UAPSDQueue[AcId];

			while(pPsQueAc->Head)
			{
				pPktQued = (PNDIS_PACKET)RemoveHeadQueue(pPsQueAc);

				InsertTailQueue(&pCdb->PsQueue,
								PACKET_TO_QUEUE_ENTRY(pPktQued));
			} /* End of while */
			RTMP_IRQ_UNLOCK(&pAd->irq_lock, SplFlags);
		} /* End of if */

		/* move legacy PS queue to UAPSD packets */
		if (FlgIsDeEnabled &&
			!pCdb->bAPSDDeliverEnabledPerAC[AcId] &&
			pCdb->PsQueue.Head)
		{
			RTMP_IRQ_LOCK(&pAd->irq_lock, SplFlags);
			pPsQueAc = &pCdb->PsQueue;

			while(pPsQueAc->Head)
			{
				pPktQued = (PNDIS_PACKET)RemoveHeadQueue(pPsQueAc);

				InsertTailQueue(&pCdb->UAPSDQueue[AcId],
								PACKET_TO_QUEUE_ENTRY(pPktQued));
			} /* End of while */
			RTMP_IRQ_UNLOCK(&pAd->irq_lock, SplFlags);
		} /* End of if */
#endif /* 0 */

		/* update UAPSD state */
		pCdb->bAPSDCapablePerAC[AcId] = FlgIsTrEnabled;
		pCdb->bAPSDDeliverEnabledPerAC[AcId] = FlgIsDeEnabled;

		/* update UAPSD control block information */
		if ((pCdb->bAPSDDeliverEnabledPerAC[QID_AC_BE] == 0) &&
			(pCdb->bAPSDDeliverEnabledPerAC[QID_AC_BK] == 0) &&
			(pCdb->bAPSDDeliverEnabledPerAC[QID_AC_VI] == 0) &&
			(pCdb->bAPSDDeliverEnabledPerAC[QID_AC_VO] == 0))
		{
			CLIENT_STATUS_CLEAR_FLAG(pCdb, fCLIENT_STATUS_APSD_CAPABLE);
		}
		else
		{
			CLIENT_STATUS_SET_FLAG(pCdb, fCLIENT_STATUS_APSD_CAPABLE);
		} /* End of if */

		if ((pCdb->bAPSDDeliverEnabledPerAC[QID_AC_BE] == 1) &&
			(pCdb->bAPSDDeliverEnabledPerAC[QID_AC_BK] == 1) &&
			(pCdb->bAPSDDeliverEnabledPerAC[QID_AC_VI] == 1) &&
			(pCdb->bAPSDDeliverEnabledPerAC[QID_AC_VO] == 1))
		{
			/* all AC are U-APSD delivery-enabled */
			pCdb->bAPSDAllAC = 1;

			ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> all AC are UAPSD!\n"));
		}
		else
		{
			/* at least one AC is not U-APSD delivery-enabled */
			pCdb->bAPSDAllAC = 0;

			ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> not all AC are UAPSD!\n"));
		} /* End of if */
#endif /* UAPSD_SUPPORT */
	} /* End of if */
#endif /* CONFIG_AP_SUPPORT */


#ifdef CONFIG_STA_SUPPORT
	if (ACMR_IS_STA_MODE(pAd))
	{
		BOOLEAN *pApsdCur;


		WMM_ACM_FUNC_NAME_PRINT("IN");

#ifdef RELEASE_EXCLUDE
		/*
			In IEEE802.11e spec., section 11.2.1.5 (c),
			When all ACs are delivery-enabled, the APSD-capable QAP shall
			assemble the partial virtual bitmap containing the buffer status
			for all ACs per destination for non-AP QSTAs.

			So we only care about delivery-enabled AC and we only record for
			trigger-enabled AC.
		*/
#endif /* RELEASE_EXCLUDE */
		if (Direction != ACM_DIRECTION_DOWN_LINK)
		{
			if (!FlgTsAdd)
				FlgIsApsdEnable = pAd->CommonCfg.bACMAPSDBackup[AcId];
			/* End of if */

			pAd->CommonCfg.bACMAPSDTr[AcId] = FlgIsApsdEnable;
		} /* End of if */

		if (Direction == ACM_DIRECTION_UP_LINK)
		{
			ACMR_DEBUG(ACMR_DEBUG_TRACE,
						("acm_msg> Not care uplink APSD (TR=%d)!\n",
						FlgIsApsdEnable));
			return;
		} /* End of if */

		if (FlgTsAdd)
		{
			ACMR_DEBUG(ACMR_DEBUG_TRACE,
						("acm_msg> UAPSD mode change to AcId = %d, dir = %d, "
						"TSPEC add, apsd = %d\n",
						AcId, Direction, FlgIsApsdEnable));
		}
		else
		{
			/* recover to old APSD state */
			FlgIsApsdEnable = pAd->CommonCfg.bACMAPSDBackup[AcId];

			ACMR_DEBUG(ACMR_DEBUG_TRACE,
						("acm_msg> UAPSD mode recover to AcId = %d, dir = %d, "
						"TSPEC del, apsd = %d\n",
						AcId, Direction, FlgIsApsdEnable));
		} /* End of if */

		switch(AcId)
		{
			case ACM_EDCA_VO_AC_QUE_ID:
				pApsdCur = &pAd->CommonCfg.bAPSDAC_VO;
				break;

			case ACM_EDCA_VI_AC_QUE_ID:
				pApsdCur = &pAd->CommonCfg.bAPSDAC_VI;
				break;

			case ACM_EDCA_BK_AC_QUE_ID:
				pApsdCur = &pAd->CommonCfg.bAPSDAC_BK;
				break;

			default:
				pApsdCur = &pAd->CommonCfg.bAPSDAC_BE;
				break;
		} /* End of switch */

		if (((FlgIsApsdEnable && (*pApsdCur))) ||
			((!FlgIsApsdEnable && !(*pApsdCur))))
		{
			/* nothing to do */
			return;
		} /* End of if */

		if (FlgIsApsdEnable)
		{
			ACMR_DEBUG(ACMR_DEBUG_TRACE,
						("acm_msg> AC%d is delivery-enabled AC!\n", AcId));
		}
		else
		{
			ACMR_DEBUG(ACMR_DEBUG_TRACE,
						("acm_msg> AC%d is not delivery-enabled AC!\n", AcId));
		} /* End of if */

		*pApsdCur = FlgIsApsdEnable;

#if 0
		/*
			2009/07/23
			We can not reset bAPSDCapable to 0.
			The flag is a static flag from RT28xxSTA.dat
			We can not modify it in any time.

			Or after AP disassociates us, MaxSPLength is always 0 in next
			association request frame due to pAd->CommonCfg.bAPSDCapable = 0 of
			MlmeAssocReqAction().
		*/
		if ((pAd->CommonCfg.bAPSDAC_VO == FALSE) &&
			(pAd->CommonCfg.bAPSDAC_VI == FALSE) &&
			(pAd->CommonCfg.bAPSDAC_BE == FALSE) &&
			(pAd->CommonCfg.bAPSDAC_BK == FALSE))
		{
			pAd->CommonCfg.bAPSDCapable = FALSE;
		}
		else
			pAd->CommonCfg.bAPSDCapable = TRUE;
		/* End of if */
#endif
	} /* End of if */
#endif /* CONFIG_STA_SUPPORT */
} /* End of ACM_APSD_Ctrl */


/*
========================================================================
Routine Description:
	Check whether bandwidth is enough to allow new stream.

Arguments:
	pAd					- WLAN control block pointer
	StmAcId				- the AC
	SI					- the service interval
	Policy				- ACM_ACCESS_POLICY_EDCA
	Direction			-	ACM_DIRECTION_UP_LINK,
							ACM_DIRECTION_DOWN_LINK,
							ACM_DIRECTION_DIRECT_LINK,
							ACM_DIRECTION_BIDIREC_LINK
	AcmTimeOld			- old used time of the same stream, unit: microsecond
	AcmTimeNew			- the requested time of new stream, unit: microsecond
	AcmTimeOldBi		- old used time for bidirectional, unit: microsecond
	*pTimeOffset		- the insufficient time when check result is fail
	*pVbAc				- the borrowed AC ID, 0 ~ 3
	*pVbBw				- the borrowed bandwidth from a AC, unit: microsecond

Return Value:
	ACM_RTN_OK			- allow
	ACM_RTN_FAIL		- doesnt allow due to total ACM time < Tcp,
						i.e. no any AC stream can be deleted to
						increase bandwidth
	ACM_RTN_INSUFFICIENT_BD_BUT_DEL_AC - doesnt allow but we maybe delete AC
										 streams to increase bandwidth

Note:
	pTimeOffset can be NULL;
	if pTimeOffset == NULL, no any output value will be set.

	Only used for QAP.
========================================================================
*/
STATIC ACM_FUNC_STATUS ACM_BandwidthCheck(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	UINT32					StmAcId,
	ACM_PARAM_IN	UINT32					SI,
	ACM_PARAM_IN	UINT32					Policy,
	ACM_PARAM_IN	UINT32					Direction,
	ACM_PARAM_IN	UINT32					AcmTimeOld,
	ACM_PARAM_IN	UINT32					AcmTimeNew,
	ACM_PARAM_IN	UINT32					AcmTimeOldBi,
	ACM_PARAM_OUT	UINT32					*pTimeOffset,
	ACM_PARAM_OUT	UINT32					*pVbAc,
	ACM_PARAM_OUT	UINT32					*pVbBw)
{
	UINT32 TimeResidual;
	UINT32 TimeAc10;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* init */
#ifdef RELEASE_EXCLUDE
	*pVbAc = ACM_DATL_NO_BORROW;
#else
	*pVbAc = 0xFF;
#endif /* RELEASE_EXCLUDE */
	*pVbBw = 0;

	/* check AcmTimeNew */
	if (AcmTimeNew > ACM_TIME_BASE)
		goto LabelErr; /* > time base */
	/* End of if */

	/* check whether Direction is bidirectional link */
	if (Direction == ACM_DIRECTION_BIDIREC_LINK)
	{
		/*
			double medium time for bidirectional because bidirectional
			link = uplink + dnlink

			Only for new time because
			AcmTimeOld = pOldStreamIn+pOldStreamOut+pOldStreamDiffAc

			But when old stream is bidirectional TSPEC for the same AC,
			we need to double the old time.
		*/
		AcmTimeNew = AcmTimeNew << 1;
		AcmTimeOld += AcmTimeOldBi;
	} /* End of if */

	ACMR_DEBUG(ACMR_DEBUG_TRACE,
				("acm_msg> Old time = %d, new time = %d\n", AcmTimeOld, AcmTimeNew));

	/* check whether new requested time <= old requested time */
	if (AcmTimeNew <= AcmTimeOld)
		return ACM_RTN_OK; /* bandwidth is enough */
	/* End of if */

	/* calculate reserved AC1/0 (best effort and background) time every sec */
	if ((StmAcId == ACM_EDCA_VI_AC_QUE_ID) ||
		(StmAcId == ACM_EDCA_VO_AC_QUE_ID))
	{
		/* only VI or VO ACM need to care about BE/BK reserved bandwidth */
		TimeAc10 = ACM_BANDWIDTH_CHECK_BASE * ACMR_CB->EdcaCtrlParam.BEK_MinNu;
		TimeAc10 /= ACMR_CB->EdcaCtrlParam.BEK_MinDe;
	}
	else
		TimeAc10 = 0;
	/* End of if */

	/* examine residual bandwidth */
	if (Policy == ACM_ACCESS_POLICY_EDCA)
	{
		/* EDCA stream */
		if (SI == 0)
		{
			/* no any HCCA stream exists */
			/* residual time = 1s - total ACM time */
			TimeResidual = ACM_BANDWIDTH_CHECK_BASE;
			TimeResidual -= ACMR_CB->EdcaCtrlParam.AcmTotalTime;
		}
		else
		{
			/* at lease one HCCA stream exists */
			/* residual time = 1s - total polled time - total ACM time */
			TimeResidual = SI; /* SI */
			TimeResidual = TimeResidual*ACM_BANDWIDTH_CHECK_BASE/SI;
			TimeResidual -= ACMR_CB->EdcaCtrlParam.AcmTotalTime;
		} /* End of if */

		/* substract reserved AC1/0 time from the residual time (1 second) */
		TimeResidual -= TimeAc10;

#ifdef ACM_CC_FUNC_MBSS
		/* substract time from other BSS */
		if (TimeResidual < ACMR_CB->MbssTotalUsedTime)
			TimeResidual = 0;
		else
			TimeResidual -= ACMR_CB->MbssTotalUsedTime;
		/* End of if */
#endif /* ACM_CC_FUNC_MBSS */

		/* check whether residual time is enough for the new stream */
		if ((TimeResidual + AcmTimeOld) <= AcmTimeNew)
		{
			/* residual bandwidth is not enough for new EDCA stream */
			goto LabelErr;
		} /* End of if */

#ifdef RELEASE_EXCLUDE
		/* dynamic ATL: also do dynamic ATL check */
		if (ACMR_CB->EdcaCtrlParam.FlgDatl)
		{
			if (ACM_DATL_Handle(pAd, StmAcId, AcmTimeOld, AcmTimeNew,
								pVbAc, pVbBw) != ACM_RTN_OK)
			{
				ACMR_DEBUG(ACMR_DEBUG_TRACE,
						("acm_msg> DATL not allow! BandwidthCheck()\n"));
				goto LabelErr;
			} /* End of if */
		} /* End of if */
#endif /* RELEASE_EXCLUDE */

		/* so bandwidth is enough */
		return ACM_RTN_OK;
	} /* End of if */

LabelErr:
	ACMR_DEBUG(ACMR_DEBUG_TRACE,
				("acm_msg> Not enough bandwidth (new request time = %dus)\n",
				AcmTimeNew));
	return ACM_RTN_FAIL;
} /* End of ACM_BandwidthCheck */


#ifdef RELEASE_EXCLUDE
/*
========================================================================
Routine Description:
	Check whether bandwidth is enough to allow new stream.

Arguments:
	pAd					- WLAN control block pointer
	DatlAcId			- the AC
	AcmTimeOld			- old used time of the same stream, unit: microsecond
	AcmTimeNew			- the requested time of new stream, unit: microsecond
	*pDatlAc			- the borrowed AC ID, 0 ~ 3
	*pDatlBw			- the borrowed bandwidth from a AC, unit: microsecond

Return Value:
	ACM_RTN_OK			- allow
	ACM_RTN_INSUFFICIENT_BD_BUT_DEL_AC - doesnt allow but we maybe delete AC
										 streams to increase bandwidth

Note:
	We only check available bandwidth of 'A' AC, not for all ACs.

	For example: if AC3 need more 100ms bandwidth, but residual bandwidth of
	AC2 is 80ms and residual bandwidth of AC1 is 60ms, we still NOT allow the
	TS.  We only check 100>80 and 100>60 so we can not allow the TS.
	We dont check 100<(80+60).
========================================================================
*/
STATIC ACM_FUNC_STATUS ACM_DATL_Handle(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	UINT32					StmAcId,
	ACM_PARAM_IN	UINT32					AcmTimeOld,
	ACM_PARAM_IN	UINT32					AcmTimeNew,
	ACM_PARAM_OUT	UINT32					*pDatlAc,
	ACM_PARAM_OUT	UINT32					*pDatlBw)
{
#define ACM_LMR_DATL_REAL_TIME_CAL(__Time, __Percent)	\
	__Time = __Percent;									\
	__Time *= AcmTotalTime;								\
	__Time /= 100;

	ACM_CTRL_PARAM *pEdcaParam;
	UINT32 *pTimeAcmCur;
	UCHAR *pBwMax, *pBwMin;
	INT32 AcmTotalTime;
	INT32 TimeOpCur, TimeOpMax, TimeOpOff;
	INT32 TimeAcCur, TimeAcMax, TimeAcMin, TimeAcOff;
	UINT32 BwBorAc[ACM_DEV_NUM_OF_AC][ACM_DEV_NUM_OF_AC];
	UINT32 IdAcNum, IdAcOther;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* sanity check */
	if ((AcmTimeOld != 0) && (AcmTimeNew <= AcmTimeOld))
		return ACM_RTN_OK;
	/* End of if */

	/* here, AcmTimeNew >= AcmTimeOld (AcmTimeOld maybe 0) */

	/* init */
	pEdcaParam = &(ACMR_CB->EdcaCtrlParam);
	pTimeAcmCur = pEdcaParam->AcmAcTime;
	pBwMax = pEdcaParam->DatlBwMax; /* unit: percentage */
	pBwMin = pEdcaParam->DatlBwMin; /* unit: percentage */
	ACMR_MEM_COPY(BwBorAc, pEdcaParam->DatlBorAcBw, sizeof(BwBorAc));

	*pDatlAc = ACM_DATL_NO_BORROW;
	*pDatlBw = 0;

	/* get TOTAL allowed ACM time */
	AcmTotalTime = ACM_TIME_BASE;

	/* get new used time for the AC */
	TimeOpCur = pTimeAcmCur[StmAcId] + AcmTimeNew - AcmTimeOld;
	if (TimeOpCur < 0)
		return ACM_RTN_OK; /* should not be here */
	/* End of if */

	/* get maximum allowed ACM time for the AC */
	ACM_LMR_DATL_REAL_TIME_CAL(TimeOpMax, pBwMax[StmAcId]);

#ifdef ACM_CC_FUNC_MBSS
	TimeOpCur += ACMR_CB->MbssAcUsedTime[StmAcId];
#endif /* ACM_CC_FUNC_MBSS */

	if (TimeOpCur <= TimeOpMax)
		return ACM_RTN_OK; /* yet exceed its max time so no borrow is needed */
	/* End of if */


	/* here, extra bandwidth is needed so calculating needed extra bandwidth */
	TimeOpOff = AcmTimeNew - AcmTimeOld; /* >= 0 */

	/* sub extra bandwidth by self available bandwidth */
	if ((INT32)(pTimeAcmCur[StmAcId]) < TimeOpMax)
	{
		if (TimeOpOff <= (TimeOpMax - (INT32)(pTimeAcmCur[StmAcId])))
		{
			/* current available bandwidth is enough */

			/*
				Should not be here because TimeOpCur will
				<= TimeOpMax, we will return above.
			*/
			return ACM_RTN_OK;
		} /* End of if */

		TimeOpOff -= (TimeOpMax - pTimeAcmCur[StmAcId]);
	} /* End of if */

	/*
		Check the needed extra bandwidth in the available bandwidth
		of other ACs.
	*/
	for(IdAcNum=0; IdAcNum<ACM_DEV_NUM_OF_AC; IdAcNum++)
	{
		if (pEdcaParam->FlgAcmStatus[IdAcNum] == 0)
			continue; /* ACM is disabled */
		/* End of if */

		if (IdAcNum == StmAcId)
			continue; /* skip same AC */
		/* End of if */

		if (pBwMin[IdAcNum] >= pBwMax[IdAcNum])
			continue; /* no extra bandwidth can be borrow */
		/* End of if */

		/* calculate cur/max/min time for the AC, unit: microsecond */
		TimeAcCur = pTimeAcmCur[IdAcNum];
		ACM_LMR_DATL_REAL_TIME_CAL(TimeAcMax, pBwMax[IdAcNum]);
		ACM_LMR_DATL_REAL_TIME_CAL(TimeAcMin, pBwMin[IdAcNum]);

#ifdef ACM_CC_FUNC_MBSS
		TimeAcCur += ACMR_CB->MbssAcUsedTime[IdAcNum];
#endif /* ACM_CC_FUNC_MBSS */

		/* no available bandwidth for the AC */
		if (TimeAcCur > TimeAcMax)
			continue;
		/* End of if */

		/*
			If cur used time > min threshold time, use cur used time as
			min threshold time.
		*/
		if (TimeAcCur > TimeAcMin)
			TimeAcMin = TimeAcCur;
		/* End of if */

		/* accumulate borrowed bandwidth for the AC i */
		for(IdAcOther=0; IdAcOther<ACM_DEV_NUM_OF_AC; IdAcOther++)
			TimeAcMin += BwBorAc[IdAcNum][IdAcOther];
		/* End of for */

		/* calculate residual bandwidth that can be borrowed */
		TimeAcOff = TimeAcMax - TimeAcMin;

		if (TimeOpOff <= TimeAcOff)
		{
			/* we can borrow the bandwidth from the AC */
			*pDatlAc = IdAcNum;
			*pDatlBw = TimeOpOff;

			ACMR_DEBUG(ACMR_DEBUG_TRACE,
						("acm_msg> borrow bandwidth %dus from AC%d!"
						"ACM_DATL_Handle()\n", TimeOpOff, IdAcNum));
			return ACM_RTN_OK;
		} /* End of if */

		/* bandwidth is not enough, check next one */
	} /* End of for */

	return ACM_RTN_INSUFFICIENT_BD_BUT_DEL_AC;
} /* End of ACM_DATL_Handle */


/*
========================================================================
Routine Description:
	Update ACM DATL information.

Arguments:
	pAd					- WLAN control block pointer
	StmAcId				- the AC
	AcmTimeOld			- old used time of the same stream, unit: microsecond
	AcmTimeNew			- the requested time of new stream, unit: microsecond
	DatlAcId			- the borrowed AC ID, 0 ~ 3
	DatlBandwidth		- the borrowed bandwidth from a AC, unit: microsecond

Return Value:
	None

Note:
	When AcmTimeNew > AcmTimeOld, DatlBandwidth will be the extra needed
	bandwidth (AcmTimeNew - AcmTimeOld).

	When AcmTimeNew <= AcmTimeOld, DatlBandwidth will be 0.
========================================================================
*/
STATIC VOID ACM_DATL_Update(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	UINT32					StmAcId,
	ACM_PARAM_IN	UINT32					AcmTimeOld,
	ACM_PARAM_IN	UINT32					AcmTimeNew,
	ACM_PARAM_OUT	UINT32					DatlAcId,
	ACM_PARAM_OUT	UINT32					DatlBandwidth)
{
	ACM_CTRL_PARAM  *pEdcaParam;
	UINT32 IdAcNum;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* init */
	pEdcaParam = &(ACMR_CB->EdcaCtrlParam);

	/* reduce time_offset from any borrow bandwidth of other AC */
	if ((AcmTimeOld != 0) && (AcmTimeOld > AcmTimeNew))
	{
		/* time_offset is the bandwidth we need to reclaim it to other AC */
		UINT32 time_offset = AcmTimeOld - AcmTimeNew;


		for(IdAcNum=0; IdAcNum<ACM_DEV_NUM_OF_AC; IdAcNum++)
		{
			if (IdAcNum == StmAcId)
				continue;
			/* End of if */

			if (time_offset > pEdcaParam->DatlBorAcBw[IdAcNum][StmAcId])
			{
				/* reclaim some time to the AC */
				time_offset -= pEdcaParam->DatlBorAcBw[IdAcNum][StmAcId];
				pEdcaParam->DatlBorAcBw[IdAcNum][StmAcId] = 0;

				/* check next AC */
			}
			else
			{
				/* reclaim all time to the AC */
				pEdcaParam->DatlBorAcBw[IdAcNum][StmAcId] -= time_offset;
				break;
			} /* End of if */
		} /* End of for */
	} /* End of if */

	/* accumulate new borrowed bandwidth */
	if (DatlAcId != ACM_DATL_NO_BORROW)
		pEdcaParam->DatlBorAcBw[DatlAcId][StmAcId] += DatlBandwidth;
	/* End of if */
} /* End of ACM_DATL_Update */
#endif /* RELEASE_EXCLUDE */


/*
========================================================================
Routine Description:
	Send a delts frame to the peer.

Arguments:
	pAd				- WLAN control block pointer
	*pCdb			- the destination QSTA
	*pStream		- the stream

Return Value:
	TRUE			- send successfully
	FALSE			- send fail

Note:
========================================================================
*/
STATIC BOOLEAN ACM_DeltsFrameSend(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	ACMR_STA_DB				*pCdb,
	ACM_PARAM_IN	ACM_STREAM				*pStream)
{
	UCHAR *pPktBuf;
	UINT32 Len;
	ACM_TS_INFO TsInfo;


	if (ACMR_MGMT_FME_ALLOCATE(pAd, &pPktBuf) != NDIS_STATUS_SUCCESS)
		return FALSE;
	/* End of if */

	Len = 0;

#ifdef CONFIG_AP_SUPPORT
	if (ACMR_IS_AP_MODE(pAd))
		Len = ACM_FrameDeltsToStaMakeUp(pAd, pCdb, pPktBuf, pStream);
	/* End of if */
#endif /* CONFIG_AP_SUPPORT */
#ifdef CONFIG_STA_SUPPORT
	if (ACMR_IS_STA_MODE(pAd))
		Len = ACM_FrameDeltsToApMakeUp(pAd, pCdb, pPktBuf, pStream);
	/* End of if */
#endif /* CONFIG_STA_SUPPORT */

	ACMR_MEM_COPY(&TsInfo, &pStream->pTspec->TsInfo, sizeof(ACM_TS_INFO));
	ACMR_MGMT_PKT_TX(pAd, pPktBuf, Len);
	ACMR_MGMT_FME_FREE(pAd, pPktBuf);

#ifdef CONFIG_AP_SUPPORT
	if (ACMR_IS_AP_MODE(pAd))
	{
		if (ACMR_STATION_IS_NOT_IN_PS_MODE(pCdb))
		{
			ACM_DeltsFrameACK(pAd, ACMR_CLIENT_MAC(pStream->pCdb),
								(UCHAR *)&pStream->pTspec->TsInfo, 0);
			ACM_FrameBwAnnSend(pAd, FALSE);
		} /* End of if */
	} /* End of if */
#endif /* CONFIG_AP_SUPPORT */

#ifdef CONFIG_STA_SUPPORT
	if (ACMR_IS_STA_MODE(pAd))
	{
		ACM_DeltsFrameACK(pAd, ACMR_AP_ADDR_GET(pAd),
						(UCHAR *)&pStream->pTspec->TsInfo, 0);
		ACM_FrameBwAnnSend(pAd, FALSE);
	} /* End of if */
#endif /* CONFIG_STA_SUPPORT */

	return TRUE;
} /* End of ACM_DeltsFrameSend */


/*
========================================================================
Routine Description:
	Get extra data length for different entrypt mode.

Arguments:
	pAd				- WLAN control block pointer
	*pCdb			- the source QSTA

Return Value:
	the extra data length

Note:
========================================================================
*/
STATIC UINT32 ACM_EncryptExtraLenGet(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	ACMR_STA_DB				*pCdb)
{
	UINT32 DataExtraLen;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	if (pCdb == NULL)
		return 0; /* error */
	/* End of if */

	if (ACMR_STA_ENCRYPT_MODE_GET(pCdb) == ACMR_ENCRYPT_WEP)
	{
		/* IV (4B) + ICV (4B) */
		DataExtraLen = 8;
	}
	else if (ACMR_STA_ENCRYPT_MODE_GET(pCdb) == ACMR_ENCRYPT_TKIP)
	{
		/* IV/KeyID (4B) + Extended IV (4B) + MIC (8B) + ICV (4B) */
		DataExtraLen = 20;
	}
	else if (ACMR_STA_ENCRYPT_MODE_GET(pCdb) == ACMR_ENCRYPT_AES)
	{
		/* CCMP Header (8B) + MIC (8B) */
		DataExtraLen = 16;
	}
	else
		DataExtraLen = 0;
	/* End of if */

	return DataExtraLen;
} /* End of ACM_EncryptExtraLenGet */


#ifdef CONFIG_STA_SUPPORT_SIM
/*
========================================================================
Routine Description:
	Make up a WME Setup Request frame to the QAP.

Arguments:
	pAd					- WLAN control block pointer
	*pCdb				- the peer
	*pBufFrame			- the packet buffer
	*pReqNew			- the requested TSPEC pointer

Return Value:
	Frame Length

Note:
	1. Use high priority queue to send.
	2. Only for QSTA mode (AP Client).
========================================================================
*/
STATIC UINT32 ACM_FrameAddtsReqMakeUp(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	ACMR_STA_DB				*pCdb,
	ACM_PARAM_IN	UCHAR					*pBufFrame,
	ACM_PARAM_IN	ACM_STREAM				*pReqNew)
{
	ACMR_WLAN_HEADER	HdrActionFrame;
	ULONG				FrameLen;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* init */
	FrameLen = 0;

	ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> Make up a ADDTS Request...\n"));

	/* make the frame header */
	MgtMacHeaderInit(
					pAd, &HdrActionFrame, SUBTYPE_ACTION, 0,
					ACMR_AP_ADDR_GET(pAd),
					ACMR_CLIENT_MAC(pReqNew->pCdb));
	MakeOutgoingFrame(
					pBufFrame, &FrameLen,
					sizeof(ACMR_WLAN_HEADER), &HdrActionFrame,
					END_OF_ARGS);

	/* make the frame body */
	FrameLen += ACM_WME_ActionFrameBodyMake(
											pAd, pReqNew,
											(UCHAR *)&pBufFrame[FrameLen],
											ACM_ACTION_WME_SETUP_REQ,
											0);
	return FrameLen;
} /* End of ACM_FrameAddtsReqMakeUp */
#endif /* CONFIG_STA_SUPPORT_SIM */


#ifdef CONFIG_STA_SUPPORT

/*
========================================================================
Routine Description:
	Make up a WME Teardown frame to the QAP.

Arguments:
	pAd				- WLAN control block pointer
	*pCdb			- the peer
	*pBufFrame		- the packet buffer
	*pStream		- the stream want to delete

Return Value:
	Frame Length

Note:
	1. Use high priority queue to send.
	2. No resource protection here.
========================================================================
*/
STATIC UINT32 ACM_FrameDeltsToApMakeUp(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	ACMR_STA_DB				*pCdb,
	ACM_PARAM_IN	UCHAR					*pBufFrame,
	ACM_PARAM_IN	ACM_STREAM				*pStream)
{
	ACMR_WLAN_HEADER	HdrActionFrame;
	ULONG				FrameLen;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* init */
	FrameLen = 0;

	ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> Make up a DELTS to QAP...\n"));

	/* make the frame header */
	MgtMacHeaderInit(
					pAd, &HdrActionFrame, SUBTYPE_ACTION, 0,
					ACMR_AP_ADDR_GET(pAd),
					pStream->StaMac);

	MakeOutgoingFrame(
					pBufFrame, &FrameLen,
					sizeof(ACMR_WLAN_HEADER), &HdrActionFrame,
					END_OF_ARGS);

	/* make the frame body */
	FrameLen += ACM_WME_ActionFrameBodyMake(
											pAd, pStream,
											(UCHAR *)&pBufFrame[FrameLen],
											ACM_ACTION_WME_TEAR_DOWN,
											0);
	return FrameLen;
} /* End of ACM_FrameDeltsToApMakeUp */

#endif /* CONFIG_STA_SUPPORT */


#ifdef CONFIG_AP_SUPPORT

/*
========================================================================
Routine Description:
	Make up a WME Teardown frame to the QSTA.

Arguments:
	pAd				- WLAN control block pointer
	*pCdb			- the peer, no use
	*pBufFrame		- the packet buffer
	*pStream		- the stream want to delete

Return Value:
	Frame Length

Note:
	1. Use high priority queue to send.
	2. No resource protection here.
========================================================================
*/
STATIC UINT32 ACM_FrameDeltsToStaMakeUp(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	ACMR_STA_DB				*pCdb,
	ACM_PARAM_IN	UCHAR					*pBufFrame,
	ACM_PARAM_IN	ACM_STREAM				*pStream)
{
	ACMR_WLAN_HEADER	HdrActionFrame;
	ULONG				FrameLen;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* init */
	FrameLen = 0;

	ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> Make up a DELTS to QSTA...\n"));

	/* make the frame header */
	MgtMacHeaderInit(
					pAd, &HdrActionFrame, SUBTYPE_ACTION, 0,
					ACMR_CLIENT_MAC(pStream->pCdb),
					pAd->ApCfg.MBSSID[pStream->pCdb->apidx].Bssid);
	MakeOutgoingFrame(
					pBufFrame, &FrameLen,
					sizeof(ACMR_WLAN_HEADER), &HdrActionFrame,
					END_OF_ARGS);

	/* make the frame body */
	FrameLen += ACM_WME_ActionFrameBodyMake(
											pAd, pStream,
											(UCHAR *)&pBufFrame[FrameLen],
											ACM_ACTION_WME_TEAR_DOWN,
											0);
	return FrameLen;
} /* End of ACM_FrameDeltsToStaMakeUp */
#endif /* CONFIG_AP_SUPPORT */


/*
========================================================================
Routine Description:
	Increase or decrease the link number counter.

Arguments:
	pAd					- WLAN control block pointer
	AccessPolicy		- ACM_ACCESS_POLICY_EDCA
	Dir					-	ACM_DIRECTION_UP_LINK,
							ACM_DIRECTION_DOWN_LINK,
							ACM_DIRECTION_DIRECT_LINK,
							ACM_DIRECTION_BIDIREC_LINK
	FlgIsAdd			- 1: increase by 1; 0: decrease by 1

Return Value:
	None

Note:
========================================================================
*/
STATIC VOID ACM_LinkNumCtrl(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	UINT32					AccessPolicy,
	ACM_PARAM_IN	UINT32					Dir,
	ACM_PARAM_IN	UINT32					FlgIsAdd)
{
	UINT32 *pAcLinkNum[ACM_DIRECTION_MAX] = \
				{	&ACMR_CB->EdcaCtrlParam.LinkNumUp,
					&ACMR_CB->EdcaCtrlParam.LinkNumDn,
					&ACMR_CB->EdcaCtrlParam.LinkNumDi,
					&ACMR_CB->EdcaCtrlParam.LinkNumBi };
	UINT32 *pNumStm;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	if (AccessPolicy == ACM_ACCESS_POLICY_EDCA)
		pNumStm = pAcLinkNum[Dir];
	else
	{
		ACMR_DEBUG(ACMR_DEBUG_ERR,
					("acm_err> non-WMM is not supported! LinkNumCtrl()\n"));
		return;
	} /* End of if */

	if (FlgIsAdd == 1)
		(*pNumStm) ++; /* a new link is accepted */
	else
	{
		if ((*pNumStm) > 0)
			(*pNumStm) --; /* a old link is deleted */
		else
		{
			ACMR_DEBUG(ACMR_DEBUG_ERR,
						("acm_err> link number == 0! dir = %d "
						"ACM_LinkNumCtrl()\n", Dir));
			(*pNumStm) = 0; /* fix it */
		} /* End of if */
	} /* End of if */
} /* End of ACM_LinkNumCtrl */


/*
========================================================================
Routine Description:
	Set the minimum PHY Mode and MCS to the packet.

Arguments:
	pAd				- WLAN control block pointer
	*pStream		- the stream pointer

Return Value:
	ACM_RTN_OK		- get ok
	ACM_RTN_FAIL	- get fail

Note:
	We have checked the min physical rate should be the supported rate.
	So dont need to empty the non-supported rate in
	gAcmMCS_CCK[] or gAcmMCS_OFDM[].
========================================================================
*/
STATIC VOID ACM_PacketPhyModeMCSSet(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	ACM_STREAM				*pStream)
{
	UINT16 Rate[] = { /* non-11n rate */
		10, 20, 55, 60, 90, 110, 120, 180, 240, 360, 480, 540 };
	UINT32 MinPhyRate; /* unit: bps */
	UCHAR PhyModeMin, McsMin;
	UINT32 PreambleId, RateId;
	ACMR_STA_DB *pCdb;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* sanity check */
	if (pStream->pTspec->MinPhyRate == 0)
		return;
	/* End of if */

	/* init */
	PhyModeMin = ACMR_PHY_NONE;
	McsMin = 0;
	PreambleId = 0; /* 0: long preamble, 1: short preamble */
	pCdb = pStream->pCdb;

	if (ACMR_STA_IS_SPREAMBLE(pAd, pCdb))
		PreambleId = 1;
	/* End of if */

	/* find the phy mode & mcs based on minimum physical rate */
	while(1)
	{
		MinPhyRate = pStream->pTspec->MinPhyRate;

		for(RateId=0; RateId<ACM_RATE_B_NUM; RateId++)
		{
			if (MinPhyRate == \
				((UINT32)gAcmMCS_CCK[PreambleId][RateId][1] * ACM_RATE_UNIT))
			{
				PhyModeMin = ACMR_PHY_CCK;
				McsMin = gAcmMCS_CCK[1][RateId][0];
				break;
			} /* End of if */
		} /* End of for */

		if (PhyModeMin == ACMR_PHY_NONE)
		{
			/* not found in CCK rate so try to find it in OFDM rate */
			for(RateId=0; RateId<ACM_RATE_G_NUM; RateId++)
			{
				if (MinPhyRate == \
					((UINT32)gAcmMCS_OFDM[RateId][1] * ACM_RATE_UNIT))
				{
					PhyModeMin = ACMR_PHY_OFDM;
					McsMin = gAcmMCS_OFDM[RateId][0];
					break;
				} /* End of if */
			} /* End of for */
		} /* End of if */

#ifdef ACM_CC_FUNC_11N
		if (PhyModeMin == ACMR_PHY_NONE)
		{
			BOOLEAN FlgIs2040;
			UINT32 IdGI;


			/* not found in OFDM rate so try to find it in N rate */
			FlgIs2040 = ACMR_IS_2040_STA(pCdb);

			for(IdGI=0; IdGI<2; IdGI++)
			{
				for(RateId=0; RateId<ACM_RATE_MAX_NUM_HT; RateId++)
				{
					if (MinPhyRate == \
						((UINT32)gAcmMCS_HT[FlgIs2040][0][RateId] * \
																ACM_RATE_UNIT))
					{
						/* always use regular GI */
						PhyModeMin = ACMR_PHY_HT;
						McsMin = RateId;
						break;
					} /* End of if */
				} /* End of for */
			} /* End of for */
		} /* End of if */
#endif /* ACM_CC_FUNC_11N */

		if (PhyModeMin != ACMR_PHY_NONE)
		{
			/* correct MIN PHY rate */
			break;
		} /* End of if */

		/* should not be here */
		ACMR_DEBUG(ACMR_DEBUG_TRACE,
					("acm_msg> MIN PHY Rate %d bps is not "
					"our supported rate!\n",
					MinPhyRate));

		/* the rate does not belong to any specified rate */
		/* we need to choose one as the minimum physical rate */
		MinPhyRate = MinPhyRate / ACM_RATE_UNIT;

		/* try to find the correct rate in non-HT rates */
		for(RateId=0; RateId<ACM_RATE_MAX_NUM; RateId++)
		{
			if (MinPhyRate < Rate[RateId])
			{
				if (RateId > 0)
					RateId--;
				/* End of if */

				MinPhyRate = Rate[RateId];
				break;
			} /* End of if */
		} /* End of for */

#ifdef ACM_CC_FUNC_11N
		/* if not found, try to find the correct rate in HT rates */
		if (RateId == ACM_RATE_MAX_NUM)
		{
			BOOLEAN FlgIs2040;
			UINT32 McsRate, McsRateMin;
			UINT32 OffsetMin;


			FlgIs2040 = ACMR_IS_2040_STA(pCdb);
			McsRateMin = gAcmMCS_HT[FlgIs2040][0][0] * ACM_RATE_UNIT;
			OffsetMin = 0xFFFFFFFF;

#ifdef RELEASE_EXCLUDE
			/*
				Try to find the correct MCS rate with minimum offset with
				the incorrect minimum physical rate and smaller than the
				correct MCS rate.

				Note: HT MCS 1 'maybe' not smaller than HT MCS 2.
			*/
#endif /* RELEASE_EXCLUDE */
			for(RateId=0; RateId<ACM_RATE_MAX_NUM_HT; RateId++)
			{
				McsRate = (UINT32)gAcmMCS_HT[FlgIs2040][0][RateId];

				if (McsRate <= MinPhyRate)
				{
					if ((MinPhyRate - McsRate) < OffsetMin)
					{
						OffsetMin = (MinPhyRate - McsRate);
						McsRateMin = McsRate;
					} /* End of if */
				} /* End of if */
			} /* End of for */

			MinPhyRate = McsRateMin;
		} /* End of if */
#endif /* ACM_CC_FUNC_11N */

		/* adjust the minimum physical rate to the correct rate number */
		pStream->pTspec->MinPhyRate = MinPhyRate * ACM_RATE_UNIT;

		ACMR_DEBUG(ACMR_DEBUG_TRACE,
					("acm_msg> Correct MIN PHY Rate to %d bps!\n",
					pStream->pTspec->MinPhyRate));
	} /* End of while */

	/* assign minimum phy mode & mcs to the stream */
	pStream->PhyModeMin = PhyModeMin;
	pStream->McsMin = McsMin;

	ACMR_DEBUG(ACMR_DEBUG_TRACE,
				("acm_msg> Min PHY mode = %d, Min MCS = %d\n",
				PhyModeMin, McsMin));
} /* End of ACM_PacketPhyModeMCSSet */


/*
========================================================================
Routine Description:
	Get the MAC address of next client data base.

Arguments:
	pAd				- WLAN control block pointer
	*pDevIndex		- search base, first = 0
	*pDevMac		- the MAC of the client

Return Value:
	ACM_RTN_OK		- get successfully
	ACM_RTN_FAIL	- no client can be got

Note:
========================================================================
*/
STATIC ACM_FUNC_STATUS ACM_PeerDeviceMacGetNext(
	ACM_PARAM_IN		ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN_OUT	UINT32					*pDevIndex,
	ACM_PARAM_IN		UCHAR					*pDevMac)
{
	ACMR_STA_DB *pCdb;
	UINT32 IdDevNum;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* init */
	pCdb = NULL; /* initial value must be NULL */

	/* search for all client entries */
	for(IdDevNum=(*pDevIndex); IdDevNum<ACMR_STA_MAX_NUM; IdDevNum++)
	{
		pCdb = ACMR_STA_GET(pAd, pCdb, IdDevNum);

		if (ACMR_STA_IS_VALID(pCdb))
		{
			/* the client entry exists */
			ACMR_MEM_MAC_COPY(pDevMac, ACMR_CLIENT_MAC(pCdb));

			/* return next client entry index */
			*pDevIndex = (IdDevNum+1);
			return ACM_RTN_OK;
		} /* End of if */
	} /* End of for */

	/* no any client exists */
	return ACM_RTN_FAIL;
} /* End of ACM_PeerDeviceMacGetNext */


#ifdef CONFIG_STA_SUPPORT
/*
========================================================================
Routine Description:
	Change PS mode to ACTIVE mode.

Arguments:
	pAd			- WLAN control block pointer

Return Value:
	None

Note:
	We will enter ACTIVE mode after we send a ADDTS request frame.
========================================================================
*/
STATIC VOID ACM_PS_ActiveOn(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd)
{
#ifndef ACM_CC_FUNC_PS_MGMT_FME
	WMM_ACM_FUNC_NAME_PRINT("IN");

	ACMR_STA_PS_MODE_ACTIVE(pAd);

	ACMR_DEBUG(ACMR_DEBUG_TRACE,
				("acm_msg> Active On! PS_ActiveOn()\n"));
#endif /* ACM_CC_FUNC_PS_MGMT_FME */
} /* End of ACM_PS_ActiveOn */


/*
========================================================================
Routine Description:
	Enable or disable TSPEC UAPSD function.

Arguments:
	pAd				- WLAN control block pointer
	FlgIsEnable		- 1: enable; 0: disable

Return Value:
	None

Note:
	Only for QSTA.

	If TSPEC UAPSD function is disabled, the UAPSD field of TSPEC will
	be same as the value in station association request frame.
========================================================================
*/
STATIC VOID ACM_PS_UapsdCtrl(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	UCHAR					FlgIsEnable)
{
	ACM_CTRL_PARAM  *pEdcaParam;


	pEdcaParam = &(ACMR_CB->EdcaCtrlParam);
	pEdcaParam->FlgIsTspecUpasdEnable = FlgIsEnable;

	ACMR_DEBUG(ACMR_DEBUG_TRACE,
				("acm_msg> TSPEC UAPSD function flag = %d!\n", FlgIsEnable));
} /* End of ACMP_TC_UapsdCtrl */
#endif /* CONFIG_STA_SUPPORT */


/*
========================================================================
Routine Description:
	Mapping current station rate to ACM rate.

Arguments:
	pAd				- WLAN control block pointer
	*pCdb			- the QSTA

Return Value:
	ACM_PRE_TIME_ID_1M, etc.

Note:
========================================================================
*/
STATIC UCHAR ACM_Rate_Mapping(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	ACMR_STA_DB				*pCdb)
{
#ifdef RELEASE_EXCLUDE
	/*
		[0] is used in WLAN module;
		[1] is used in tx time calculation;
		[2] is used in tx time array index.
	*/
#endif /* RELEASE_EXCLUDE */
	UCHAR RateMapping[ACM_RATE_MAX_NUM][3] = {
		{ ACMR_RATE_DRV_1M,		ACM_RATE_1M,	ACM_PRE_TIME_ID_1M },
		{ ACMR_RATE_DRV_2M,		ACM_RATE_2M,	ACM_PRE_TIME_ID_2M },
		{ ACMR_RATE_DRV_5_5M,	ACM_RATE_5_5M,	ACM_PRE_TIME_ID_5_5M },
		{ ACMR_RATE_DRV_11M,	ACM_RATE_11M,	ACM_PRE_TIME_ID_11M },
		{ ACMR_RATE_DRV_6M,		ACM_RATE_6M,	ACM_PRE_TIME_ID_6M },
		{ ACMR_RATE_DRV_9M,		ACM_RATE_9M,	ACM_PRE_TIME_ID_9M },
		{ ACMR_RATE_DRV_12M,	ACM_RATE_12M,	ACM_PRE_TIME_ID_12M },
		{ ACMR_RATE_DRV_18M,	ACM_RATE_18M,	ACM_PRE_TIME_ID_18M },
		{ ACMR_RATE_DRV_24M,	ACM_RATE_24M,	ACM_PRE_TIME_ID_24M },
		{ ACMR_RATE_DRV_36M,	ACM_RATE_36M,	ACM_PRE_TIME_ID_36M },
		{ ACMR_RATE_DRV_48M,	ACM_RATE_48M,	ACM_PRE_TIME_ID_48M },
		{ ACMR_RATE_DRV_54M,	ACM_RATE_54M,	ACM_PRE_TIME_ID_54M } };
	UINT32 PhyMode, MCS;
	UINT32 RateIndex;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	RateIndex = ACM_RATE_MAX_NUM - 1; /* default use maximum rate */

	ACMR_CLIENT_PHY_MODE_MCS_GET(pCdb, PhyMode, MCS);

	if (PhyMode == ACMR_PHY_CCK)
	{
		/* CCK PHY */
		ACMR_CLIENT_CCK_RATE_INDEX_GET(MCS, RateIndex);
	}
	else
	{
		/* OFDM PHY */
		ACMR_CLIENT_OFDM_RATE_INDEX_GET(MCS, RateIndex);
	} /* End of if */

	return RateMapping[RateIndex][2];
} /* End of ACM_Rate_Mapping */


/*
========================================================================
Routine Description:
	Get the output or input TSPEC array list of the device.

Arguments:
	pAd					- WLAN control block pointer
	*pDevMac			- the MAC of the client
	FlgIsOutputLink		- 1: output links; 0: input links

Return Value:
	ACM_RTN_OK			- get ok
	ACM_RTN_FAIL		- get fail

Note:
========================================================================
*/
STATIC UCHAR **ACM_StationTspecListGet(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	UCHAR					*pDevMac,
	ACM_PARAM_IN	BOOLEAN					FlgIsOutputLink)
{
	ACMR_STA_DB *pCdb;
	ACM_ENTRY_INFO *pStaAcmInfo;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	pCdb = ACMR_STA_ENTRY_GET(pAd, pDevMac);
	if (pCdb == NULL)
	{
		/* maybe the peer device disassociated or we disassociated it */
		return NULL;
	} /* End of if */

	pStaAcmInfo = ACMR_STA_ACM_PARAM_INFO(pCdb);

	if (FlgIsOutputLink == TRUE)
		return pStaAcmInfo->pAcStmOut;
	/* End of if */

	return pStaAcmInfo->pAcStmIn;
} /* End of ACM_StationTspecListGet */


/*
========================================================================
Routine Description:
	Get the TSID from the packet from VLAN ID or DSCP.

Arguments:
	pAd				- WLAN control block pointer
	*pMbuf			- the QoS frame

Return Value:
	the TSID

Note:
========================================================================
*/
STATIC UCHAR ACM_TSID_Get(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd,
	ACM_PARAM_IN	ACMR_MBUF			*pMbuf)
{
	ACM_IPHDR *pIpHdr;
	PUCHAR pBufSrc;
	UCHAR TSID;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	TSID = 0; /* TSID = default BE */

	ACMR_PKT_DATA_GET(pMbuf, pBufSrc);

	if (((pBufSrc[12] << 8) + pBufSrc[13]) == 0x0800)
	{
		/* IPv4 packet */
		pIpHdr = (ACM_IPHDR *)&pBufSrc[14];
		TSID = pIpHdr->TOS >> 5; /* UP = DSCP / 8 */
	}
	else
	{
		/* VLAN priority is larger than DSCP */
		if (((pBufSrc[12] << 8) + pBufSrc[13]) == 0x8100)
		{
			/* VLAN packet */
			TSID = (pBufSrc[14] & 0xe0) >> 5;
		} /* End of if */
	} /* End of if */

	return TSID;
} /* End of ACM_TSID_Get */


/*
========================================================================
Routine Description:
	Get the physical transmit queue type.

Arguments:
	AcmAcId				- physical AC ID

Return Value:
	the physical transmit queue type

Note:
========================================================================
*/
STATIC UINT32 ACM_TxQueueTypeGet(
	ACM_PARAM_IN	UCHAR		AcmAcId)
{
	/* EDCA stream, AC0 ~ AC3 */
	switch(AcmAcId)
	{
		case 0:
			return ACMR_QID_AC_BE;	/* AC0 (BE) stream */

		case 1:
			return ACMR_QID_AC_BK;	/* AC1 (BK) stream */

		case 2:
			return ACMR_QID_AC_VI;	/* AC2 (VI) stream */

		case 3:
			return ACMR_QID_AC_VO;	/* AC3 (VO) stream */

		default: /* fatal error */
			ACMR_DEBUG(ACMR_DEBUG_ERR,
					("acm_err> EDCA AcmAcId > 3! TxQueueTypeGet()\n"));
			return ACMR_QID_AC_BE;	/* default: AC0 (BE) stream */
	} /* End of switch */
} /* End of ACM_TxQueueTypeGet */




/* =========== Peer device management function =========== */

/*
========================================================================
Routine Description:
	Insert the peer device to the backup link list.

Arguments:
	pAd					- WLAN control block pointer
	*pDevMac			- the MAC of the QSTA

Return Value:
	ACM_RTN_OK			- add ok
	ACM_RTN_FAIL		- add fail

Note:
========================================================================
*/
STATIC ACM_FUNC_STATUS ACM_PeerDeviceAdd(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	UCHAR					*pDevMac)
{
	ACM_PEER_DEV_LIST *pAcmStmList, *pStmLast;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* init */
	pAcmStmList = ACMR_CB->pDevListPeer;

	/* check whether no any QSTA exists in the backup list */
	if (pAcmStmList == NULL)
	{
		ACMR_MEM_ALLOC(ACMR_CB->pDevListPeer,
						sizeof(ACM_PEER_DEV_LIST), (ACM_PEER_DEV_LIST *));

		if (ACMR_CB->pDevListPeer == NULL)
			goto LabelAllocFail;
		/* End of if */

		ACMR_CB->pDevListPeer->pPrev = NULL;
		ACMR_CB->pDevListPeer->pNext = NULL;
		ACMR_MEM_MAC_COPY(ACMR_CB->pDevListPeer->MAC, pDevMac);
		return ACM_RTN_OK;
	} /* End of if */

	/* search the peer device in the list and find the last one */
	while(pAcmStmList != NULL)
	{
		if (AMR_IS_SAME_MAC(pAcmStmList->MAC, pDevMac))
		{
			/* the peer device has already existed so no need to backup it */
			return ACM_RTN_OK;
		} /* End of if */

		pStmLast = pAcmStmList;
		pAcmStmList = pAcmStmList->pNext;
	} /* End of while */

	pAcmStmList = pStmLast;

	/* do not find so append the new peer device to the last one */
	ACMR_MEM_ALLOC(pStmLast, sizeof(ACM_PEER_DEV_LIST), (ACM_PEER_DEV_LIST *));
	if (pStmLast == NULL)
		goto LabelAllocFail;
	/* End of if */

	pAcmStmList->pNext = pStmLast;

	pStmLast->pPrev = pAcmStmList;
	pStmLast->pNext = NULL;
	ACMR_MEM_MAC_COPY(pStmLast->MAC, pDevMac);
	return ACM_RTN_OK;

LabelAllocFail:
	ACMR_DEBUG(ACMR_DEBUG_ERR, ("acm_err> Allocate peer device entry fail!\n"));
	return ACM_RTN_FAIL;
} /* End of ACM_PeerDeviceAdd */


/*
========================================================================
Routine Description:
	Delete the peer device from the backup link list.

Arguments:
	pAd					- WLAN control block pointer
	*pDevMac			- the MAC of the QSTA

Return Value:
	None

Note:
========================================================================
*/
STATIC VOID ACM_PeerDeviceDel(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	UCHAR					*pDevMac)
{
	ACM_PEER_DEV_LIST *pDevList, *pDevNext;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	pDevList = ACMR_CB->pDevListPeer;

	while(1)
	{
		if (pDevList == NULL)
		{
			/* can not found the next peer device */
			break;
		} /* End of if */

		pDevNext = pDevList->pNext;

		if (AMR_IS_SAME_MAC(pDevList->MAC, pDevMac))
		{
			/* find it and delete it */
			if (pDevList->pPrev == NULL)
				ACMR_CB->pDevListPeer = pDevList->pNext;
			else
				(pDevList->pPrev)->pNext = pDevList->pNext;
			/* End of if */

			if (pDevList->pNext != NULL)
				(pDevList->pNext)->pPrev = pDevList->pPrev;
			/* End of if */

			ACMR_MEM_FREE(pDevList);

			/* not break, try to delete all redudant entries for the peer */
			/* should not occur */
		} /* End of if */

		pDevList = pDevNext;
	} /* End of while */
} /* End of ACM_PeerDeviceDel */


/*
========================================================================
Routine Description:
	Get next the peer device from the backup link list.

Arguments:
	pAd					- WLAN control block pointer
	**ppDevicePeer		- the last peer device
	*pDevMac			- the MAC of the QSTA

Return Value:
	ACM_RTN_OK			- get ok
	ACM_RTN_FAIL		- get fail

Note:
========================================================================
*/
STATIC ACM_FUNC_STATUS ACM_PeerDeviceGetNext(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	ACM_PEER_DEV_LIST		**ppDevicePeer,
	ACM_PARAM_IN	UCHAR					*pDevMac)
{
	ACM_PEER_DEV_LIST *pDevNext;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	if (*ppDevicePeer == NULL)
	{
		/* get first peer */
		pDevNext = ACMR_CB->pDevListPeer;

		if (pDevNext == NULL)
		{
			*ppDevicePeer = NULL;
			return ACM_RTN_FAIL;
		} /* End of if */
	}
	else
	{
		/* get next peer */
		pDevNext = *ppDevicePeer;

		if (pDevNext->pNext == NULL)
		{
			*ppDevicePeer = NULL;
			return ACM_RTN_FAIL;
		} /* End of if */

		pDevNext = pDevNext->pNext;
	} /* End of if */

	*ppDevicePeer = pDevNext;
	ACMR_MEM_MAC_COPY(pDevMac, pDevNext->MAC);
	return ACM_RTN_OK;
} /* End of ACM_PeerDeviceGetNext */


/*
========================================================================
Routine Description:
	Free all the peer device backup link list.

Arguments:
	pAd				- WLAN control block pointer

Return Value:
	None

Note:
========================================================================
*/
STATIC VOID ACM_PeerDeviceAllFree(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd)
{
	ACM_PEER_DEV_LIST *pDevList, *pDevNext;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	pDevList = ACMR_CB->pDevListPeer;

	while(pDevList)
	{
		pDevNext = pDevList->pNext;
		ACMR_MEM_FREE(pDevList);
		pDevList = pDevNext;
	} /* End of while */

	ACMR_CB->pDevListPeer = NULL;
} /* End of ACM_PeerDeviceAllFree */


/*
========================================================================
Routine Description:
	Maintain the peer device backup link list.

Arguments:
	pAd					- WLAN control block pointer
	*pDevMac			- the MAC of the QSTA

Return Value:
	None

Note:
========================================================================
*/
STATIC VOID ACM_PeerDeviceMaintain(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	UCHAR					*pDevMac)
{
	ACMR_STA_DB *pCdb;
	ACM_ENTRY_INFO *pStaAcmInfo;
	UINT32 IdTidNum;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* get device entry */
	pCdb = ACMR_STA_ENTRY_GET(pAd, pDevMac);
	if (pCdb == NULL)
		return;
	/* End of if */

	/* get ACM control block of the device */
	pStaAcmInfo = ACMR_STA_ACM_PARAM_INFO(pCdb);

	/* check if all TS are deleted for the device */
	for(IdTidNum=0; IdTidNum<ACM_STA_TID_MAX_NUM; IdTidNum++)
	{
		if (pStaAcmInfo->pAcStmOut[IdTidNum] != NULL)
		{
			/* at least one output TS exists so dont kill the entry */
			return;
		} /* End of if */

		if (pStaAcmInfo->pAcStmIn[IdTidNum] != NULL)
		{
			/* at least one input TS exists so dont kill the entry */
			return;
		} /* End of if */
	} /* End of for */

	/* no any TS exists so we delete the peer device record */
	ACM_PeerDeviceDel(pAd, pDevMac);
} /* End of ACM_PeerDeviceMaintain */




/* ========================= Stream Management Function ===================== */

/*
========================================================================
Routine Description:
	Check whether a stream is timeout due to inactivity or suspendsion.

Arguments:
	pAd					- WLAN control block pointer
	*pStream			- the activated stream

Return Value:
	TRUE				- Need to send out a DELTS frame
	FALSE				- No need to send out a DELTS frame

Note:
	We can not delete steams only after station disassociated.
	Because maybe one link timeout but other links do not timeout for a
	QSTA.
========================================================================
*/
STATIC BOOLEAN ACM_STM_IdleCheck(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd,
	ACM_PARAM_IN	ACM_STREAM			*pStream)
{
	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* check current stream status */
	if ((pStream->Status != TSPEC_STATUS_ACTIVE) &&
		(pStream->Status != TSPEC_STATUS_ACTIVE_SUSPENSION))
	{
		/* the request will be deleted, do NOT need to care about it */
		return FALSE;
	} /* End of if */

	/* check inactivity timeout */
	if (pStream->pTspec->InactivityInt != ACM_TSPEC_INACTIVITY_DISABLE)
	{
		/* inactivity function is enabled */

		if (pStream->InactivityCur <= ACM_STREAM_CHECK_BASE)
		{
			/* stream is timeout so we need to delete the stream */
			if (pStream->InactivityCur > 0)
			{
#ifdef RELEASE_EXCLUDE
				/*
					For bidirectional link, if dnlink is deleting, uplink will
					not be deleting; when dnlink is deleted, uplink will also
					be deleted.  For its uplink, pStream->InactivityCur will
					be 0 until its dnlink is deleted.
				*/
#endif /* RELEASE_EXCLUDE */

				ACMR_DEBUG(ACMR_DEBUG_TRACE,
							("acm_msg> Inactivity timeout! TX a DELTS frame! "
							"(dir %d) ACM_STM_IdleCheck()!\n",
							pStream->pTspec->TsInfo.Direction));

				pStream->InactivityCur = 0;
				pStream->Cause = TSPEC_CAUSE_INACTIVITY_TIMEOUT;
				return ACM_TC_Delete(pAd, pStream);
			} /* End of if */
		}
		else
			pStream->InactivityCur -= ACM_STREAM_CHECK_BASE;
		/* End of if */
	} /* End of if */

	return FALSE;
} /* End of ACM_STM_IdleCheck */


/*
========================================================================
Routine Description:
	Copy the stream information.

Arguments:
	*pStreamSrc				- the source stream
	*pStreamInfoDst			- the destination buffer

Return Value:
	None

Note:
========================================================================
*/
STATIC VOID ACM_STM_InfoCopy(
	ACM_PARAM_OUT	ACM_STREAM_INFO     *pStreamInfoDst,
	ACM_PARAM_IN	ACM_STREAM			*pStreamSrc)
{
	UINT32 IdTclasNum;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* copy information */
	ACM_TSPEC_COPY((&(pStreamInfoDst->Tspec)), pStreamSrc->pTspec);

	for(IdTclasNum=0; IdTclasNum<ACM_TSPEC_TCLAS_MAX_NUM; IdTclasNum++)
	{
		if (pStreamSrc->pTclas[IdTclasNum] != NULL)
		{
			ACM_TCLAS_COPY((&(pStreamInfoDst->Tclas[IdTclasNum])),
							pStreamSrc->pTclas[IdTclasNum]);
		} /* End of if */
	} /* End of for */

	ACMR_MEM_MAC_COPY(pStreamInfoDst->DevMac, pStreamSrc->StaMac);

	pStreamInfoDst->TclasProcessing	= pStreamSrc->TclasProcessing;
	pStreamInfoDst->StreamType		= pStreamSrc->StreamType;
	pStreamInfoDst->UP				= pStreamSrc->UP;
	pStreamInfoDst->Status			= pStreamSrc->Status;
	pStreamInfoDst->Cause			= pStreamSrc->Cause;
	pStreamInfoDst->AcmAcId			= pStreamSrc->AcmAcId;
	pStreamInfoDst->FlgOutLink		= pStreamSrc->FlgOutLink;
	pStreamInfoDst->Reserved2[0]	= 0;
	pStreamInfoDst->Reserved2[1]	= 0;
	pStreamInfoDst->Reserved2[2]	= 0;
	pStreamInfoDst->InactivityCur	= pStreamSrc->InactivityCur;
	pStreamInfoDst->SuspensionCur	= pStreamSrc->SuspensionCur;
	pStreamInfoDst->PhyModeMin		= pStreamSrc->PhyModeMin;
	pStreamInfoDst->McsMin			= pStreamSrc->McsMin;
} /* End of ACM_STM_InfoCopy */


/*
========================================================================
Routine Description:
	Check inactivity and suspension for all activated streams.

Arguments:
	Data				- WLAN control block pointer

Return Value:
	None

Note:
	This is a tasklet.
========================================================================
*/
STATIC VOID ACM_TASK_STM_Check(
	ACM_PARAM_IN	ULONG		Data)
{
	ACMR_PWLAN_STRUC pAd;
	ACM_CTRL_PARAM *pEdcaParam;
	ACM_PEER_DEV_LIST *pAcmDevList;
	ACM_STREAM **ppAcmStmList, *pStream;
	ACMR_LIST StreamList;
	ACM_FUNC_STATUS RtnCode;
	UCHAR DirectionId[2] = \
					{ ACM_PEER_TSPEC_OUTPUT_GET, ACM_PEER_TSPEC_INPUT_GET };
	UCHAR MAC[ACM_MAC_ADDR_LEN], MAC_Last[ACM_MAC_ADDR_LEN];
	UCHAR UP;
	BOOLEAN FlgIsNeedToDel;
	UINT32 IdTidNum, IdLinkNum, AcId;
	ULONG SplFlags;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* init */
	pAd = (ACMR_PWLAN_STRUC) Data;
	RtnCode = ACM_RTN_FAIL;
	FlgIsNeedToDel = FALSE;
	ACMR_LIST_INIT(&StreamList);

	/* get management semaphore */
	ACM_TSPEC_IRQ_LOCK_CHK(pAd, SplFlags, LabelSemErr);

	/* sanity check */
	pEdcaParam = &ACMR_CB->EdcaCtrlParam;

	if (pEdcaParam->FlgIsTspecTimeoutEnable == 0)
	{
		ACM_TSPEC_IRQ_UNLOCK(pAd, SplFlags, LabelSemErr);
		return; /* no need to do TSPEC timeout check */
	} /* End of if */

	/* check all output and input links for each peer */
	pAcmDevList = NULL;

	while(1)
	{
		/* get next device */
		if (ACM_PeerDeviceGetNext(pAd, &pAcmDevList, MAC) != ACM_RTN_OK)
			break;
		/* End of if */

		/*
			Check if we need to delete the device entry due to NO any output
			and input link for the peer device.
		*/
		if (FlgIsNeedToDel == TRUE)
			ACM_PeerDeviceDel(pAd, MAC_Last);
		/* End of if */

		/* check all output and input streams for the peer device */
		for(IdLinkNum=0; IdLinkNum<2; IdLinkNum++)
		{
			ppAcmStmList = (ACM_STREAM **)ACM_StationTspecListGet(
											pAd, MAC, DirectionId[IdLinkNum]);
			if (ppAcmStmList == NULL)
			{
				/*
					We can not delete the entry here or we can not find the
					pNext in ACM_PeerDeviceGetNext(), we need to use sta_p at
					next loop.
				*/
				ACMR_MEM_MAC_COPY(MAC_Last, MAC);
				FlgIsNeedToDel = TRUE;
				break;
			} /* End of if */

			for(IdTidNum=0; IdTidNum<ACM_STA_TID_MAX_NUM; IdTidNum++)
			{
				pStream = ppAcmStmList[IdTidNum];

				if (pStream != NULL)
				{
					if ((IdLinkNum == 1) &&
						(pStream->pTspec->TsInfo.Direction == \
													ACM_DIRECTION_BIDIREC_LINK))
					{
						/*
							For bidirectional link, we will check the one
							when IdLinkNum == 0, so we dont need to check twice.
						*/
						continue;
					} /* End of if */

					UP = pStream->pTspec->TsInfo.UP;
                    AcId = ACM_MR_EDCA_AC(UP);

					/* do NOT check NULL TSPEC */
                    if (ACMR_CB->EdcaCtrlParam.FlgAcmStatus[AcId] != 0)
					{
						RtnCode = ACM_RTN_OK;

						if (ACM_STM_IdleCheck(pAd, pStream) == TRUE)
						{
							/* the stream timeouts so we need to delete it */
							ACMR_LIST_ALLOC_AND_INSERT_TO_TAIL(pAd, &StreamList, pStream);
						} /* End of if */
					} /* End of if */
				} /* End of if */
			} /* End of for */

			FlgIsNeedToDel = FALSE;
		} /* End of for */
	} /* End of while */

	/* check if no any activated stream exists */
	if (RtnCode == ACM_RTN_FAIL)
	{
#ifndef ACMR_HANDLE_IN_TIMER
		/* disable the stream activity & suspend check timer */
		ACMR_TIMER_DISABLE(ACMR_CB->FlgStreamAliveCheckEnable,
							ACMR_CB->TimerStreamAliveCheck);
#endif /* ACMR_HANDLE_IN_TIMER */
	}
	else
	{
#ifdef ACMR_HANDLE_IN_TIMER
		/* schedule next timer */
		ACMR_TIMER_ENABLE(ACMR_CB->FlgStreamAliveCheckEnable,
							ACMR_CB->TimerStreamAliveCheck,
							ACM_STREAM_CHECK_OFFSET);
#endif /* ACMR_HANDLE_IN_TIMER */
	} /* End of if */

	/* release semaphore */
	ACM_TSPEC_IRQ_UNLOCK(pAd, SplFlags, LabelSemErr);

	/* send a DELTS frame for each TSPEC */
	while(1)
	{
		pStream = (ACM_STREAM *)ACMR_LIST_REMOVE_FRM_HEAD(&StreamList);
		if (pStream == NULL)
			break;
		/* End of if */

		ACM_DELTS_SEND(pAd, pStream->pCdb, pStream, LabelSemErr);

		/* isolate the stream */
		pStream->pPrev = NULL;
		pStream->pNext = NULL;

		/* free it */
		ACM_TC_Free(pAd, pStream);
	}
	return;

LabelSemErr:
	ACMR_SEM_FAIL_PRINT();
	return;
} /* End of ACM_TASK_STM_Check */


/*
========================================================================
Routine Description:
	Check inactivity and suspension for all activated streams.

Arguments:
	Data			- WLAN control block pointer

Return Value:
	None

Note:
	1. Only for QAP Mode.
	2. Waked up every 100ms.
========================================================================
*/
VOID ACM_TR_STM_Check(
	ACM_PARAM_IN	ULONG		Data)
{
#ifndef ACMR_HANDLE_IN_TIMER
	ACMR_PWLAN_STRUC pAd = (ACMR_PWLAN_STRUC) Data;
	ULONG SplFlags;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	ACM_TSPEC_IRQ_LOCK_CHK(pAd, SplFlags, LabelSemErr);

	/* sanity check for enable flag */
	if (ACMR_CB->FlgStreamAliveCheckEnable == 0)
	{
		ACMR_TIMER_DISABLE(ACMR_CB->FlgStreamAliveCheckEnable,
							ACMR_CB->TimerStreamAliveCheck);
		ACM_TSPEC_IRQ_UNLOCK(pAd, SplFlags, LabelSemErr);
		return;
	} /* End of if */

	/* inform TSPEC request check task */
	ACMR_TASK_ACTIVATE(ACMR_CB->TaskletStreamAliveCheck,
						ACMR_CB->TimerStreamAliveCheck,
						ACM_STREAM_CHECK_OFFSET);
	ACM_TSPEC_IRQ_UNLOCK(pAd, SplFlags, LabelSemErr);
	return;

LabelSemErr:
	ACMR_SEM_FAIL_PRINT();
	return;
#else

	ACM_TASK_STM_Check(Data);
#endif /* ACMR_HANDLE_IN_TIMER */
} /* End of ACM_TR_STM_Check */




/* =========================== 11e TSPEC Function =========================== */

/*
========================================================================
Routine Description:
	Inform us that tx compleletion interrupt occurred.

Arguments:
	pAd				- WLAN control block pointer
	*pDevMac		- the destination MAC
	*pTsInfo		- the TS Info of the packet
	FlgIsErr		- if the frame tx is error

Return Value:
	None

Note:
	1. Responsible for DELTS ACK frame check.
	2. Now we call the function after DELTS frame is sent immediately.
	3. If we need to guarantee the DELTS frame is received in peer device,
		we need to call the function in TX done service routine.
========================================================================
*/
VOID ACM_DeltsFrameACK(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd,
	ACM_PARAM_IN	UCHAR				*pDevMac,
	ACM_PARAM_IN	UCHAR				*pTsInfo,
	ACM_PARAM_IN	UCHAR				FlgIsErr)
{
	ACM_STREAM *pStream;
	ACMR_STA_DB *pCdb;
	UINT32 NumMaxSearch;
	UCHAR StmAcId;
	UCHAR Direction;
/*	UCHAR FlgIsActive; */


	WMM_ACM_FUNC_NAME_PRINT("IN");

#if 0 /* if we use IRQ LOCK in WLAN module, we enable the check */
	/* sanity check */
	if (!ACMR_IS_ENABLED(pAd))
	{
		ACMR_DEBUG(ACMR_DEBUG_ERR,
				("acm_err> WMM ACM is disabled! DeltsFrameACK()\n"));
		return;
	} /* End of if */

	/* DELTS ACK frame is received so we can free the stream */
	ACMR_DEBUG(ACMR_DEBUG_TRACE,
				("acm_msg> RV a DELTS ACK! DeltsFrameACK()\n"));
#endif /* 0 */

	/* init */
	pCdb = NULL;
	NumMaxSearch = 0;
	StmAcId = 0;
	Direction = 0;
/*	FlgIsActive = 0; */

	/* find it in request or active list */
	while(1)
	{
		/* should be once in the while loop */
		pStream = ACM_TC_Find(pAd, pDevMac, (ACM_TS_INFO *)pTsInfo);
		if (pStream == NULL)
			break; /* not find */
		/* End of if */

		pCdb = pStream->pCdb;
		StmAcId = pStream->AcmAcId;
		Direction = pStream->pTspec->TsInfo.Direction;

#if 0
		if ((pStream->Status == TSPEC_STATUS_ACTIVE) ||
			(pStream->Status == TSPEC_STATUS_ACTIVE_SUSPENSION) ||
			(pStream->Status == TSPEC_STATUS_ACT_DELETING))
		{
			FlgIsActive = 1;
		} /* End of if */
#endif

		/* destroy the request or active stream */
		ACM_TC_Destroy(pAd, pStream, 0);

		ACMR_DEBUG(ACMR_DEBUG_TRACE,
					("acm_msg> DEL the request ok! DeltsFrameACK()\n"));

		if (++NumMaxSearch > 5)
			break; /* avoid forever loop, should not be here */
		/* End of if */
	} /* End of while */

	/* recover the UAPSD state if the deleted TSPEC is ever actived */
	/* we have already recover UAPSD state in ACM_TC_Destroy() */
#if 0
	if ((pCdb != NULL) && (FlgIsActive))
		ACM_APSD_Ctrl(pAd, pCdb, StmAcId, Direction, 0, 0);
	/* End of if */
#endif /* 0 */

#ifdef RELEASE_EXCLUDE
	/*
		Note: We can not call ACM_PS_CtrlRightReturn() here because
		managemnet lock mabye locked before calling ACM_DeltsFrameACK();
		If we call management frame tx function in ACM_PS_CtrlRightReturn(),
		the tx function will lock again to cause 'CPU lock bug'.

		So we remove calling it here.
	*/
#endif /* RELEASE_EXCLUDE */
	return;
} /* End of ACM_DeltsFrameACK */


/*
========================================================================
Routine Description:
	Translate factor decimal part binary to decimal. (unit: 1/100)

Arguments:
	BIN				- the binary of decimal part

Return Value:
	the decimal

Note:
	Ex: 0b0001 1000 0000 0000 ==> 0.75
========================================================================
*/
UINT32 ACM_SurplusFactorDecimalBin2Dec(
	ACM_PARAM_IN	UINT32				BIN)
{
	UINT32 ValueMax, Bit1Index, Base, ValueDec, ValueCarry;
	UINT32 IdBitNum;


	WMM_ACM_FUNC_NAME_PRINT("IN");

#ifdef RELEASE_EXCLUDE
	/* init */
	/* ex1: 0b0001 1000 0000 0000 */
	/* ex2: 0b0001 0000 0000 0000 */
	/* ex3: 0b0000 0010 1110 0001 */
#endif /* RELEASE_EXCLUDE */
	ValueMax = 1;
	ValueMax <<= ACM_SURPLUS_DEC_BIT_NUM;

	Bit1Index = ValueMax>>1;
	Base = 0;
	ValueDec = 0;

	/* translate to decimal, unit: 1/(1<<ACM_SURPLUS_DEC_BIT_NUM) */
	for(IdBitNum=0; IdBitNum<ACM_SURPLUS_DEC_BIT_NUM; IdBitNum++)
	{
#ifdef RELEASE_EXCLUDE
		/* 8192 = 2^13 */
		/* ex1: decimal = 8192 + 4096 */
		/* ex2: decimal = 8192 */
#endif /* RELEASE_EXCLUDE */
		if (BIN & Bit1Index)
			ValueDec += ValueMax >> Base;
		/* End of if */

		/* check next bit */
		Bit1Index >>= 1;
		Base ++;
	} /* End of for */

#ifdef RELEASE_EXCLUDE
	/* translate to decimal, unit: 1/ACM_SURPLUS_DEC_BASE */
	/* ex1: decimal = 12288 * 100 / 8192 / 2 = 75 (i.e. 0.75) */
	/* ex2: decimal = 8192 * 100 / 8192 / 2 = 50 */
#endif /* RELEASE_EXCLUDE */
	ValueDec *= ACM_SURPLUS_DEC_BASE;
	ValueCarry = ValueDec * 10;
	ValueDec >>= ACM_SURPLUS_DEC_BIT_NUM;
	ValueDec >>= 1;
	ValueCarry >>= ACM_SURPLUS_DEC_BIT_NUM;
	ValueCarry >>= 1;

	/* check if we need to carry (>= 0.5) */
	if ((ValueCarry - ValueDec*10) >= 5)
		ValueDec ++;
	/* End of if */

	return ValueDec;
} /* End of ACM_SurplusFactorDecimalBin2Dec */


/*
========================================================================
Routine Description:
	Translate factor decimal part decimal to binary.

Arguments:
	DEC				- the deciaml of decimal part, 00 ~ 99 (base 1/100)

Return Value:
	the binary

Note:
	Ex: 0.75 ==> 0b0001 1000 0000 0000
========================================================================
*/
STATIC UINT32 ACM_SurplusFactorDecimalDec2Bin(
	ACM_PARAM_IN	UINT32				DEC)
{
	UINT32 ValueMax, Bin1Index, ValueBin;
	UINT32 NumBit, TempValueDec;
	UINT32 IdBitNum;


	WMM_ACM_FUNC_NAME_PRINT("IN");

#ifdef RELEASE_EXCLUDE
	/* init */
	/* ex1:	0.75, DEC = 75 */
	/* ex2:	0.5,  DEC = 50 */
	/* ex3:	0.1,  DEC = 10 */
	/* ex4: 0.09, DEC = 9 */
#endif /* RELEASE_EXCLUDE */
	Bin1Index = 1;
	Bin1Index <<= (ACM_SURPLUS_DEC_BIT_NUM-1);

	ValueBin = 0;
	DEC = DEC % 100; /* 0.01 ~ 0.99 */

	NumBit = 0;
	ValueMax = 100;

	/* translate to binary */
	for(IdBitNum=0; IdBitNum<ACM_SURPLUS_DEC_BIT_NUM; IdBitNum++)
	{
#ifdef RELEASE_EXCLUDE
		/* DEC = DEC * 2 */
		/* ex1: DEC = 150 */
		/* ex2: DEC = 10 */
		/* ex3: DEC = 2 */
#endif /* RELEASE_EXCLUDE */
		DEC <<= 1;

#ifdef RELEASE_EXCLUDE
		/* check if carry */
		/* ex1:	TempValueDec = 150/100 = 1
				ValueBin = 0x1000
				DEC = 50 */
		/* ex2: TempValueDec = 10/10 = 1
				ValueBin = 0x1000
				DEC = 0 */
#endif /* RELEASE_EXCLUDE */
		TempValueDec = DEC;
		TempValueDec /= ValueMax;
		if (TempValueDec)
		{
			ValueBin |= Bin1Index;
			DEC -= ValueMax;
		} /* End of if */

		Bin1Index >>= 1;
	} /* End of for */

	return ValueBin;
} /* End of ACM_SurplusFactorDecimalDec2Bin */


/*
========================================================================
Routine Description:
	Active a requested TSPEC and move it to the active table.

Arguments:
	pAd					- WLAN control block pointer
	*pStreamReq			- the requested TSPEC pointer

Return Value:
	ACM_RTN_OK			- active ok
	ACM_RTN_FAIL		- active fail

Note:
	1. for QAP, the dnlink link TSPEC will be moved to the active
		table; the bidirectional link TSPEC will be duplicated to
		the active table & the client data base; otherwise to the
		client data base;
	2. for QSTA, the uplink or direct link TSPEC will be moved to
		the active table; otherwise to the client data base;
	3. If the requested TSPEC is a negotiation TSPEC, the old
		TSPEC will be freed.
========================================================================
*/
STATIC ACM_FUNC_STATUS ACM_TC_Active(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd,
	ACM_PARAM_IN	ACM_STREAM			*pStreamReq)
{
	ACMR_STA_DB *pCdb;
	ACM_ENTRY_INFO *pStaAcmInfo;
	UINT32 Direction, AccessPolicy;
	UCHAR FlgOutLink;
	UCHAR StmAcId;
	UCHAR TSID;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> TC_Active()!\n"));

	/* check whether current status of the request is DELETING */
	if ((pStreamReq->Status == TSPEC_STATUS_REQ_DELETING) ||
		(pStreamReq->Status == TSPEC_STATUS_ACT_DELETING))
	{
		/* rearrange the requested TSPEC */
		ACM_TC_Rearrange(pAd, pStreamReq, ACM_MAX_NUM_OF_DELTS_RETRY);
		return ACM_RTN_FAIL;
	} /* End of if */

	/* init */
	pCdb		= pStreamReq->pCdb;
	FlgOutLink	= 0; /* default input link */
	AccessPolicy= pStreamReq->pTspec->TsInfo.AccessPolicy;
	Direction	= pStreamReq->pTspec->TsInfo.Direction;
	StmAcId		= 0; /* 0: BE */

	/* determine if the link is output link */
	if (ACMR_IS_AP_MODE(pAd))
	{
		/* QAP mode */
		if ((Direction == ACM_DIRECTION_DOWN_LINK) ||
			(Direction == ACM_DIRECTION_BIDIREC_LINK))
		{
			FlgOutLink = 1;
		} /* End of if */
	}
	else
	{
		/* QSTA mode */
		if ((Direction == ACM_DIRECTION_UP_LINK) ||
			(Direction == ACM_DIRECTION_DIRECT_LINK) ||
			(Direction == ACM_DIRECTION_BIDIREC_LINK))
		{
			FlgOutLink = 1;
		} /* End of if */
	} /* End of if */

	/* backup the new stream */
	pStaAcmInfo  = ACMR_STA_ACM_PARAM_INFO(pCdb);
	TSID = pStreamReq->pTspec->TsInfo.TSID;

	if (FlgOutLink == 1)
	{
		/* assign actual AC queue ID */
		StmAcId = ACM_MR_EDCA_AC(pStreamReq->UP);
		pStreamReq->TxQueueType = ACM_TxQueueTypeGet(StmAcId);

		/* backup output stream by TSID */
		pStaAcmInfo->pAcStmOut[TSID] = (UCHAR *)pStreamReq;

		/* for bidirectional link, we also backup it in input array by TSID */
		/* for bidirectional link, FlgOutLink must be 1 */
		if (Direction == ACM_DIRECTION_BIDIREC_LINK)
			pStaAcmInfo->pAcStmIn[TSID] = (UCHAR *)pStreamReq;
		/* End of if */
	}
	else
	{
		/* no transmit queue for input link */
		pStreamReq->TxQueueType = ACM_TX_QUEUE_TYPE_NOT_EXIST;

		/* backup input stream by TSID */
		pStaAcmInfo->pAcStmIn[TSID] = (UCHAR *)pStreamReq;
	} /* End of if */

	/* change status to ACTIVE */
	pStreamReq->FlgOutLink = FlgOutLink;
	pStreamReq->Status = TSPEC_STATUS_ACTIVE;

	/* statustics counter */
	ACM_LINK_NUM_INCREASE(pAd, pStreamReq->pTspec->TsInfo.AccessPolicy, Direction);

	/* count number of TSPEC */
	ACM_NUM_OF_TSPEC_RECOUNT(pAd, pStreamReq->pCdb);

	return ACM_RTN_OK;
} /* End of ACM_TC_Active */


/*
========================================================================
Routine Description:
	Remove a activated TSPEC.

Arguments:
	pAd					- WLAN control block pointer
	*pStream			- the actived stream

Return Value:
	None

Note:
	"Activated" means the TSPEC is accepted, whatever dnlink or uplink or
	bidirectional link.
========================================================================
*/
STATIC VOID ACM_TC_ActRemove(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd,
	ACM_PARAM_IN	ACM_STREAM			*pStream)
{
	ACMR_STA_DB *pCdb;
	ACM_ENTRY_INFO *pStaAcmInfo;
	ACM_STREAM *pStmFree;
	BOOLEAN FlgIsNeedToFree;
	UCHAR TSID;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* sanity check */
	pCdb = pStream->pCdb;
	if (pCdb == NULL)
		return;
	/* End of if */

	/* check if the request is from a actived stream */
	FlgIsNeedToFree = 0;

	if ((pStream->Status == TSPEC_STATUS_ACT_DELETING) ||
		(pStream->Status == TSPEC_STATUS_RENEGOTIATING))
	{
		/* pStream is in request list so we will free other copys in
			non-request lists */
		FlgIsNeedToFree = 1;
	} /* End of if */

	/* reset the backup array for the stream */
	pStaAcmInfo = ACMR_STA_ACM_PARAM_INFO(pCdb);
	TSID = pStream->pTspec->TsInfo.TSID;

	if (pStream->FlgOutLink)
		pStmFree = (ACM_STREAM *)pStaAcmInfo->pAcStmOut[TSID];
	else
		pStmFree = (ACM_STREAM *)pStaAcmInfo->pAcStmIn[TSID];
	/* End of if */

	pStaAcmInfo->pAcStmOut[TSID] = NULL;
	pStaAcmInfo->pAcStmIn[TSID] = NULL;

	if (pStmFree == NULL)
	{
		ACMR_DEBUG(ACMR_DEBUG_TRACE,
					("acm_err> TSPEC == NULL! TC_ActRemove()\n"));
	} /* End of if */

	/* maybe free it */
	if ((FlgIsNeedToFree) && (pStmFree != NULL))
		ACM_TC_Free(pAd, pStmFree);
	/* End of if */
} /* End of ACM_TC_ActRemove */


/*
========================================================================
Routine Description:
	Delete a activated TSPEC.

Arguments:
	pAd					- WLAN control block pointer
	*pStream			- the activated stream

Return Value:
	TRUE				- Need to send out a DELTS frame
	FALSE				- No need to send out a DELTS frame

Note:
	1. Send a DELTS to the QSTA or QAP.
	2. Insert the activated TSPEC to the requested list.
	3. The TSPEC will be moved to the failed list when DELTS ACK
		is received or retry count is reached.
========================================================================
*/
STATIC BOOLEAN ACM_TC_Delete(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd,
	ACM_PARAM_IN	ACM_STREAM			*pStream)
{
	ACM_STREAM *pTspecDup;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	if ((pStream->Status == TSPEC_STATUS_REQ_DELETING) ||
		(pStream->Status == TSPEC_STATUS_ACT_DELETING))
	{
		/* already in deleting state */
		return FALSE;
	} /* End of if */

	if ((pStream->Status == TSPEC_STATUS_REQUEST) ||
		(pStream->Status == TSPEC_STATUS_RENEGOTIATING))
	{
		/* only for QSTA mode: request TSPEC or renegotiate TSPEC */

		ACM_TC_Req_ADDTS2DELTS(pAd, pStream);
		goto label_check_timer_enable;
	} /* End of if */

	if ((pStream->Status == TSPEC_STATUS_ACTIVE) ||
		(pStream->Status == TSPEC_STATUS_ACTIVE_SUSPENSION))
	{
		/* QAP or QSTA mode: actived TSPEC */

		/*
			Duplicate the stream, because we can not insert the stream to the
			requested list directly; Or the pPrev & pNext will be modified in
			ACM_TC_ReqInsert().

			So we duplicate a same stream and put it to the request list.

			If deletion is successfully, we will move the stream and the
			duplicated one.
		*/
		pTspecDup = ACM_TC_Duplicate(pAd, pStream);
		if (pTspecDup == NULL)
		{
			/* no enough memory, delete the stream at next time */
			pStream->InactivityCur = ACM_STREAM_CHECK_BASE;
			return FALSE;
		} /* End of if */

		/* change its state to deleting mode */
		pTspecDup->Status			= TSPEC_STATUS_ACT_DELETING;
		pTspecDup->TimeoutAction	= ACM_TC_TIMEOUT_ACTION_DELTS;
		pTspecDup->Timeout			= pStream->TimeoutDelts;
		pTspecDup->Retry			= ACM_MAX_NUM_OF_DELTS_RETRY;

		/* insert the duplicated stream to the requested list */
		if (ACM_TC_ReqInsert(pAd, pTspecDup) != ACM_RTN_OK)
		{
			/* insert fail (maybe already exist), free duplicate one */
			ACM_TC_Free(pAd, pTspecDup);

			/* delete the stream at next time */
			pStream->InactivityCur = ACM_STREAM_CHECK_BASE;
		}
		else
		{
			/* insert ok so send a DELTS frame to peer */

			/*
				The new allocated stream is put into the request list so
				we dont need to use the stream to send DELTS, we can use
				pStream to send the DELTS frame.
			*/
			goto label_check_timer_enable;
		} /* End of if */
	} /* End of if */

	return FALSE;

label_check_timer_enable:
	/* enable request timeout check timer */
	ACMR_TIMER_ENABLE(ACMR_CB->FlgTspecReqCheckEnable,
						ACMR_CB->TimerTspecReqCheck,
						ACM_STREAM_CHECK_OFFSET);
	return TRUE;
} /* End of ACM_TC_Delete */


/*
========================================================================
Routine Description:
	Move TSPEC active the the fail list.

Arguments:
	pAd					- WLAN control block pointer
	*pStreamReq			- the TSPEC pointer
	FlgIsActiveExcluded	-	1: do not delete active TSPEC
							0: delete both request and active TSPEC

Return Value:
	None

Note:
	1. Put the TSPEC into the failed list. Not free it to OS kernel.
	2. If pStreamReq belongs to a active TSPEC, we will delete the
		both of request TSPEC and active TSPEC simulatenously.
========================================================================
*/
STATIC VOID ACM_TC_Destroy(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd,
	ACM_PARAM_IN	ACM_STREAM			*pStreamReq,
	ACM_PARAM_IN	BOOLEAN				FlgIsActiveExcluded)
{
	ACM_TSPEC_REQ_LIST *pTspecFreedList;
	ACM_STREAM *pTspecFree;
	UINT32 Direction;
	UCHAR FlgIsActStm;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* sanity check whether the request exists in the failed list */
	FlgIsActStm = 0;
	pTspecFreedList = &ACMR_CB->TspecListFail;
	pTspecFree = pTspecFreedList->pHead;

	while(pTspecFree != NULL)
	{
		if (AMR_IS_SAME_POINTER(pTspecFree, pStreamReq))
		{
			ACMR_DEBUG(ACMR_DEBUG_TRACE,
						("acm_msg> already deleted! TC_Destroy()\n"));
			return; /* already exists (same pointer) */
		} /* End of if */

		pTspecFree = pTspecFree->pNext;
	} /* End of while */

	/*
		Remove the TSPEC from the requested list or the active table

		For Status == TSPEC_STATUS_ACT_DELETING, we should delete it in the
		requested list and the active table.
	*/
	if ((pStreamReq->Status == TSPEC_STATUS_REQUEST) ||
		(pStreamReq->Status == TSPEC_STATUS_REQ_DELETING) ||
		(pStreamReq->Status == TSPEC_STATUS_ACT_DELETING) ||
		(pStreamReq->Status == TSPEC_STATUS_RENEGOTIATING))
	{
		/* remove it from the requested linked list */
		ACM_TC_ReqRemove(pAd, pStreamReq);
	} /* End of if */

	if (FlgIsActiveExcluded == 0)
	{
		/* need also to delete active TSPEC */
		if ((pStreamReq->Status == TSPEC_STATUS_ACTIVE) ||
			(pStreamReq->Status == TSPEC_STATUS_ACTIVE_SUSPENSION) ||
			(pStreamReq->Status == TSPEC_STATUS_ACT_DELETING))
		{
			/* remove it from the active table linked list */
			FlgIsActStm = 1; /* this is a active stream */
			ACM_TC_ActRemove(pAd, pStreamReq);
		} /* End of if */
	} /* End of if */

	/* change current status to fail */
	pStreamReq->Status = TSPEC_STATUS_FAIL;

	/* insert it to the failed list */
	if (pTspecFreedList->TspecNum >= ACM_MAX_NUM_OF_FAIL_RSV_TSPEC)
	{
		/* free the first one (the oldest one) */
		pTspecFree = pTspecFreedList->pHead;

		if (pTspecFree == NULL)
		{
			/* fatal error: pHead == NULL but TspecNum > 0 */
			pTspecFreedList->TspecNum = 0;

			ACMR_DEBUG(ACMR_DEBUG_ERR,
						("acm_err> pTspecFree == NULL! TC_Destroy()\n"));
		}
		else
		{
			pTspecFreedList->pHead = pTspecFree->pNext;
			pTspecFree->pNext->pPrev = NULL;
			pTspecFreedList->TspecNum --;

			ACM_FREE_TS(pTspecFree);
		} /* End of if */
	} /* End of if */

	/* insert the new failed TSPEC to the last one */
	if (pTspecFreedList->pTail != NULL)
	{
		pTspecFreedList->pTail->pNext = pStreamReq;
		pStreamReq->pPrev = pTspecFreedList->pTail;
		pTspecFreedList->pTail = pStreamReq;
	}
	else
	{
		pTspecFreedList->pTail = pStreamReq;
		pStreamReq->pPrev = NULL;
	} /* End of if */

	if (pTspecFreedList->pHead == NULL)
		pTspecFreedList->pHead = pTspecFreedList->pTail;
	/* End of if */

	pStreamReq->pNext = NULL;
	pTspecFreedList->TspecNum ++;

	/* reclaim the used time of the stream */
	if (FlgIsActStm)
	{
		/* only for ACTIVE stream */
		ACM_EDCA_AllocatedTimeReturn(pAd, pStreamReq);

		/* statistics counter */
		Direction = pStreamReq->pTspec->TsInfo.Direction;
		ACM_LINK_NUM_DECREASE(pAd,
							pStreamReq->pTspec->TsInfo.AccessPolicy,
							Direction);

		/* delete peer device record if no any TSPEC exists for the peer */
		if (pStreamReq->pCdb != NULL)
			ACM_PeerDeviceMaintain(pAd, ACMR_CLIENT_MAC(pStreamReq->pCdb));
		/* End of if */

		/* recover the UAPSD state if the TSPEC is active TSPEC */
		if (pStreamReq->pCdb != NULL)
		{
			ACM_APSD_Ctrl(pAd,
						pStreamReq->pCdb,
						ACM_MR_EDCA_AC(pStreamReq->UP),
						Direction, 0, 0);
		} /* End of if */
	} /* End of if */

	/* count number of TSPEC */
	ACM_NUM_OF_TSPEC_RECOUNT(pAd, pStreamReq->pCdb);
} /* End of ACM_TC_Destroy */


/*
========================================================================
Routine Description:
	Free a stream and do NOT move the failed TSPEC to the fail list.

Arguments:
	pAd					- WLAN control block pointer
	*pStreamReq			- the TSPEC pointer

Return Value:
	None

Note:
	1. Free the TSPEC silently. Do NOT put it to the failed list.
========================================================================
*/
STATIC VOID ACM_TC_Discard(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd,
	ACM_PARAM_IN	ACM_STREAM			*pStreamReq)
{
	ACMR_STA_DB	*pCdb;
	UCHAR FlgIsActStm;
	UINT32 Direction;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* init */
	FlgIsActStm = 0;

	/*
		Remove the TSPEC from the requested list or the active table

		For Status == TSPEC_STATUS_ACT_DELETING, we should delete it in the
		requested list and the active table.
	*/
	if ((pStreamReq->Status == TSPEC_STATUS_REQUEST) ||
		(pStreamReq->Status == TSPEC_STATUS_REQ_DELETING) ||
		(pStreamReq->Status == TSPEC_STATUS_ACT_DELETING) ||
		(pStreamReq->Status == TSPEC_STATUS_RENEGOTIATING))
	{
		/* remove it from the requested list */
		ACM_TC_ReqRemove(pAd, pStreamReq);
	} /* End of if */

	if ((pStreamReq->Status == TSPEC_STATUS_ACTIVE) ||
		(pStreamReq->Status == TSPEC_STATUS_ACTIVE_SUSPENSION) ||
		(pStreamReq->Status == TSPEC_STATUS_ACT_DELETING))
	{
		/* also remove it from the active table */
		FlgIsActStm = 1;
		ACM_TC_ActRemove(pAd, pStreamReq);
	} /* End of if */

	/* reclaim the used time of the stream */
	Direction = pStreamReq->pTspec->TsInfo.Direction;

	if (FlgIsActStm == 1)
	{
		/* only for ACTIVE stream */
		ACM_EDCA_AllocatedTimeReturn(pAd, pStreamReq);

		/* statistics counter */
		ACM_LINK_NUM_DECREASE(pAd,
							pStreamReq->pTspec->TsInfo.AccessPolicy,
							Direction);

		/* delete peer device record if no any TSPEC for the peer */
		if (pStreamReq->pCdb != NULL)
			ACM_PeerDeviceMaintain(pAd, ACMR_CLIENT_MAC(pStreamReq->pCdb));
		/* End of if */
	} /* End of if */

#ifdef RELEASE_EXCLUDE
	/* recover APSD state */ /* bug fixed in Test Event 3 */
#endif /* RELEASE_EXCLUDE */
	if (pStreamReq->pCdb != NULL)
	{
		ACM_APSD_Ctrl(pAd, pStreamReq->pCdb,
					ACM_MR_EDCA_AC(pStreamReq->UP), Direction, 0, 0);
	} /* End of if */

	/* free the stream */
	pCdb = pStreamReq->pCdb;
	ACM_TC_Free(pAd, pStreamReq);

	ACMR_DEBUG(ACMR_DEBUG_TRACE,
				("acm_msg> Discard the TSPEC (out flag = %d) ok!\n",
                FlgIsActStm));

	/* count number of TSPEC */
	if (pCdb != NULL)
		ACM_NUM_OF_TSPEC_RECOUNT(pAd, pCdb);
	/* End of if */
} /* End of ACM_TC_Discard */


/*
========================================================================
Routine Description:
	Duplicate a stream.

Arguments:
	pAd					- WLAN control block pointer
	*pStream			- the source stream

Return Value:
	the duplicate stream

Note:
========================================================================
*/
STATIC ACM_STREAM *ACM_TC_Duplicate(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd,
	ACM_PARAM_IN	ACM_STREAM			*pStream)
{
	ACM_STREAM *pTspecDup;
#ifdef ACM_CC_FUNC_TCLAS
	UINT32 IdTclasNum;
#endif /* ACM_CC_FUNC_TCLAS */


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* allocate & copy the stream */
	ACMR_MEM_ALLOC(pTspecDup, sizeof(ACM_STREAM), (ACM_STREAM *));

	if (pTspecDup == NULL)
		return NULL;
	/* End of if */

	ACMR_MEM_COPY(pTspecDup, pStream, sizeof(ACM_STREAM));

	pTspecDup->pPrev = pTspecDup->pNext = NULL;

	/* allocate & copy TSPEC */
	ACMR_MEM_ALLOC(pTspecDup->pTspec, sizeof(ACM_TSPEC), (ACM_TSPEC *));

	if (pTspecDup->pTspec == NULL)
	{
		ACMR_MEM_FREE(pTspecDup);
		return NULL;
	} /* End of if */

	ACM_TSPEC_COPY(pTspecDup->pTspec, pStream->pTspec);

#ifdef ACM_CC_FUNC_TCLAS
	/* allocate & copy TCLAS */
	for(IdTclasNum=0; IdTclasNum<ACM_TSPEC_TCLAS_MAX_NUM; IdTclasNum++)
		pTspecDup->pTclas[IdTclasNum] = NULL;
	/* End of for */

	for(IdTclasNum=0; IdTclasNum<ACM_TSPEC_TCLAS_MAX_NUM; IdTclasNum++)
	{
		if (pStream->pTclas[IdTclasNum] != NULL)
		{
			ACMR_MEM_ALLOC(pTspecDup->pTclas[IdTclasNum],
							sizeof(ACM_TCLAS), (ACM_TCLAS *));

			if (pTspecDup->pTclas[IdTclasNum] == NULL)
				goto label_dup_err;
			/* End of if */

			ACM_TCLAS_COPY(pTspecDup->pTclas[IdTclasNum],
							pStream->pTclas[IdTclasNum]);
		} /* End of if */
	} /* End of for */
#endif /* ACM_CC_FUNC_TCLAS */

	return pTspecDup;

#ifdef ACM_CC_FUNC_TCLAS
label_dup_err:
	ACM_FREE_TS(pTspecDup);
	return NULL;
#endif /* ACM_CC_FUNC_TCLAS */
} /* End of ACM_TC_Duplicate */


/*
========================================================================
Routine Description:
	Find a stream by TS Info.

Arguments:
	pAd					- WLAN control block pointer
	*pDevMac			- the QSTA
	*pTsInfo			- the TS Info

Return Value:
	the stream

Note:
	1. the search sequence must be REQ --> CDB
	2. we need STATION MAC information to compare because TS INFO can
		be the same for two different QSTAs.
	3. we only need pTsInfo->TSID to find a TS, other fields can be 0.
		So you can NOT use the function to check if same TS exists.
========================================================================
*/
STATIC ACM_STREAM *ACM_TC_Find(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd,
	ACM_PARAM_IN	UCHAR				*pDevMac,
	ACM_PARAM_IN	ACM_TS_INFO			*pTsInfo)
{
	ACM_STREAM *pStream;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* sanity check */
	if (pDevMac == NULL)
		return NULL;
	/* End of if */

	/* search the TSPEC in the requested list */
	pStream = ACM_TC_FindInReq(pAd, pDevMac, pTsInfo);
	if (pStream != NULL)
		return pStream;
	/* End of if */

	/* search the TSPEC in the peer device database (input and output TSPEC) */
	pStream = ACM_TC_FindInPeer(pAd, pDevMac, pTsInfo);
	if (pStream != NULL)
		return pStream;
	/* End of if */

	return NULL;
} /* End of ACM_TC_Find */


/*
========================================================================
Routine Description:
	Find a stream in the peer record by TS Info.

Arguments:
	pAd					- WLAN control block pointer
	*pDevMac			- the QSTA
	*pTsInfo			- the TS Info

Return Value:
	the stream

Note:
========================================================================
*/
STATIC ACM_STREAM *ACM_TC_FindInPeer(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd,
	ACM_PARAM_IN	UCHAR				*pDevMac,
	ACM_PARAM_IN	ACM_TS_INFO			*pTsInfo)
{
	ACM_STREAM *pStream;
	ACM_STREAM **ppAcmStmList;
	UCHAR DirectionId[2] = \
					{ ACM_PEER_TSPEC_OUTPUT_GET, ACM_PEER_TSPEC_INPUT_GET };
	UINT32 IdTidNum, IdLinkNum;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* sanity check */
	if (pDevMac == NULL)
		return NULL;
	/* End of if */

	/* try to find it */
	for(IdLinkNum=0; IdLinkNum<2; IdLinkNum++)
	{
		ppAcmStmList = (ACM_STREAM **)ACM_StationTspecListGet(
										pAd, pDevMac, DirectionId[IdLinkNum]);
		if (ppAcmStmList == NULL)
			continue;
		/* End of if */

		for(IdTidNum=0; IdTidNum<ACM_STA_TID_MAX_NUM; IdTidNum++)
		{
			pStream = ppAcmStmList[IdTidNum];

			while(pStream != NULL)
			{
				if (ACM_IS_SAME_TS_INFOP(&pStream->pTspec->TsInfo, pTsInfo))
				{
					/* same TS INFO so we find it */
					return pStream;
				} /* End of if */

				/* only one TS for a direction/AC so the step can be skipped */
				/* pStream->pNext should be always NULL */
				pStream = pStream->pNext;
			} /* End of while */
		} /* End of for */
	} /* End of for */

	return NULL;
} /* End of ACM_TC_FindInPeer */


/*
========================================================================
Routine Description:
	Find a stream in the requested list by TS Info.

Arguments:
	pAd					- WLAN control block pointer
	*pDevMac			- the QSTA
	*pTsInfo			- the TS Info

Return Value:
	the stream

Note:
========================================================================
*/
STATIC ACM_STREAM *ACM_TC_FindInReq(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd,
	ACM_PARAM_IN	UCHAR				*pDevMac,
	ACM_PARAM_IN	ACM_TS_INFO			*pTsInfo)
{
	ACM_STREAM *pStream;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	pStream = ACMR_CB->TspecListReq.pHead;

	while(pStream != NULL)
	{
		if ((AMR_IS_SAME_MAC(ACMR_CLIENT_MAC(pStream->pCdb), pDevMac)) &&
			(ACM_IS_SAME_TS_INFOP(&pStream->pTspec->TsInfo, pTsInfo)))
		{
			/* same peer and TS INFO so we find it */
			return pStream;
		} /* End of if */

		pStream = pStream->pNext;
	} /* End of while */

	return NULL;
} /* End of ACM_TC_FindInReq */


/*
========================================================================
Routine Description:
	Free a TSPEC.

Arguments:
	pAd					- WLAN control block pointer
	*pStream			- the stream

Return Value:
	ACM_RTN_OK			- free ok
	ACM_RTN_FAIL		- *pStream = NULL

Note:
========================================================================
*/
STATIC ACM_FUNC_STATUS ACM_TC_Free(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd,
	ACM_PARAM_IN	ACM_STREAM			*pStream)
{
	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* sanity check */
	if (pStream == NULL)
		return ACM_RTN_FAIL;
	/* End of if */

	/* free the TSPEC */
	ACM_FREE_TS(pStream);
	return ACM_RTN_OK;
} /* End of ACM_TC_Free */


/*
========================================================================
Routine Description:
	Get the user priority.

Arguments:
	*pTsInfo			- the TS Info element
	TclasNum			- the number of TCLASS, max 5
	*pTclas				- the requested TCLASS array pointer

Return Value:
	None

Note:
========================================================================
*/
STATIC UCHAR ACM_TC_UP_Get(
	ACM_PARAM_IN	ACM_TS_INFO				*pTsInfo,
	ACM_PARAM_IN	UINT32					TclasNum,
	ACM_PARAM_IN	ACM_TCLAS				*pTclas)
{
#ifdef ACM_CC_FUNC_TCLAS
	if (TclasNum > 0)
		return pTclas->UserPriority;
	/* End of if */
#endif /* ACM_CC_FUNC_TCLAS */

	return pTsInfo->UP;
} /* End of ACM_TC_UP_Get */


/*
========================================================================
Routine Description:
	Rearrange a requested TSPEC in the request list.

Arguments:
	pAd					- WLAN control block pointer
	*pReqNew			- the requested TSPEC pointer
	Retry				- the retry count, base 0

Return Value:
	None

Note:
========================================================================
*/
STATIC VOID ACM_TC_Rearrange(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd,
	ACM_PARAM_IN	ACM_STREAM			*pReqNew,
	ACM_PARAM_IN	UINT16				Retry)
{
	ACM_STREAM *pStmPrev, *pStmNext;
	UINT32 TimeoutNew;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* reset the timeout & retry count */
	switch(pReqNew->Status)
	{
		case TSPEC_STATUS_REQUEST:
			/* the TSPEC is waiting for ADDTS response */
			TimeoutNew = pReqNew->TimeoutAddts;
			break;

		case TSPEC_STATUS_REQ_DELETING:
		case TSPEC_STATUS_ACT_DELETING:
			/* the TSPEC is waiting for DELTS response */
			TimeoutNew = pReqNew->TimeoutDelts;
			break;

		default:
			return; /* error status */
	} /* End of switch */

	pReqNew->Retry = Retry;


	/* check the number of requested TSPEC */
	if (ACMR_CB->TspecListReq.TspecNum <= 1)
	{
		pReqNew->Timeout = TimeoutNew;
		return; /* only a requested TSPEC, do NOT need to rearrange */
	} /* End of if */


	/* remove the requested TSPEC from the requested list */
	if (pReqNew->pPrev == NULL)
	{
		/* the TSPEC is the first one */
		pStmNext = pReqNew->pNext;

		if (pStmNext == NULL)
		{
			/* fatal error: only one requested TSPEC but TspecNum != 1 */
			/* prev = next = NULL */

			ACMR_CB->TspecListReq.TspecNum = 1; /* fix the Tspec number */
			pReqNew->Timeout = TimeoutNew;

			ACMR_DEBUG(ACMR_DEBUG_ERR,
						("acm_err> pNext == NULL! TC_Rearrange()\n"));
			return;
		} /* End of if */

		/* adjust the timeout for next request */
		pStmNext->Timeout += pReqNew->Timeout;
		pStmNext->pPrev = NULL;
		ACMR_CB->TspecListReq.pHead = pStmNext;
		ACMR_CB->TspecListReq.TspecNum --;
	}
	else
	{
		/* the TSPEC is not the first one */
		pStmPrev = pReqNew->pPrev;
		pStmNext = pReqNew->pNext;

		if (pStmNext == NULL)
		{
			/* the TSPEC is the last one so dont need to adjust timeout */
			pStmPrev->pNext = NULL;
			ACMR_CB->TspecListReq.pTail = pStmPrev;
			ACMR_CB->TspecListReq.TspecNum --;
		}
		else
		{
			/* the TSPEC is not the first one or the last one */
			pStmPrev->pNext = pStmNext;
			pStmNext->pPrev = pStmPrev;
			pStmNext->Timeout += pReqNew->Timeout;
			ACMR_CB->TspecListReq.TspecNum --;
		} /* End of if */
	} /* End of if */


	/* re-insert the requested TSPEC to the list */
	pReqNew->Timeout = TimeoutNew;
	ACM_TC_ReqInsert(pAd, pReqNew);
} /* End of ACM_TC_Rearrange */


/*
========================================================================
Routine Description:
	Check whether the requested TSPEC is a renegotiation TSPEC.

Arguments:
	pAd						- WLAN control block pointer
	*pDevMac				- the client MAC address
	UP						- the user priority of new stream
	*pTsInfo				- the TS Info element
	**ppStreamIn			- the old in TSPEC of same AC, can be NULL
	**ppStreamOut			- the old out TSPEC of same AC, can be NULL
	**ppStreamDifAc			- the old TSPEC of different AC, can be NULL

Return Value:
	ACM_RTN_OK 				- renegotiation TSPEC in active table or database
	ACM_RTN_RENO_IN_REQ_LIST- renegotiation TSPEC in the req list
	ACM_RTN_FAIL			- new TSPEC
	ACM_RTN_FATAL_ERR		- unexpected error occurred

Note:
	Maximum 2 TS can be existed in a AC, such as a uplink TS and a dnlink TS.

	04112008:
	Spec. metions that "The admission of any TS with the same TID as an
	existing TS deletes the existing TS and replaces it with the new TS,
	even if the TSs are in different ACs"

	So a new TSPEC can replace uplink TSPEC (same AC), dnlink TSPEC (same AC),
	bilink (same AC), uplink/dnlink/bilink (different AC, same TID)

	For example:
	(exist) 1) VI, TID3, bi
			2) VO, TID6, up
			3) VO, TID7, dn
	(new)	VO, TID3, bi

	==> delete all exist ones and use new one (VO, TID3, bi) in VO queue.
========================================================================
*/
STATIC ACM_FUNC_STATUS ACM_TC_RenegotiationCheck(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd,
	ACM_PARAM_IN	UCHAR				*pDevMac,
	ACM_PARAM_IN	UCHAR				UP,
	ACM_PARAM_IN	ACM_TS_INFO			*pTsInfo,
	ACM_PARAM_OUT	ACM_STREAM			**ppStreamIn,
	ACM_PARAM_OUT	ACM_STREAM			**ppStreamOut,
	ACM_PARAM_OUT	ACM_STREAM			**ppStreamDifAc)
{
/* two TSPECs are the same when their TS Info are the same */
#define LMR_MEMCMP_RENE_TC(other_ts_info) \
	ACM_IS_SAME_TS_INFOP(&other_ts_info, pTsInfo)

	ACMR_STA_DB *pCdb;
	ACM_STREAM *pStreamReq;
	ACM_ENTRY_INFO *pStaAcmInfo;
	BOOLEAN FlgIsFindSameTspec;
	UINT32 IdTidNum;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* init */
	FlgIsFindSameTspec = FALSE;

	if (ppStreamIn != NULL)
		*ppStreamIn = NULL;
	/* End of if */
	if (ppStreamOut != NULL)
		*ppStreamOut = NULL;
	/* End of if */
	if (ppStreamDifAc != NULL)
		*ppStreamDifAc = NULL;
	/* End of if */

	pCdb = ACMR_STA_ENTRY_GET(pAd, pDevMac);
	if (pCdb == NULL)
		return ACM_RTN_FATAL_ERR;
	/* End of if */

	/* check wether no any requested TSPEC exists */
	if (ACMR_CB->TspecListReq.TspecNum <= 0)
	{
		/* no TSPEC requested list in QAP mode */
		goto label_active_check;
	} /* End of if */

	/* find the TSPEC in requested list */
	pStreamReq = ACMR_CB->TspecListReq.pHead;

	while(pStreamReq != NULL)
	{
		if (pStreamReq->pTspec == NULL)
		{
			/* fatal error: TSPEC pointer is NULL, delete it */
			ACMR_DEBUG(ACMR_DEBUG_ERR,
					("acm_err> TSPEC = NULL! TC_RenegotiationCheck()\n"));
		}
		else
		{
			/* check whether TS Info element is same */
			if ((AMR_IS_SAME_MAC(ACMR_CLIENT_MAC(pStreamReq->pCdb), pDevMac)) &&
				(LMR_MEMCMP_RENE_TC(pStreamReq->pTspec->TsInfo)))
			{
				/* find the original TSPEC in the request list */
				return ACM_RTN_RENO_IN_REQ_LIST;
			} /* End of if */
		} /* End of if */

		/* check next outstanding requested TSPEC */
		pStreamReq = pStreamReq->pNext;
	} /* End of while */

label_active_check:
	/* check all existed output and input streams of the peer */
	pStaAcmInfo = ACMR_STA_ACM_PARAM_INFO(pCdb);

	/* find the TSPEC with same TID but different AC */
	for(IdTidNum=0; IdTidNum<ACM_STA_TID_MAX_NUM; IdTidNum++)
	{
		if (pStaAcmInfo->pAcStmOut[IdTidNum] != NULL)
		{
			pStreamReq = (ACM_STREAM *)pStaAcmInfo->pAcStmOut[IdTidNum];

			/* same TID but different same AC ID */
			if ((pStreamReq->pTspec->TsInfo.TSID == pTsInfo->TSID) &&
				(ACM_MR_EDCA_AC(pStreamReq->UP) != ACM_MR_EDCA_AC(UP)))
			{
				ACMR_DEBUG(ACMR_DEBUG_TRACE,
							("acm_msg> Same OUT TS! Exist TSID = %d "
							"dif UP = %d DIR = %d! "
							"New TSID = %d UP = %d DIR = %d! "
							"ACM_TC_RenegotiationCheck()\n",
							pStreamReq->pTspec->TsInfo.TSID,
							pStreamReq->UP,
							pStreamReq->pTspec->TsInfo.Direction,
							pTsInfo->TSID,
							UP,
							pTsInfo->Direction));

				FlgIsFindSameTspec = TRUE;

				if (ppStreamDifAc != NULL)
					*ppStreamDifAc = pStreamReq;
				/* End of if */

				/* only one possible stream with same TID and different AC */
				break;
			} /* End of if */
		} /* End of if */

		if (pStaAcmInfo->pAcStmIn[IdTidNum] != NULL)
		{
			pStreamReq = (ACM_STREAM *)pStaAcmInfo->pAcStmIn[IdTidNum];

			/* same TID but different same AC ID */
			if ((pStreamReq->pTspec->TsInfo.TSID == pTsInfo->TSID) &&
				(ACM_MR_EDCA_AC(pStreamReq->UP) != ACM_MR_EDCA_AC(UP)))
			{
				ACMR_DEBUG(ACMR_DEBUG_TRACE,
							("acm_msg> Same IN TS! Exist TSID = %d "
							"dif UP = %d DIR = %d! "
							"New TSID = %d UP = %d DIR = %d! "
							"ACM_TC_RenegotiationCheck()\n",
							pStreamReq->pTspec->TsInfo.TSID,
							pStreamReq->UP,
							pStreamReq->pTspec->TsInfo.Direction,
							pTsInfo->TSID,
							UP,
							pTsInfo->Direction));

				FlgIsFindSameTspec = TRUE;

				if (ppStreamDifAc != NULL)
					*ppStreamDifAc = pStreamReq;
				/* End of if */

				/* only one possible stream with same TID and different AC */
				break;
			} /* End of if */
		} /* End of if */
	} /* End of for */

	/* find the TSPEC with same TID or same AC */
	for(IdTidNum=0; IdTidNum<ACM_STA_TID_MAX_NUM; IdTidNum++)
	{
		if (pStaAcmInfo->pAcStmOut[IdTidNum] != NULL)
		{
			pStreamReq = (ACM_STREAM *)pStaAcmInfo->pAcStmOut[IdTidNum];

			/* skip ppStreamDifAc */
			if (ppStreamDifAc != NULL)
			{
				if (AMR_IS_SAME_POINTER(pStreamReq, (*ppStreamDifAc)))
					continue;
				/* End of if */
			} /* End of if */

			/* same TID or same AC ID */
			if (ACM_IS_SAME_TS(pStreamReq->pTspec->TsInfo.TSID,
								pTsInfo->TSID,
								pStreamReq->UP,
								UP,
								pStreamReq->pTspec->TsInfo.Direction,
								pTsInfo->Direction))
			{
				ACMR_DEBUG(ACMR_DEBUG_TRACE,
							("acm_msg> Same OUT TS! Exist TSID = %d "
							"UP = %d DIR = %d! "
							"New TSID = %d UP = %d DIR = %d! "
							"ACM_TC_RenegotiationCheck()\n",
							pStreamReq->pTspec->TsInfo.TSID,
							pStreamReq->UP,
							pStreamReq->pTspec->TsInfo.Direction,
							pTsInfo->TSID,
							UP,
							pTsInfo->Direction));

				FlgIsFindSameTspec = TRUE;

				if (ppStreamOut != NULL)
					*ppStreamOut = pStreamReq;
				/* End of if */

				if (pStreamReq->pTspec->TsInfo.Direction == \
													ACM_DIRECTION_BIDIREC_LINK)
				{
					/* for bi-directional link, check once is enough */
					return ACM_RTN_OK;
				} /* End of if */

				/* not return, try to find another input stream maybe */
			} /* End of if */
		} /* End of if */

		if (pStaAcmInfo->pAcStmIn[IdTidNum] != NULL)
		{
			pStreamReq = (ACM_STREAM *)pStaAcmInfo->pAcStmIn[IdTidNum];

			/* skip ppStreamDifAc */
			if (ppStreamDifAc != NULL)
			{
				if (AMR_IS_SAME_POINTER(pStreamReq, (*ppStreamDifAc)))
					continue;
				/* End of if */
			} /* End of if */

			/* same TID or same AC ID */
			if (ACM_IS_SAME_TS(pStreamReq->pTspec->TsInfo.TSID,
								pTsInfo->TSID,
								pStreamReq->UP,
								UP,
								pStreamReq->pTspec->TsInfo.Direction,
								pTsInfo->Direction))
			{
				ACMR_DEBUG(ACMR_DEBUG_TRACE,
							("acm_msg> Same IN TS! Exist TSID = %d "
							"UP = %d DIR = %d! "
							"New TSID = %d UP = %d DIR = %d! "
							"ACM_TC_RenegotiationCheck()\n",
							pStreamReq->pTspec->TsInfo.TSID,
							pStreamReq->UP,
							pStreamReq->pTspec->TsInfo.Direction,
							pTsInfo->TSID,
							UP,
							pTsInfo->Direction));

				FlgIsFindSameTspec = TRUE;

				if (ppStreamIn != NULL)
					*ppStreamIn = pStreamReq;
				/* End of if */

				if (pStreamReq->pTspec->TsInfo.Direction == \
													ACM_DIRECTION_BIDIREC_LINK)
				{
					/* for bi-directional link, check once is enough */
					return ACM_RTN_OK;
				} /* End of if */

				/* not return, try to find another output stream maybe */
			} /* End of if */
		} /* End of if */
	} /* End of for */

	if (FlgIsFindSameTspec == TRUE)
		return ACM_RTN_OK; /* find one */
	/* End of if */

	return ACM_RTN_FAIL;
} /* End of ACM_TC_RenegotiationCheck */


#ifdef ACM_CC_FUNC_REPLACE_RULE_TG
/*
========================================================================
Routine Description:
	Check whether the replacement TSPEC can be accepted.

Arguments:
	pAd						- WLAN control block pointer
	*pDevMac				- the client MAC address
	UP						- the user priority of new stream
	*pTsInfo				- the TS Info element

Return Value:
	ACM_RTN_OK 				- accept
	ACM_RTN_FAIL			- reject
	ACM_RTN_FATAL_ERR		- unexpected error occurred

Note:
	We can not accept a replacement TSPEC from QSTA if
	1. same TID, but not same AC; or
	2. same TID, same AC, but not same Direction;
========================================================================
*/
STATIC ACM_FUNC_STATUS ACM_TC_ReplacementCheck(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd,
	ACM_PARAM_IN	UCHAR				*pDevMac,
	ACM_PARAM_IN	UCHAR				UP,
	ACM_PARAM_IN	ACM_TS_INFO			*pTsInfo)
{
	ACMR_STA_DB *pCdb;
	ACM_STREAM *pStreamReq;
	ACM_TS_INFO *pTsInfoOld;
	ACM_ENTRY_INFO *pStaAcmInfo;
	UINT32 IdTidNum, IdDirNum;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* init */
	pCdb = ACMR_STA_ENTRY_GET(pAd, pDevMac);
	if (pCdb == NULL)
		return ACM_RTN_FATAL_ERR;
	/* End of if */

	pStaAcmInfo = ACMR_STA_ACM_PARAM_INFO(pCdb);

	/* check for all TSPECs in input and output TSPECs */
	for(IdDirNum=0; IdDirNum<2; IdDirNum++)
	{
		for(IdTidNum=0; IdTidNum<ACM_STA_TID_MAX_NUM; IdTidNum++)
		{
			if (IdDirNum == 0)
				pStreamReq = (ACM_STREAM *)pStaAcmInfo->pAcStmOut[IdTidNum];
			else
				pStreamReq = (ACM_STREAM *)pStaAcmInfo->pAcStmIn[IdTidNum];
			/* End of if */

			if (pStreamReq != NULL)
			{
				pTsInfoOld = &pStreamReq->pTspec->TsInfo;

				/* same TID but different same AC ID */
				if ((pTsInfoOld->TSID == pTsInfo->TSID) &&
					(ACM_MR_EDCA_AC(pStreamReq->UP) != ACM_MR_EDCA_AC(UP)))
				{
					/* match condition 1 */
					return ACM_RTN_FAIL;
				} /* End of if */

#if 0 /* rejected in WMM ACM discussion */
				/* same TID/AC but different Direction */
				if ((pTsInfoOld->Direction != ACM_DIRECTION_BIDIREC_LINK) &&
					(pTsInfo->Direction != ACM_DIRECTION_BIDIREC_LINK))
				{
					if ((pTsInfoOld->TSID == pTsInfo->TSID) &&
						(ACM_MR_EDCA_AC(pStreamReq->UP) == ACM_MR_EDCA_AC(UP)))
					{
						if (pTsInfoOld->Direction != pTsInfo->Direction)
						{
							/* match condition 2 */
							return ACM_RTN_FAIL;
						} /* End of if */
					} /* End of if */
				} /* End of if */
#endif
			} /* End of if */
		} /* End of for */
	} /* End of for */

	return ACM_RTN_OK;
} /* End of ACM_TC_ReplacementCheck */
#endif /* ACM_CC_FUNC_REPLACE_RULE_TG */


/*
========================================================================
Routine Description:
	Change ADDTS state to DELTS state.

Arguments:
	pAd					- WLAN control block pointer
	*pStreamReq			- the requested TSPEC pointer

Return Value:
	None

Note:
========================================================================
*/
STATIC VOID ACM_TC_Req_ADDTS2DELTS(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd,
	ACM_PARAM_IN	ACM_STREAM			*pStreamReq)
{
	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* change its state to deleting mode */
	pStreamReq->Status = TSPEC_STATUS_REQ_DELETING;
	pStreamReq->TimeoutAction = ACM_TC_TIMEOUT_ACTION_DELTS;
	pStreamReq->Timeout = pStreamReq->TimeoutDelts;
	pStreamReq->Retry = ACM_MAX_NUM_OF_DELTS_RETRY;

	/* rearrange the requested TSPEC in the requested list */
	ACM_TC_Rearrange(pAd, pStreamReq, ACM_MAX_NUM_OF_DELTS_RETRY);
} /* End of ACM_TC_Req_ADDTS2DELTS */


/*
========================================================================
Routine Description:
	Check if another link or same link exists in the requested list.

Arguments:
	pAd					- WLAN control block pointer
	*pStream			- the requested or actived stream

Return Value:
	ACM_RTN_EXIST		- same link exists
	ACM_RTN_NOT_EXIST	- no same link exists

Note:
	Same link means same TS info and same QSTA MAC.
========================================================================
*/
STATIC ACM_FUNC_STATUS ACM_TC_ReqCheck(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd,
	ACM_PARAM_IN	ACM_STREAM			*pStream)
{
	ACM_STREAM *pAcmStmList;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	pAcmStmList = ACMR_CB->TspecListReq.pHead;

	while(pAcmStmList != NULL)
	{
		/* check whether they are from same QSTA */
		if (AMR_IS_SAME_POINTER(pAcmStmList->pCdb, pStream->pCdb))
		{
			/* check whether TSPEC is the same */
			if (ACM_IS_SAME_TSPEC(pAcmStmList->pTspec, pStream->pTspec))
				return ACM_RTN_EXIST;
			/* End of if */
		} /* End of if */

		pAcmStmList = pAcmStmList->pNext;
	} /* End of while */

	return ACM_RTN_NOT_EXIST;
} /* End of ACM_TC_ReqCheck */


/*
========================================================================
Routine Description:
	Free all requested TSPEC.

Arguments:
	pAd				- WLAN control block pointer

Return Value:
	None

Note:
========================================================================
*/
STATIC VOID ACM_TC_ReqAllFree(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd)
{
	ACM_TSPEC_REQ_LIST *pStmReqList;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	pStmReqList = &ACMR_CB->TspecListReq;
	ACM_LIST_ALL_FREE(pAd, pStmReqList);
} /* End of ACM_TC_ReqAllFree */


/*
========================================================================
Routine Description:
	Free requested TSPEC for the peer device.

Arguments:
	pAd					- WLAN control block pointer
	*pCdb				- the destination device entry

Return Value:
	None

Note:
========================================================================
*/
STATIC VOID ACM_TC_ReqDeviceFree(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd,
 	ACM_PARAM_IN	ACMR_STA_DB			*pCdb)
{
	ACM_TSPEC_REQ_LIST *pStmReqList;
	ACM_STREAM *pStreamReq, *pStreamReqNext;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* init */
	pStmReqList = &ACMR_CB->TspecListReq;
	pStreamReq = pStmReqList->pHead;

	/* loop for all request TSPEC */
	while(pStreamReq != NULL)
	{
		pStreamReqNext = pStreamReq->pNext;

		/* check whether same device MAC */
		if (AMR_IS_SAME_CDB(pStreamReq->pCdb, pCdb))
		{
			/* remove the request from the request list */
			ACM_TC_ReqRemove(pAd, pStreamReq);

			/* free it */
			ACM_TC_Free(pAd, pStreamReq);
		} /* End of if */

		pStreamReq = pStreamReqNext;
	} /* End of while */
} /* End of ACM_TC_ReqDeviceFree */


/*
========================================================================
Routine Description:
	Insert a requested TSPEC to the request list.

Arguments:
	pAd					- WLAN control block pointer
	*pReqNew			- the requested TSPEC pointer

Return Value:
	ACM_RTN_OK			- insert ok
	ACM_RTN_FAIL		- insert fail

Note:
========================================================================
*/
STATIC ACM_FUNC_STATUS ACM_TC_ReqInsert(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd,
	ACM_PARAM_IN	ACM_STREAM			*pReqNew)
{
	ACM_TSPEC_REQ_LIST *pStmReqList;
	ACM_STREAM *pStmReq;
	ACM_STREAM *pStmSwapUse;
	UINT32 NumTimeout;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* init */
	pStmReqList = &ACMR_CB->TspecListReq;
	pReqNew->pPrev = NULL;
	pReqNew->pNext = NULL;
	NumTimeout = 0;

	/* insert */
	if (pStmReqList->pHead == NULL)
	{
		/* no any outgoing requested TSPEC exists */
		pStmReqList->pHead = pReqNew;
		pStmReqList->pTail = pReqNew;
		pStmReqList->TspecNum = 1;
	}
	else
	{
		/* check if the request exists */
		if (ACM_TC_ReqCheck(pAd, pReqNew) == ACM_RTN_EXIST)
		{
			ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> req already exist!\n"));
			return ACM_RTN_FAIL; /* the request already exists */
		} /* End of if */

		/* at least one outgoing requested TSPEC exists */
		pStmReq = pStmReqList->pHead;

		while(pStmReq != NULL)
		{
			/* calculate timeout sum */
			NumTimeout += pStmReq->Timeout;

			if (pReqNew->Timeout <= NumTimeout)
			{
				/* insert the new requested TSPEC */

				/* adjust request timeout */
				pReqNew->Timeout -= (NumTimeout - pStmReq->Timeout);
				pStmReq->Timeout -= pReqNew->Timeout;

				if (pStmReq->pPrev == NULL)
				{
					/* the old request is the first one */
					pStmReqList->pHead = pReqNew;
					pReqNew->pNext = pStmReq;
					pStmReq->pPrev = pReqNew;
				}
				else
				{
					/* the old request is not the first one */
					pStmSwapUse = pStmReq->pPrev;
					pStmSwapUse->pNext = pReqNew;
					pReqNew->pPrev = pStmSwapUse;
					pReqNew->pNext = pStmReq;
					pStmReq->pPrev = pReqNew;
				} /* End of if */

				/* a new requested TSPEC is added */
				pStmReqList->TspecNum ++;
				return ACM_RTN_OK;
			} /* End of if */

			/* move to next requested TSPEC */
			pStmReq = pStmReq->pNext;
		} /* End of while */

		/* insert it to the last one */
		pStmReq = pStmReqList->pTail;
		pStmReq->pNext = pReqNew;
		pReqNew->pPrev = pStmReq;
		pStmReqList->pTail = pReqNew;

		pReqNew->Timeout -= NumTimeout;
		pStmReqList->TspecNum ++;
	} /* End of if */

	return ACM_RTN_OK;
} /* End of ACM_TC_ReqInsert */


/*
========================================================================
Routine Description:
	Remove a requested TSPEC from the request list.

Arguments:
	pAd					- WLAN control block pointer
	*pStreamReq			- the requested TSPEC pointer

Return Value:
	None

Note:
========================================================================
*/
STATIC VOID ACM_TC_ReqRemove(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd,
	ACM_PARAM_IN	ACM_STREAM			*pStreamReq)
{
	ACM_TSPEC_REQ_LIST *pStmReqList;
	ACM_STREAM *pAcmStmList;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* init */
	pStmReqList = &ACMR_CB->TspecListReq;
	pAcmStmList = pStmReqList->pHead;

	/* check whether the request exists in the requested list */
	while(pAcmStmList != NULL)
	{
		if (AMR_IS_SAME_POINTER(pAcmStmList, pStreamReq))
			break; /* find it */
		/* End of if */

		pAcmStmList = pAcmStmList->pNext;
	} /* End of while */

	if (pAcmStmList == NULL)
	{
		/* fatal error, can NOT find the request */
		ACMR_DEBUG(ACMR_DEBUG_ERR,
					("acm_err> The stream is not in the requested list! "
					"TC_ReqRemove()\n"));
		return;
	} /* End of if */

	/* remove it */
	if (AMR_IS_SAME_POINTER(pStmReqList->pHead, pStreamReq))
	{
		/* the requested TSPEC is the first one */
		if (AMR_IS_SAME_POINTER(pStmReqList->pTail, pStreamReq))
		{
			/* the requested TSPEC is also the last one */
			pStmReqList->pHead = pStmReqList->pTail = NULL;
			pStmReqList->TspecNum = 0;
			return;
		} /* End of if */

		/* here, exist at least two requests */
		pStmReqList->pHead = pStreamReq->pNext;
		(pStreamReq->pNext)->pPrev = NULL;
		(pStreamReq->pNext)->Timeout += pStreamReq->Timeout;
		pStmReqList->TspecNum --;
		return;
	} /* End of if */

	if (AMR_IS_SAME_POINTER(pStmReqList->pTail, pStreamReq))
	{
		/* the requested TSPEC is the last one */
		pStmReqList->pTail = pStreamReq->pPrev;
		(pStreamReq->pPrev)->pNext = NULL;
		pStmReqList->TspecNum --;
		return;
	} /* End of if */

	/* the requested TSPEC is not either the first one or the last one */
	(pStreamReq->pPrev)->pNext = pStreamReq->pNext;
	(pStreamReq->pNext)->pPrev = pStreamReq->pPrev;
	(pStreamReq->pNext)->Timeout += pStreamReq->Timeout;
	pStmReqList->TspecNum --;
} /* End of ACM_TC_ReqRemove */


/*
========================================================================
Routine Description:
	Release all activated TSPECs without DELTS.

Arguments:
	pAd				- WLAN control block pointer

Return Value:
	None

Note:
	Used in module remove only.
========================================================================
*/
STATIC VOID ACM_TC_ReleaseAll(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd)
{
	ACM_TSPEC_REQ_LIST *pTspecFreedList;
	ACM_STREAM **ppAcmStmList;
	ACM_PEER_DEV_LIST *pAcmDevList;
	UCHAR DirectionId[2] = \
					{ ACM_PEER_TSPEC_OUTPUT_GET, ACM_PEER_TSPEC_INPUT_GET };
	UCHAR MAC[ACM_MAC_ADDR_LEN];
	UINT32 IdTidNum, IdLinkNum;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* sanity check */
	if (ACMR_ADAPTER_DB == NULL)
		return;
	/* End of if */

	/* clean all requested TSPEC if exists */
	ACM_TC_ReqAllFree(pAd);

	/* free all streams for all peer devices */
	pAcmDevList = NULL; /* get first device */

	while(1)
	{
		/* get next device */
		if (ACM_PeerDeviceGetNext(pAd, &pAcmDevList, MAC) != ACM_RTN_OK)
			break;
		/* End of if */

		/* delete all streams for device */
		for(IdLinkNum=0; IdLinkNum<2; IdLinkNum++)
		{
			ppAcmStmList = (ACM_STREAM **)ACM_StationTspecListGet(
													pAd, MAC, DirectionId[IdLinkNum]);
			if (ppAcmStmList == NULL)
				break;
			/* End of if */

			for(IdTidNum=0; IdTidNum<ACM_STA_TID_MAX_NUM; IdTidNum++)
			{
				if (ppAcmStmList[IdTidNum] != NULL)
				{
					if ((IdLinkNum != 0) &&
						(ppAcmStmList[IdTidNum]->pTspec->TsInfo.Direction == \
													ACM_DIRECTION_BIDIREC_LINK))
					{
						/*
							We only backup one stream for IdLinkNum = 0
							for bi-dir link so we can not free it twice.
						*/
					}
					else
						ACM_TC_Free(pAd, ppAcmStmList[IdTidNum]);
					/* End of if */

					/* empty the record */
					ppAcmStmList[IdTidNum] = NULL;
				} /* End of if */
			} /* End of for */
		} /* End of for */
	} /* End of while */

	/* free all backup peer device entries */
	ACM_PeerDeviceAllFree(pAd);

	/* free all failed streams in the failed list */
	pTspecFreedList = &ACMR_CB->TspecListFail;
	ACM_LIST_ALL_FREE(pAd, pTspecFreedList);
} /* End of ACM_TC_ReleaseAll */


/*
========================================================================
Routine Description:
	Activate periodically.

Arguments:
	Data

Return Value:
	None

Note:
========================================================================
*/
STATIC VOID ACM_TASK_General(
	ACM_PARAM_IN	ULONG		Data)
{
	ACMR_PWLAN_STRUC pAd = (ACMR_PWLAN_STRUC)Data;


#if defined(ACM_CC_FUNC_MBSS) || defined(ACM_CC_FUNC_CHAN_UTIL_MONITOR)
	BOOLEAN FlgIsAnyAcEnabled;
	ULONG SplFlags;


	FlgIsAnyAcEnabled = (ACMP_IsAnyACEnabled(pAd) == ACM_RTN_OK) ? TRUE : FALSE;

	/* get management semaphore */
	ACM_TSPEC_IRQ_LOCK_CHK(pAd, SplFlags, LabelSemErr);
#endif /* ACM_CC_FUNC_MBSS || ACM_CC_FUNC_CHAN_UTIL_MONITOR */

	pAd = pAd; /* avoid compile warning */

	WMM_ACM_FUNC_NAME_PRINT("IN");

#ifdef ACM_CC_FUNC_MBSS
	ACMR_CB->TimeoutMbssAcm ++;

	if (ACMR_CB->TimeoutMbssAcm >= ACM_MBSS_BW_ANNONCE_TIMEOUT_NUM)
	{
		ACM_TC_TASK_BwAnn(Data);

		ACMR_CB->TimeoutMbssAcm = 0;
	} /* End of if */
#endif /* ACM_CC_FUNC_MBSS */

#ifdef CONFIG_AP_SUPPORT
	if (ACMR_IS_AP_MODE(pAd))
	{
#ifdef ACM_CC_FUNC_CHAN_UTIL_MONITOR
		/* TODO: maybe we can check at least one TSPEC exist */
		if (FlgIsAnyAcEnabled == TRUE)
		{
			/* only do the function when at least one AC ACM is set */
			ACMR_CB->CU_MON_Timeout ++;

			if (ACMR_CB->CU_MON_Timeout >= ACM_CH_MON_TIMEOUT_NUM)
			{
				ACM_TC_TASK_CU_Mon(Data);

				ACMR_CB->CU_MON_Timeout = 0;
			} /* End of if */
		}
		else
		{
			/* reset all parameters */
			ACMR_CB->CU_MON_Timeout = 0;

			ACMR_CB->CU_MON_FlgLastMode = ACM_CU_MON_MODE_RECOVER;
			ACMR_CB->CU_MON_AdjustCount = 0;
			ACMR_CB->CU_MON_AdjustNum = 0;
			ACMR_CB->CU_MON_RecoverCount = 0;

			ACMR_AIFSN_DEFAULT_GET(pAd, ACMR_CB->CU_MON_AifsnAp, ACMR_CB->CU_MON_AifsnBss);

			ACMR_CB->CU_MON_FlgChangeNeed = 1;
		} /* End of if */
#endif /* ACM_CC_FUNC_CHAN_UTIL_MONITOR */
	} /* End of if */
#endif /* CONFIG_AP_SUPPORT */

#if defined(ACM_CC_FUNC_MBSS) || defined(ACM_CC_FUNC_CHAN_UTIL_MONITOR)
	ACMR_TIMER_ENABLE(ACMR_CB->FlgTimerGeneralEnable,
						ACMR_CB->TimerGeneral,
						ACM_TIMER_GENERAL_PERIOD_TIMEOUT);

	/* release semaphore */
	ACM_TSPEC_IRQ_UNLOCK(pAd, SplFlags, LabelSemErr);
	return;

LabelSemErr:
	ACMR_SEM_FAIL_PRINT();
	return;
#endif /* ACM_CC_FUNC_MBSS || ACM_CC_FUNC_CHAN_UTIL_MONITOR */
} /* End of ACM_TASK_General */


/*
========================================================================
Routine Description:
	Check if TSPEC requests are timeout.

Arguments:
	Data

Return Value:
	None

Note:
	For USB driver, we can not call ACM_DELTS_SEND() or ACM_ADDREQ_SEND()
	in spin lock range, or we will fail in RTUSB_VendorRequest().
	If you do spin lock first, in_interrupt() will be 1.
========================================================================
*/
STATIC VOID ACM_TASK_TC_ReqCheck(
	ACM_PARAM_IN	ULONG		Data)
{
#define TASK_LMR_STREAM_DESTROY(pAd, pStreamReq, FlgIsActiveExcluded)	\
	StmAcId = pStreamReq->AcmAcId;										\
	ACM_TC_Destroy(pAd, pStreamReq, FlgIsActiveExcluded);

	ACMR_PWLAN_STRUC pAd = (ACMR_PWLAN_STRUC)Data;
	ACM_TSPEC_REQ_LIST *pStmReqList;
	ACM_STREAM *pStreamReq, *pNext;
	ACMR_LIST StreamList;
	UCHAR StmAcId;
#ifdef CONFIG_STA_SUPPORT
	UCHAR *pFrameBuf;
	UINT32 FrameLen;
#endif /* CONFIG_STA_SUPPORT */
	ULONG SplFlags;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* init */
	ACMR_LIST_INIT(&StreamList);

	/* get management semaphore */
	ACM_TSPEC_IRQ_LOCK_CHK(pAd, SplFlags, LabelSemErr);

	/* check if no any requested TSPEC exists */
	pStmReqList = &ACMR_CB->TspecListReq;

	if (pStmReqList->TspecNum == 0)
		goto LabelDisableTimer;
	/* End of if */

	pStreamReq = pStmReqList->pHead;
	if (pStreamReq == NULL)
		goto LabelDisableTimer;
	/* End of if */

	/* sanity check for timeout of 1st requested TSPEC */
	if (pStreamReq->Timeout <= 0)
	{
		/* error: the timeout == 0 */
		ACMR_DEBUG(ACMR_DEBUG_ERR,
					("acm_err> timeout of 1st TSPEC == 0! "
					"TC_TASK_ReqCheck()\n"));

		/* reset timeout */
		pStreamReq->Timeout = 1;
	} /* End of if */

	/* subtract 1 from timeout */
	pStreamReq->Timeout --;

	if (pStreamReq->Timeout > 0)
	{
		/* no timeout occurred */
		goto LabelSemRelease;
	} /* End of if */

	/* handle TSPEC timeout */
	while(pStreamReq != NULL)
	{
		/*
			Backup next requested stream (must put here), because pStreamReq
			maybe moved to the last one in the list by ACM_TC_Rearrange().

			If pStreamReq is moved to the last one, the pStreamReq->pNext
			will be NULL.
		*/
		pNext = pStreamReq->pNext;

		/* init */
		StmAcId = 0;
#ifdef CONFIG_STA_SUPPORT
		pFrameBuf = NULL;
		FrameLen = 0;
#endif /* CONFIG_STA_SUPPORT */

		/* check retry count */
		if (pStreamReq->Retry > 0)
		{
			/* subtract 1 from retry count */
			pStreamReq->Retry --;

			/* execute timeout action for the TSPEC (retransmit) */
			switch(pStreamReq->TimeoutAction)
			{
				case ACM_TC_TIMEOUT_ACTION_DELTS:
					/* re-send a DELTS frame! */
					ACMR_DEBUG(ACMR_DEBUG_TRACE,
								("acm_msg> DELTS timeout! "
								"TC_TASK_ReqCheck()\n"));
					ACM_TC_Rearrange(pAd, pStreamReq, pStreamReq->Retry);
					ACMR_LIST_ALLOC_AND_INSERT_TO_TAIL(pAd, &StreamList, pStreamReq);
					break;

				case ACM_TC_TIMEOUT_ACTION_ADDTS_REQ:
#ifdef CONFIG_STA_SUPPORT
					if (ACMR_IS_STA_MODE(pAd))
					{
						/* send ADDTS Request frame to QAP */
						ACMR_DEBUG(ACMR_DEBUG_TRACE,
									("acm_msg> ADDTS timeout! "
									"TC_TASK_ReqCheck()\n"));

						/* make up ADDTS Request frame */
						ACM_TC_Rearrange(pAd, pStreamReq, pStreamReq->Retry);
						ACMR_LIST_ALLOC_AND_INSERT_TO_TAIL(pAd, &StreamList, pStreamReq);
					} /* End of if */
#endif /* CONFIG_STA_SUPPORT */

#ifdef CONFIG_AP_SUPPORT
					if (ACMR_IS_AP_MODE(pAd))
					{
						/* for QAP, the type is error type, so delete the request */
						TASK_LMR_STREAM_DESTROY(pAd, pStreamReq, 0);
					} /* End of if */
#endif /* CONFIG_AP_SUPPORT */
					break;

				default:
					/* error timeout action, delete it */
					ACMR_DEBUG(ACMR_DEBUG_TRACE,
								("acm_err> error timeout action! "
								"TASK_TC_Req_Check()\n"));
					TASK_LMR_STREAM_DESTROY(pAd, pStreamReq, 0);
					break;
			} /* End of switch */
		}
		else
		{
			/* reach retry limit count */
			switch(pStreamReq->TimeoutAction)
			{
				case ACM_TC_TIMEOUT_ACTION_DELTS:
				default:
					/* we do not yet take care about ACK frame of DELTS */

#ifdef CONFIG_STA_SUPPORT
					if (ACMR_IS_STA_MODE(pAd))
					{
						/* move to the fail list from the requested list */
						ACMR_DEBUG(ACMR_DEBUG_TRACE,
									("acm_msg> DELTS tx limit! "
									"Delete the request/active! "
									"TC_TASK_ReqCheck()\n"));

						/*
							In STA, AP will not sleep so AP will receive the
							DELTS frame.

							Delete both of request TSPEC and active TSPEC.
						*/
						TASK_LMR_STREAM_DESTROY(pAd, pStreamReq, 0);
					} /* End of if */
#endif /* CONFIG_STA_SUPPORT */

#ifdef CONFIG_AP_SUPPORT
					if (ACMR_IS_AP_MODE(pAd))
					{
						ACMR_DEBUG(ACMR_DEBUG_TRACE,
									("acm_msg> DELTS tx limit! "
									"Delete the request! "
									"TC_TASK_ReqCheck()\n"));

						/*
							We can not delete activated TSPEC after DELTS
							timeout in AP if PS STA does not use trigger frame
							or PS-Poll frame to get the DELTS frame.

							So we just only delete TSPEC request, not TSPEC
							active.

							EX: UAPSD of VO is enabled, all DELTS will be put
							in VO queue. DELTS will not sent from AP if STA does
							not send any trigger frame.
						*/
						TASK_LMR_STREAM_DESTROY(pAd, pStreamReq, 1);
					} /* End of if */
#endif /* CONFIG_AP_SUPPORT */
					break;

				case ACM_TC_TIMEOUT_ACTION_ADDTS_REQ:
					/* change to delts action in the requested list */
					ACMR_DEBUG(ACMR_DEBUG_TRACE,
								("acm_msg> ADDTS timeout! "
								"TC_TASK_ReqCheck()\n"));
					ACM_TC_Req_ADDTS2DELTS(pAd, pStreamReq);
					ACMR_LIST_ALLOC_AND_INSERT_TO_TAIL(pAd, &StreamList, pStreamReq);
					break;
			} /* End of switch */
		} /* End of if */

		/* check next requested TSPEC */
		pStreamReq = pNext;

		if ((pStreamReq != NULL) && (pStreamReq->Timeout > 0))
		{
			/* yet timeout for next request */
			break;
		} /* End of if */
	} /* End of while */

LabelSemRelease:
#ifdef ACMR_HANDLE_IN_TIMER
	/* schedule next time */
	ACMR_TIMER_ENABLE(ACMR_CB->FlgTspecReqCheckEnable,
						ACMR_CB->TimerTspecReqCheck,
						ACM_STREAM_CHECK_OFFSET);
#endif /* ACMR_HANDLE_IN_TIMER */

	/* release semaphore */
	ACM_TSPEC_IRQ_UNLOCK(pAd, SplFlags, LabelSemErr);

	/* send a action frame for each timeout stream */
	while(1)
	{
		pStreamReq = (ACM_STREAM *)ACMR_LIST_REMOVE_FRM_HEAD(&StreamList);
		if (pStreamReq == NULL)
			break; /* no any more */
		/* End of if */

		if (pStreamReq->Retry > 0)
		{
			switch(pStreamReq->TimeoutAction)
			{
				case ACM_TC_TIMEOUT_ACTION_DELTS:
					/* re-send a DELTS frame! */
					ACMR_DEBUG(ACMR_DEBUG_TRACE,
								("acm_msg> TX a DELTS frame! "
								"TC_TASK_ReqCheck()\n"));
					ACM_DELTS_SEND(pAd, pStreamReq->pCdb, pStreamReq, LabelSemErr);
					break;

				case ACM_TC_TIMEOUT_ACTION_ADDTS_REQ:
#ifdef CONFIG_STA_SUPPORT
					if (ACMR_IS_STA_MODE(pAd))
					{
						/* send ADDTS Request frame to QAP */
						ACMR_DEBUG(ACMR_DEBUG_TRACE,
									("acm_msg> TX a ADDTS! "
									"TC_TASK_ReqCheck()\n"));

						/* make up ADDTS Request frame */
						ACM_ADDREQ_MAKEUP(pAd, pStreamReq->pCdb, pFrameBuf,
										FrameLen, pStreamReq, LabelSemErr);

						/* send ADDTS Request frame again */
						if (FrameLen > 0)
						{
							ACM_ADDREQ_SEND(pAd, pFrameBuf, FrameLen);
						} /* End of if */
					} /* End of if */
#endif /* CONFIG_STA_SUPPORT */

					/* nothing for AP mode */
					break;
			} /* End of switch */
		}
		else
		{
			/* reach retry limit count */
			switch(pStreamReq->TimeoutAction)
			{
				case ACM_TC_TIMEOUT_ACTION_DELTS:
				default:
					/* nothing to do */
					break;

				case ACM_TC_TIMEOUT_ACTION_ADDTS_REQ:
					/* change to delts action in the requested list */
					ACMR_DEBUG(ACMR_DEBUG_TRACE,
								("acm_msg> TX a DELTS frame! "
								"TC_TASK_ReqCheck()\n"));
					ACM_DELTS_SEND(pAd, pStreamReq->pCdb, pStreamReq, LabelSemErr);
					break;
			} /* End of switch */
		} /* End of if */

		/* isolate the stream */
		pStreamReq->pPrev = NULL;
		pStreamReq->pNext = NULL;

		/* free it */
		ACM_TC_Free(pAd, pStreamReq);
	} /* End of while */

#ifdef CONFIG_STA_SUPPORT
	if (ACMR_IS_STA_MODE(pAd))
	{
		/* return power save right if possible */
		ACMP_StaPsCtrlRightReturn(pAd);
	} /* End of if */
#endif /* CONFIG_STA_SUPPORT */
	return;

LabelDisableTimer:
#ifndef ACMR_HANDLE_IN_TIMER
	/* give up schedule next time */
	ACMR_TIMER_DISABLE(ACMR_CB->FlgTspecReqCheckEnable,
						ACMR_CB->TimerTspecReqCheck);
#endif /* ACMR_HANDLE_IN_TIMER */

	ACM_TSPEC_IRQ_UNLOCK(pAd, SplFlags, LabelSemErr);

#ifdef CONFIG_STA_SUPPORT
	if (ACMR_IS_STA_MODE(pAd))
	{
		/* return power save right if possible */
		ACMP_StaPsCtrlRightReturn(pAd);
	} /* End of if */
#endif /* CONFIG_STA_SUPPORT */
	return;

LabelSemErr:
	ACMR_SEM_FAIL_PRINT();
	return;
} /* End of ACM_TASK_TC_ReqCheck */


/*
========================================================================
Routine Description:
	Get IP information from the frame.

Arguments:
	*pPkt				- the IP header
	*pTclas				- the IP information

Return Value:
	ACM_RTN_OK			- get ok
	ACM_RTN_FAIL		- get fail

Note:
========================================================================
*/
STATIC ACM_FUNC_STATUS ACM_TCLAS_IP_INFO_Get(
	ACM_PARAM_IN	UCHAR			*pPkt,
	ACM_PARAM_OUT	ACM_TCLAS		*pTclas)
{
	ACM_IPHDR *pIpHdr;
	UINT16 Type;
	UINT16 *pPortSrc;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	Type = *(UINT16 *)pPkt;

	if (Type != ACMR_HTONS(0x0800)) /* 0800: IP packet type */
		return ACM_RTN_FAIL;
	/* End of if */

	pIpHdr = (ACM_IPHDR *)(pPkt+2);

	pTclas->Clasifier.IPv4.Version = pIpHdr->Version;
	pTclas->Clasifier.IPv4.IpSource = pIpHdr->AddrSrc;
	pTclas->Clasifier.IPv4.IpDest = pIpHdr->AddrDst;
	pTclas->Clasifier.IPv4.DSCP = pIpHdr->TOS;
	pTclas->Clasifier.IPv4.Protocol = pIpHdr->Protocol;

	pPortSrc = (UINT16 *)((UCHAR *)pIpHdr + (pIpHdr->IHL<<2));
	pTclas->Clasifier.IPv4.PortSource = *pPortSrc;
	pTclas->Clasifier.IPv4.PortDest = *(pPortSrc+1);
	return ACM_RTN_OK;
} /* End of ACM_TCLAS_IP_INFO_Get */


/*
========================================================================
Routine Description:
	Get VLAN information from the frame.

Arguments:
	*pPkt				- the frame
	*pVlanTag			- the VLAN Tag of the frame

Return Value:
	ACM_RTN_OK			- get ok
	ACM_RTN_FAIL		- get fail

Note:
========================================================================
*/
STATIC ACM_FUNC_STATUS ACM_TCLAS_VLAN_INFO_Get(
	ACM_PARAM_IN	UCHAR			*pPkt,
	ACM_PARAM_OUT	UINT16			*pVlanTag)
{
	UINT16 Type;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	Type = *(UINT16 *)pPkt;

	if (Type != ACMR_HTONS(0x8100)) /* 8100: VLAN packet type */
		return ACM_RTN_FAIL;
	/* End of if */

	*pVlanTag = *(UINT16 *)(&pPkt[2]);
	return ACM_RTN_OK;
} /* End of ACM_TCLAS_VLAN_INFO_Get */


/*
========================================================================
Routine Description:
	Activate periodically.

Arguments:
	Data

Return Value:
	None

Note:
========================================================================
*/
VOID ACM_TR_TC_General(
	ACM_PARAM_IN	ULONG		Data)
{
#ifndef ACMR_HANDLE_IN_TIMER
	ACMR_PWLAN_STRUC pAd = (ACMR_PWLAN_STRUC)Data;
	ULONG SplFlags;


	ACM_TSPEC_IRQ_LOCK_CHK(pAd, SplFlags, LabelSemErr);

	ACMR_TASK_ACTIVATE(ACMR_CB->TaskletGeneral,
						ACMR_CB->TimerGeneral,
						ACM_TIMER_GENERAL_PERIOD_TIMEOUT);

	ACM_TSPEC_IRQ_UNLOCK(pAd, SplFlags, LabelSemErr);
	return;

LabelSemErr:
	ACMR_SEM_FAIL_PRINT();
	return;
#else

	ACM_TASK_General(Data);
#endif /* ACMR_HANDLE_IN_TIMER */
} /* End of ACM_TR_TC_General */


/*
========================================================================
Routine Description:
	Check whether TSPEC request is timeout.
	If timeout, move it to the failure list.

Arguments:
	Data

Return Value:
	None

Note:
	1. waked up every 100ms
	2. for example, the request list is
		timeout      = 0 --> 0 --> 4 --> 6 --> 2
		real timeout = 0ms, 0ms, 400ms, 1000ms, 1200ms
========================================================================
*/
VOID ACM_TR_TC_ReqCheck(
	ACM_PARAM_IN	ULONG		Data)
{
#ifndef ACMR_HANDLE_IN_TIMER
	ACMR_PWLAN_STRUC pAd = (ACMR_PWLAN_STRUC)Data;
	ULONG SplFlags;


	ACM_TSPEC_IRQ_LOCK_CHK(pAd, SplFlags, LabelSemErr);

	/* sanity check for enable flag */
	if (ACMR_CB->FlgTspecReqCheckEnable == 0)
	{
		ACMR_TIMER_DISABLE(ACMR_CB->FlgTspecReqCheckEnable,
							ACMR_CB->TimerTspecReqCheck);
		ACM_TSPEC_IRQ_UNLOCK(pAd, SplFlags, LabelSemErr);
		return;
	} /* End of if */

	/* inform TSPEC request check task */
	ACMR_TASK_ACTIVATE(ACMR_CB->TaskletTspecReqCheck,
						ACMR_CB->TimerTspecReqCheck,
						ACM_STREAM_CHECK_OFFSET);

	ACM_TSPEC_IRQ_UNLOCK(pAd, SplFlags, LabelSemErr);
	return;

LabelSemErr:
	ACMR_SEM_FAIL_PRINT();
	return;
#else

	ACM_TASK_TC_ReqCheck(Data);
#endif /* ACMR_HANDLE_IN_TIMER */
} /* End of ACM_TR_TC_ReqCheck */




/* ============================= Chan Util Monitor ====================== */
#ifdef CONFIG_AP_SUPPORT
#ifdef ACM_CC_FUNC_CHAN_UTIL_MONITOR
/*
========================================================================
Routine Description:
	Adjust AIFSN of non-ACM AC when channel utilization is too high.

Arguments:
	Data

Return Value:
	None

Note:
	To achieve the maximum throughput and short delay, CUmax should be
	set in the range of 0.9 to 0.95 (RTS/CTS).
	Where CUmax is maximum channel utilization.

	Fast adjust and slow recover.

	Protect in caller.
========================================================================
*/
STATIC VOID ACM_TC_TASK_CU_Mon(
	ACM_PARAM_IN	ULONG		Data)
{
	ACMR_PWLAN_STRUC pAd = (ACMR_PWLAN_STRUC)Data;
	ACM_CTRL_PARAM *pEdcaParam;
	UINT32 CUmax;
	UINT32 IdAcNum;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* init */
	pEdcaParam = &ACMR_CB->EdcaCtrlParam;
	CUmax = (255 * ACM_CH_MON_CUMAX)/100;

	/* handle new chan utilization */
	if (ACMR_CHAN_UTILIZATION_GET(pAd) >= CUmax)
	{
		ACMR_CB->CU_MON_AdjustCount ++;
		ACMR_CB->CU_MON_RecoverCount = 0;
	}
	else
	{
		ACMR_CB->CU_MON_AdjustCount = 0;
		ACMR_CB->CU_MON_RecoverCount ++;
	} /* End of if */

	/* check if we need to adjust AIFSN of non-ACM AC */
	if (ACMR_CB->CU_MON_AdjustCount >= ACM_CH_MON_ADJUST_NUM)
	{
		ACMR_CB->CU_MON_AdjustNum ++;

		if (ACMR_CB->CU_MON_AdjustNum < ACM_CH_MON_MAX_ADJUST)
		{
			ACMR_CB->CU_MON_FlgLastMode = ACM_CU_MON_MODE_ADJUST;

			for(IdAcNum=0; IdAcNum<ACM_DEV_NUM_OF_AC; IdAcNum++)
			{
				if (pEdcaParam->FlgAcmStatus[IdAcNum] == 0)
				{
					if (ACMR_CB->CU_MON_AifsnAp[IdAcNum] < 0x0e)
					{
						ACMR_CB->CU_MON_FlgChangeNeed = 1;
						ACMR_CB->CU_MON_AifsnAp[IdAcNum] += 2;
					} /* End of if */

					if (ACMR_CB->CU_MON_AifsnBss[IdAcNum] < 0x0e)
					{
						ACMR_CB->CU_MON_FlgChangeNeed = 1;
						ACMR_CB->CU_MON_AifsnBss[IdAcNum] += 2;
					} /* End of if */

					if (ACMR_CB->CU_MON_FlgChangeNeed)
					{
						ACMR_DEBUG(ACMR_DEBUG_TRACE,
								("acm_msg> chan busy, adjust AC%d...\n", IdAcNum));
					} /* End of if */
				} /* End of if */
			} /* End of for */
		} /* End of if */

		ACMR_CB->CU_MON_AdjustCount = 0; /* re-check */
	} /* End of if */

	/* check if we need to recover AIFSN of non-ACM AC */
	if (ACMR_CB->CU_MON_RecoverCount >= ACM_CH_MON_RECOVER_NUM)
	{
		UCHAR aifns_ap[ACM_DEV_NUM_OF_AC];
		UCHAR aifns_bss[ACM_DEV_NUM_OF_AC];


		ACMR_AIFSN_DEFAULT_GET(pAd, aifns_ap, aifns_bss);

		for(IdAcNum=0; IdAcNum<ACM_DEV_NUM_OF_AC; IdAcNum++)
		{
			if (pEdcaParam->FlgAcmStatus[IdAcNum] == 0)
			{
				if (ACMR_CB->CU_MON_AifsnAp[IdAcNum] > aifns_ap[IdAcNum])
				{
					ACMR_CB->CU_MON_FlgChangeNeed = 1;
					ACMR_CB->CU_MON_AifsnAp[IdAcNum] --;
				} /* End of if */

				if (ACMR_CB->CU_MON_AifsnBss[IdAcNum] > aifns_bss[IdAcNum])
				{
					ACMR_CB->CU_MON_FlgChangeNeed = 1;
					ACMR_CB->CU_MON_AifsnBss[IdAcNum] --;
				} /* End of if */

				if (ACMR_CB->CU_MON_FlgChangeNeed)
				{
					ACMR_DEBUG(ACMR_DEBUG_TRACE,
								("acm_msg> chan idle, recover AC%d...\n", IdAcNum));
				} /* End of if */
			} /* End of if */
		} /* End of for */

		ACMR_CB->CU_MON_AdjustNum = 0;
		ACMR_CB->CU_MON_RecoverCount = 0; /* re-check */
	} /* End of if */
} /* End of ACM_TC_TASK_CU_Mon */
#endif /* ACM_CC_FUNC_CHAN_UTIL_MONITOR */
#endif /* CONFIG_AP_SUPPORT */




/* ============================= MBSS function ========================== */
/*
========================================================================
Routine Description:
	Send a broadcast Bandwidth Annonce frame.

Arguments:
	pAd					- WLAN control block pointer
	FlgIsForceToSent	- 1: forece to send the frame

Return Value:
	None

Note:
	1. Carefully! This is a broadcast frame and must be non-encryption.
	2. The frame is only sent by AP. STA only forward it.
	3. AP A <--> AP B <--> AP C
		AP B only need to broadcast its used ACM time, no AP A used time.
		Because AP C does not see AP A.
========================================================================
*/
STATIC VOID ACM_FrameBwAnnSend(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	UCHAR					FlgIsForceToSent)
{
#ifdef ACM_CC_FUNC_MBSS
#ifdef CONFIG_AP_SUPPORT
	ACM_CTRL_PARAM		*pEdcaParam;
	ACM_BW_ANN_FRAME	*pFrameBwAnn;
	ACMR_WLAN_HEADER	HdrActionFrame;
	ULONG				FrameLen;
	UCHAR				*pBufFrame;
	UCHAR				MAC_BC[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
	UINT32				IdAcNum;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* init */
	FrameLen = 0;

	ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> Make up a BW ANN...\n"));

	/* allocate a frame buffer */
	if (ACMR_MGMT_FME_ALLOCATE(pAd, &pBufFrame) != NDIS_STATUS_SUCCESS)
		return;
	/* End of if */

	/* make the frame header */
	MgtMacHeaderInit(
					pAd, &HdrActionFrame, SUBTYPE_ACTION, 0,
					MAC_BC,
					pAd->ApCfg.MBSSID[BSS0].Bssid);

	ACMR_MEM_MAC_COPY(HdrActionFrame.Addr3, MAC_BC); /* BSSID = broadcast */

	MakeOutgoingFrame(
					pBufFrame, &FrameLen,
					sizeof(ACMR_WLAN_HEADER), &HdrActionFrame,
					END_OF_ARGS);

	/* make the frame body */
	pFrameBwAnn = (ACM_BW_ANN_FRAME *)&pBufFrame[FrameLen];
	memset(pFrameBwAnn, 0, sizeof(ACM_BW_ANN_FRAME));

	pFrameBwAnn->Category = ACM_CATEGORY_WME;
	pFrameBwAnn->Action = ACM_ACTION_WME_BW_ANN;

	/* sanity check */
	pEdcaParam = &ACMR_CB->EdcaCtrlParam;

	if (FlgIsForceToSent == FALSE)
	{
		if (ACMR_CB->AcmTotalTimeOld == pEdcaParam->AcmTotalTime)
			goto LabelOK; /* same acm time, no need to announce */
		/* End of if */
	} /* End of if */

	/* make the frame body */
	pFrameBwAnn->MBSS.Identifier = ACMR_CB->MbssIdentifier++;
	pFrameBwAnn->MBSS.Channel = ACMR_CHAN_GET(pAd);
	ACMR_MEM_MAC_COPY(pFrameBwAnn->MBSS.BSSID, ACMR_AP_ADDR_GET(pAd));

	for(IdAcNum=0; IdAcNum<ACM_DEV_NUM_OF_AC; IdAcNum++)
		pFrameBwAnn->MBSS.UsedTime[IdAcNum] = pEdcaParam->AcmAcTime[IdAcNum];
	/* End of for */

	/* backup total acm used time */
	ACMR_CB->AcmTotalTimeOld = pEdcaParam->AcmTotalTime;

	FrameLen += sizeof(ACM_BW_ANN_FRAME);

	/* send out the frame and free it */
	ACMR_MGMT_PKT_TX(pAd, pBufFrame, FrameLen);
	MlmeFreeMemory(pAd, pBufFrame);
	return;

LabelOK:
	MlmeFreeMemory(pAd, pBufFrame);
	return;
#endif /* CONFIG_AP_SUPPORT */
#endif /* ACM_CC_FUNC_MBSS */

	return;
} /* End of ACM_FrameBwAnnSend */


#ifdef ACM_CC_FUNC_MBSS
/*
========================================================================
Routine Description:
	Forward the bandwidth announce action frame.

Arguments:
	pAd					- WLAN control block pointer
	*pMblk				- the received frame
	PktLen				- the frame length

Return Value:
	None

Note:
	1. Only when ASSOC OK.
	2. If the source is from our AP, forward it to broadcast;
	3. If the source is not from our AP, forward it to our AP.
	4. TODO: Can not encrypt the frame.
========================================================================
*/
STATIC VOID ACM_MBSS_BwAnnForward(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	UCHAR					*pMblk,
	ACM_PARAM_IN	UINT32					PktLen)
{
	ACMR_WLAN_HEADER *pHeader;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	if (ACMR_IS_PORT_SECURE(pAd))
	{
		pHeader = (ACMR_WLAN_HEADER *)pMblk;

		if (ACMR_MAC_CMP(pHeader->Addr2, ACMR_AP_ADDR_GET(pAd)) != 0)
		{
			/* from other BSS so translating to unicast frame to our AP */
			ACMR_WLAN_PKT_RA_SET(pMblk, ACMR_AP_ADDR_GET(pAd));
			ACMR_WLAN_PKT_BSSID_SET(pMblk, ACMR_AP_ADDR_GET(pAd));
		} /* End of if */

		ACMR_WLAN_PKT_TA_SET(pMblk, ACMR_SELF_MAC_GET(pAd));
		ACMR_MGMT_PKT_TX(pAd, pMblk, PktLen);
	} /* End of if */
} /* End of ACM_MBSS_BwAnnForward */


/*
========================================================================
Routine Description:
	Handle the bandwidth announce action frame from other BSS.

Arguments:
	pAd				- WLAN control block pointer

Return Value:
	ACM_RTN_OK		- forward the frame
	ACM_RTN_FAIL	- do NOT forward the frame

Note:
========================================================================
*/
STATIC ACM_FUNC_STATUS ACM_MBSS_BwAnnHandle(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	UCHAR					*pActFrame,
	ACM_PARAM_IN	UINT32					PktLen)
{
	ACM_BW_ANN_FRAME *pFrameAnn;
	ACM_MBSS_BW *pMbssNew, *pMbss;
	UINT32 IdMbssNum;
	UCHAR FlgIsNewAnn;
	ULONG SplFlags;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* init */
	pFrameAnn = (ACM_BW_ANN_FRAME *)pActFrame;
	pMbssNew = &pFrameAnn->MBSS;
	FlgIsNewAnn = 0;

	/* get management semaphore */
	ACM_TSPEC_SEM_LOCK_CHK(pAd, SplFlags, LabelSemErr);

	pMbss = &ACMR_CB->MBSS[0];

	/* learn it to our database */
	for(IdMbssNum=0; IdMbssNum<ACM_MBSS_BK_NUM; IdMbssNum++)
	{
		/* TODO: Need check SSID ? */
		if ((pMbssNew->Channel == pMbss->Channel) &&
			(memcmp(pMbssNew->BSSID, pMbss->BSSID, 6) == 0))
		{
			/* this is from same channel and BSSID */
			if (pMbssNew->Identifier != pMbss->Identifier)
			{
				/* new announce */
				if ((pMbssNew->UsedTime[0] != pMbss->UsedTime[0]) ||
					(pMbssNew->UsedTime[1] != pMbss->UsedTime[1]) ||
					(pMbssNew->UsedTime[2] != pMbss->UsedTime[2]) ||
					(pMbssNew->UsedTime[3] != pMbss->UsedTime[3]))
				{
					/* we regard as a new one only when different used time */
					FlgIsNewAnn = 1;
				} /* End of if */
			} /* End of if */

			break; /* find it */
		} /* End of if */

		pMbss ++; /* check next one */
	} /* End of for */

	if (IdMbssNum == ACM_MBSS_BK_NUM)
	{
		/* not found, new announce, try to find an empty entry */
		ACMR_DEBUG(ACMR_DEBUG_TRACE,
					("acm_msg> [mbss] New Ann from %02x:%02x:%02x:%02x:%02x:%02x\n",
					pMbssNew->BSSID[0], pMbssNew->BSSID[1],
					pMbssNew->BSSID[2], pMbssNew->BSSID[3],
					pMbssNew->BSSID[4], pMbssNew->BSSID[5]));

		pMbss = &ACMR_CB->MBSS[0];

		for(IdMbssNum=0; IdMbssNum<ACM_MBSS_BK_NUM; IdMbssNum++)
		{
			if (pMbss->Channel == 0)
				break;
			/* End of if */
		} /* End of for */

		if (IdMbssNum == ACM_MBSS_BK_NUM)
			return ACM_RTN_OK; /* My god! Too many BSS in the same channel */
		/* End of if */

		FlgIsNewAnn = 1;
	} /* End of if */

	/* update new used time */
	ACMR_MEM_COPY(pMbss, pMbssNew, sizeof(ACM_MBSS_BW));
	pMbss->Timeout = 0;

	/* calculate total used time */
	ACM_MBSS_BwReCalculate(pAd);

	if (FlgIsNewAnn)
	{
		/* need to forward the announce frame */
		ACMR_DEBUG(ACMR_DEBUG_TRACE,
					("acm_msg> [mbss] Ann Time %d %d %d %d!\n",
					pMbss->UsedTime[0], pMbss->UsedTime[1],
					pMbss->UsedTime[2], pMbss->UsedTime[3]));

		/* release semaphore */
		ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);

		return ACM_RTN_OK;
	} /* End of if */

	/* release semaphore */
	ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);

	/* this is duplicated announce frame */
	return ACM_RTN_FAIL;

LabelSemErr:
	ACMR_SEM_FAIL_PRINT();
	return ACM_RTN_FAIL;
} /* End of ACM_MBSS_BwAnnHandle */


/*
========================================================================
Routine Description:
	Re-calculate the used time for other BSS.

Arguments:
	pAd				- WLAN control block pointer

Return Value:
	None

Note:
========================================================================
*/
STATIC VOID ACM_MBSS_BwReCalculate(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd)
{
	UINT32 IdMbssNum, IdAcNum;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	ACMR_CB->MbssTotalUsedTime = 0;

	for(IdAcNum=0; IdAcNum<ACM_DEV_NUM_OF_AC; IdAcNum++)
		ACMR_CB->MbssAcUsedTime[IdAcNum] = 0;
	/* End of for */

	for(IdMbssNum=0; IdMbssNum<ACM_MBSS_BK_NUM; IdMbssNum++)
	{
		if (memcmp(ACMR_CB->MBSS[IdMbssNum].BSSID, ACMR_SELF_MAC_GET(pAd), 6) != 0)
		{
			/* only for different BSS */
			if (ACMR_CB->MBSS[IdMbssNum].Channel != 0)
			{
				/* valid entry */
				for(IdAcNum=0; IdAcNum<ACM_DEV_NUM_OF_AC; IdAcNum++)
				{
					ACMR_CB->MbssTotalUsedTime += \
											ACMR_CB->MBSS[IdMbssNum].UsedTime[IdAcNum];

					ACMR_CB->MbssAcUsedTime[IdAcNum] += \
											ACMR_CB->MBSS[IdMbssNum].UsedTime[IdAcNum];
				} /* End of for */
			} /* End of if */
		} /* End of if */
	} /* End of for */
} /* End of ACM_MBSS_BwReCalculate */


/*
========================================================================
Routine Description:
	Broadcast our used bandwidth periodically.

Arguments:
	Data

Return Value:
	None

Note:
	We need to inform other BSS our used ACM time because we share same
	channel bandwidth.
========================================================================
*/
STATIC VOID ACM_TC_TASK_BwAnn(
	ACM_PARAM_IN	ULONG		Data)
{
	ACMR_PWLAN_STRUC pAd = (ACMR_PWLAN_STRUC)Data;
	UINT32 IdMbssNum;
	ULONG SplFlags;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* broadcast our bandwidth */
	ACMP_FrameBwAnnSend(pAd, TRUE);

	/* get management semaphore */
	ACM_TSPEC_SEM_LOCK_CHK(pAd, SplFlags, LabelSemErr);

	/* check admission time timeout from other BSS */
	for(IdMbssNum=0; IdMbssNum<ACM_MBSS_BK_NUM; IdMbssNum++)
	{
		if (ACMR_CB->MBSS[IdMbssNum].Channel == 0)
			continue; /* check next one */
		/* End of if */

		if (++ACMR_CB->MBSS[IdMbssNum].Timeout >= ACM_MBSS_ENTRY_TIMEOUT)
		{
			ACMR_DEBUG(ACMR_DEBUG_TRACE,
						("acm_msg> [mbss] TimeOut %02x:%02x:%02x:%02x:%02x:%02x\n",
						ACMR_CB->MBSS[IdMbssNum].BSSID[0],
						ACMR_CB->MBSS[IdMbssNum].BSSID[1],
						ACMR_CB->MBSS[IdMbssNum].BSSID[2],
						ACMR_CB->MBSS[IdMbssNum].BSSID[3],
						ACMR_CB->MBSS[IdMbssNum].BSSID[4],
						ACMR_CB->MBSS[IdMbssNum].BSSID[5]));

			ACMR_MEM_ZERO(&ACMR_CB->MBSS[IdMbssNum], sizeof(ACM_MBSS_BW));
		} /* End of if */
	} /* End of for */

	/* re-calculate used time for other BSS */
	ACM_MBSS_BwReCalculate(pAd);

	ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);
	return;

LabelSemErr:
	ACMR_SEM_FAIL_PRINT();
	return;
} /* End of ACM_TC_TASK_BwAnn */
#endif /* ACM_CC_FUNC_MBSS */




/* ============================= TX time function ========================== */
/*
========================================================================
Routine Description:
	Calculate the QoS packet transmission time.

Arguments:
	pAd					- WLAN control block pointer
	*pCdb				- the connected client data base
	BodyLen				- the data length, not include WLAN header
	RateIndex			- transmission rate
	FlgIsGmode			- GMODE flag
	FlgIsCtsEnable		- CTS-self flag
	FlgIsRtsEnable		- RTS/CTS flag
	FlgIsSpreambleUsed	- Short preamble flag
	FlgIsNoAckUsed		- NO ACK flag
	TxopLimit			- TXOP limitation (microseconds)

	*pTimeNoData		- the tx time, not include data body
	*pTimeHeader		- the tx time, only include WLAN header
	*pTimeCtsSelf		- the tx time, CTS-self
	*pTimeRtsCts		- the tx time, RTS/CTS
	*pTimeAck			- the tx time, ACK

Return Value:
	transmission time (miscro second, us)

Note:
	1. Only for QoS packet.
	2. If you want to get pTimeHeader only, BodyLen must not be 0.
========================================================================
*/
UINT32 ACM_TX_TimeCal(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd,
	ACM_PARAM_IN	ACMR_STA_DB         *pCdb,
	ACM_PARAM_IN	UINT32				BodyLen,
	ACM_PARAM_IN	UCHAR				RateIndex,
	ACM_PARAM_IN	UCHAR				FlgIsGmode,
	ACM_PARAM_IN	UCHAR				FlgIsCtsEnable,
	ACM_PARAM_IN	UCHAR				FlgIsRtsEnable,
	ACM_PARAM_IN	UCHAR				FlgIsSpreambleUsed,
	ACM_PARAM_IN	UCHAR				FlgIsNoAckUsed,
	ACM_PARAM_IN	UINT32				TxopLimit,
	ACM_PARAM_OUT	UINT32				*pTimeNoData,
	ACM_PARAM_OUT	UINT32				*pTimeHeader,
	ACM_PARAM_OUT	UINT32				*pTimeCtsSelf,
	ACM_PARAM_OUT	UINT32				*pTimeRtsCts,
	ACM_PARAM_OUT	UINT32				*pTimeAck)
{
#ifdef RELEASE_EXCLUDE
/*
	For G mode, no long or short preamble time, only long (20us) or
	short slot time (9us).
*/
#endif /* RELEASE_EXCLUDE */
#define LMR_PREAMBL_TIME(__FlgIsGmode, __FlgIsSpreamble, __Time)	\
	{																\
		if (__FlgIsGmode == 0)										\
		{															\
			if (__FlgIsSpreamble == 0)								\
				__Time += TIME_LONG_PREAMBLE;						\
			else													\
				__Time += TIME_SHORT_PREAMBLE;						\
		}															\
		else														\
			__Time += 20;											\
	}

	UINT32 LenHeader;
	UINT32 RateId;
	UINT32 TxTime;
#ifndef ACM_CC_FUNC_SOFT_ACM
	UINT32 TxTimeFragment;
#endif /* ACM_CC_FUNC_SOFT_ACM */
	UINT32 TimeHeader, TimeData, TimeFrag, TimeRtsCts;
	UINT32 TxTimeCtsSelf, TxTimeRtsCts, TxTimeAck;
	UINT32 DataExtraLen, LenFrag, LenLastFrag, NumFrag;
	UCHAR  FlgIsNeedHardwareFrag;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* init */
	LenHeader = ACMR_FME_QOS_HEADER_SIZE + 4; /* 4: FCS size */
	TxTime = 0;
	TimeRtsCts = 0;
	TxTimeCtsSelf = 0;
	TxTimeRtsCts = 0;
	TxTimeAck = 0;
	TimeHeader = 0;

	/* CTS-self */
	if (FlgIsCtsEnable == 1)
	{
		/* tx time += preamble + CTS-self + SIFS */
		TxTimeCtsSelf = 0;

		switch(RateIndex)
		{
			case ACM_RATE_54M:
				RateId = ACM_RATE_ID_54M;
				break;

			case ACM_RATE_48M:
				RateId = ACM_RATE_ID_48M;
				break;

			case ACM_RATE_36M:
				RateId = ACM_RATE_ID_36M;
				break;

			case ACM_RATE_24M:
				RateId = ACM_RATE_ID_24M;
				break;

			case ACM_RATE_18M:
				RateId = ACM_RATE_ID_18M;
				break;

			case ACM_RATE_12M:
				RateId = ACM_RATE_ID_12M;
				break;

			case ACM_RATE_9M:
				RateId = ACM_RATE_ID_9M;
				break;

			case ACM_RATE_6M:
				RateId = ACM_RATE_ID_6M;
				break;

			case ACM_RATE_11M:
				RateId = ACM_RATE_ID_11M;
				break;

			case ACM_RATE_5_5M:
				RateId = ACM_RATE_ID_5_5M;
				break;

			case ACM_RATE_2M:
				RateId = ACM_RATE_ID_2M;
				break;

			case ACM_RATE_1M:
				RateId = ACM_RATE_ID_1M;
				break;

			default:
				RateId = ACM_RATE_ID_1M;
				ACMR_DEBUG(ACMR_DEBUG_ERR,
							("acm_err> RateIndex Error! TX_TimeCal()\n"));
				break;
		} /* End of switch */

		LMR_PREAMBL_TIME(FlgIsGmode, FlgIsSpreambleUsed, TxTimeCtsSelf);
		TxTimeCtsSelf += \
					ACM_TX_TimePlcpCal(FRM_LENGTH_ACK, RateId, FlgIsGmode);
		TxTimeCtsSelf += TIME_SIFSG;

		if (pTimeCtsSelf != NULL)
			*pTimeCtsSelf = TxTimeCtsSelf;
		/* End of if */
	} /* End of if */


	/* RTS and CTS */
	if ((FlgIsCtsEnable == 0) && (FlgIsRtsEnable == 1))
	{
		/* tx time += preamble + RTS + SIFS + preamble + CTS + SIFS */
		LMR_PREAMBL_TIME(FlgIsGmode, FlgIsSpreambleUsed, TimeRtsCts);
		LMR_PREAMBL_TIME(FlgIsGmode, FlgIsSpreambleUsed, TimeRtsCts);

		switch(RateIndex)
		{
			case ACM_RATE_54M:
			case ACM_RATE_48M:
			case ACM_RATE_36M:
			case ACM_RATE_24M:
				TimeRtsCts += TIME_SIFSGx2;
				RateId = ACM_RATE_ID_24M;
				break;

			case ACM_RATE_18M:
			case ACM_RATE_12M:
				TimeRtsCts += TIME_SIFSGx2;
				RateId = ACM_RATE_ID_12M;
				break;

			case ACM_RATE_9M:
			case ACM_RATE_6M:
				TimeRtsCts += TIME_SIFSGx2;
				RateId = ACM_RATE_ID_6M;
				break;

			case ACM_RATE_11M:
				TimeRtsCts += TIME_SIFSx2;
				RateId = ACM_RATE_ID_11M;
				break;

			case ACM_RATE_5_5M:
				TimeRtsCts += TIME_SIFSx2;
				RateId = ACM_RATE_ID_5_5M;
				break;

			case ACM_RATE_2M:
				TimeRtsCts += TIME_SIFSx2;
				RateId = ACM_RATE_ID_2M;
				break;

			case ACM_RATE_1M:
				TimeRtsCts += TIME_SIFSx2;
				RateId = ACM_RATE_ID_1M;
				break;

			default:
				TimeRtsCts += TIME_SIFSx2;
				RateId = ACM_RATE_ID_1M;
				ACMR_DEBUG(ACMR_DEBUG_ERR,
							("acm_err> RateIndex Error! TX_TimeCal()\n"));
				break;
		} /* End of switch */

		TimeRtsCts += ACM_TX_TimePlcpCal(FRM_LENGTH_RTS, RateId, FlgIsGmode);
		TimeRtsCts += ACM_TX_TimePlcpCal(FRM_LENGTH_ACK, RateId, FlgIsGmode);

		if (pTimeRtsCts != NULL)
			*pTimeRtsCts = TimeRtsCts;
		/* End of if */
	} /* End of if */


	/* SIFS + preamble + ACK */
	if (FlgIsNoAckUsed == 0)
	{
		TxTimeAck = 0;
		LMR_PREAMBL_TIME(FlgIsGmode, FlgIsSpreambleUsed, TxTimeAck);

		switch(RateIndex)
		{
			case ACM_RATE_54M:
			case ACM_RATE_48M:
			case ACM_RATE_36M:
			case ACM_RATE_24M:
				TxTimeAck += TIME_SIFSG;
				RateId = ACM_RATE_ID_24M;
				break;

			case ACM_RATE_18M:
			case ACM_RATE_12M:
				TxTimeAck += TIME_SIFSG;
				RateId = ACM_RATE_ID_12M;
				break;

			case ACM_RATE_9M:
			case ACM_RATE_6M:
				TxTimeAck += TIME_SIFSG;
				RateId = ACM_RATE_ID_6M;
				break;

			case ACM_RATE_11M:
				TxTimeAck += TIME_SIFS;
				RateId = ACM_RATE_ID_11M;
				break;

			case ACM_RATE_5_5M:
				TxTimeAck += TIME_SIFS;
				RateId = ACM_RATE_ID_5_5M;
				break;

			case ACM_RATE_2M:
				TxTimeAck += TIME_SIFS;
				RateId = ACM_RATE_ID_2M;
				break;

			case ACM_RATE_1M:
				TxTimeAck += TIME_SIFS;
				RateId = ACM_RATE_ID_1M;
				break;

			default:
				TxTimeAck += TIME_SIFS;
				RateId = ACM_RATE_ID_1M;
				ACMR_DEBUG(ACMR_DEBUG_ERR,
							 ("acm_err> RateIndex Error! TX_TimeCal()\n"));
				break;
		} /* End of switch */

		/*
			EX: data size = 208B, rate = 54Mbps,
			TxTimeAck = 44us in A band.
		*/
		TxTimeAck += ACM_TX_TimePlcpCal(FRM_LENGTH_ACK, RateId, FlgIsGmode);
	}
	else
	{
		/* use NO ACK policy so ack time = 0 */
		TxTimeAck = 0;
	} /* End of if */

	if (pTimeAck != NULL)
		*pTimeAck = TxTimeAck;
	/* End of if */

	if (BodyLen > 0)
	{
		/* preamble + Data */
		switch(RateIndex)
		{
			case ACM_RATE_54M:
				RateId = ACM_RATE_ID_54M;
				break;

			case ACM_RATE_48M:
				RateId = ACM_RATE_ID_48M;
				break;

			case ACM_RATE_36M:
				RateId = ACM_RATE_ID_36M;
				break;

			case ACM_RATE_24M:
				RateId = ACM_RATE_ID_24M;
				break;

			case ACM_RATE_18M:
				RateId = ACM_RATE_ID_18M;
				break;

			case ACM_RATE_12M:
				RateId = ACM_RATE_ID_12M;
				break;

			case ACM_RATE_9M:
				RateId = ACM_RATE_ID_9M;
				break;

			case ACM_RATE_6M:
				RateId = ACM_RATE_ID_6M;
				break;

			case ACM_RATE_11M:
				RateId = ACM_RATE_ID_11M;
				break;

			case ACM_RATE_5_5M:
				RateId = ACM_RATE_ID_5_5M;
				break;

			case ACM_RATE_2M:
				RateId = ACM_RATE_ID_2M;
				break;

			case ACM_RATE_1M:
				RateId = ACM_RATE_ID_1M;
				break;

			default:
				RateId = ACM_RATE_ID_1M;
				ACMR_DEBUG(ACMR_DEBUG_ERR,
							("acm_err> RateIndex Error! TX_TimeCal()\n"));
				break;
		} /* End of switch */

		/* add data preamble tx time */
		/*
			EX: data size = 208B, rate = 54Mbps,
			LenHeader = 30B, TimeHeader = 28us in A band.
		*/
		LMR_PREAMBL_TIME(FlgIsGmode, FlgIsSpreambleUsed, TimeHeader);
		TimeHeader += ACM_TX_TimePlcpCal(LenHeader, RateId, FlgIsGmode);

		/* maybe software fragment */
		if (TxopLimit > 0)
		{
			/* check whether tx time > txop limit */
			LenFrag = ACMR_FRG_THRESH(pAd);

			if ((LenFrag == 0) || (BodyLen <= LenFrag))
			{
				/* no fragment is needed */
				LenFrag = BodyLen;
				FlgIsNeedHardwareFrag = 0;
			}
			else
			{
				/* fragment is needed */
				FlgIsNeedHardwareFrag = 1;
			} /* End of if */

			/* LenFrag = total len (no fragment) or fragment len */

			/* calculate data body transmission time */
			DataExtraLen = ACM_EncryptExtraLenGet(pAd, pCdb);
			TimeData = ACM_TX_TimePlcpCal(LenFrag+DataExtraLen, RateId, FlgIsGmode);

			/* calculate all transmission time */
			if (FlgIsRtsEnable == 1)
				TxTimeRtsCts = TimeRtsCts; /* need RTS/CTS */
			else
				TxTimeRtsCts = 0;
			/* End of if */

			/* pre-sum CTS-self "or" (not "and") RTS/CTS tx time */
			TxTime = TxTimeCtsSelf + TxTimeRtsCts;

			if (FlgIsNeedHardwareFrag == 1)
			{
				/*
					Calculate all fragment tx time because our ASIC can not
					distribute fragment to different TXOP.
				*/
				NumFrag = BodyLen/ LenFrag;
				TxTime += (TimeHeader + TimeData + TxTimeAck) * NumFrag;

				LenLastFrag = BodyLen % LenFrag;
				if (LenLastFrag > 0)
				{
					/* sum the last fragment tx time */
					TimeData = ACM_TX_TimePlcpCal(LenLastFrag+DataExtraLen,
													RateId, FlgIsGmode);
					TxTime += TimeHeader + TimeData + TxTimeAck;
				} /* End of if */
			}
			else
			{
				/* only a packet, no fragment */
				TxTime += (TimeHeader + TimeData + TxTimeAck);
			} /* End of if */


			/* check whether tx time > TXOP limitation */
#ifndef ACM_CC_FUNC_SOFT_ACM
			if (TxTime > TxopLimit)
			{
				/* we need to do software fragment, calculate residant TXOP */
				/* MUST no hardware fragment */
				TxTime = (TxTimeCtsSelf + TxTimeRtsCts +
						TimeHeader + TxTimeAck);

				if (TxopLimit > TxTime)
				{
					if (FlgIsNeedHardwareFrag == 0)
					{
						TxopLimit -= (TxTimeCtsSelf + TxTimeRtsCts +
									TimeHeader + TxTimeAck);
					}
					else
					{
						/* calculate a fragment tx time */
						TxTimeFragment = TimeHeader+TimeData+TxTimeAck;

						/* dont care about CTS-Self or RTS/CTS tx time */
						TxopLimit -= (TxTimeCtsSelf + TxTimeRtsCts);

						if (TxTimeFragment > TxopLimit)
						{
							/*
								A fragment tx time > TXOP limit
								so do need to do fragment, we must make a
								packet len <= fragment threshold.
							*/
							TxopLimit -= (TimeHeader + TxTimeAck);
						}
						else
						{
							/*
								We can not use the LenFrag as software fragment
								length, when >= 2 fragments can be transmitted
								in a TXOP, maybe another MSDU and our fragment
								is transmitted in a TXOP or our both fragments
								are transmitted in a TXOP, we can not predict
								the case.
							*/
							TxopLimit -= (TimeHeader + TxTimeAck);
						} /* End of if */
					} /* End of if */

					/* calculate packet len allowed to be transmitted in a TXOP */
					if (FlgIsGmode == 1)
					{
						LenFrag = TxopLimit>>2;
						LenFrag = (LenFrag<<1)*RateIndex;
						if (LenFrag > 22)
							LenFrag = (LenFrag-22)>>3;
						/* End of if */
					}
					else
						LenFrag = ((TxopLimit*RateIndex)>>4);
					/* End of if */

					if (LenFrag >= DataExtraLen)
					{
						LenFrag -= DataExtraLen;

						if (LenFrag > 1)
							LenFrag --; /* for safe */
						/* End of if */

						/* for software fragment, we dont need 128-double */
					} /* End of if */
				}
				else
				{
					/*
						Fatal error, txop limit is too small.
						So dont care txop limit and transmit the packet without
						fragment.
					*/
					LenFrag = BodyLen;
				} /* End of if */
			} /* End of if */
#endif /* ACM_CC_FUNC_SOFT_ACM */

			/*
				Calculate MSDU exchange time, LenFrag is the packet length
				allowed to transmitted in a TXOP.
			*/
			if (LenFrag == BodyLen)
			{
				/* no fragment is needed */
				TimeData = ACM_TX_TimePlcpCal(BodyLen+DataExtraLen,
												RateId, FlgIsGmode);

				/* sum all tx time */
				TxTime = TxTimeCtsSelf + TxTimeRtsCts;
				TxTime += TimeHeader + TxTimeAck;
			}
			else
			{
				/* fragment is needed */
				/* LenFrag = a software fragment length in a TXOP */
				TimeFrag = ACM_TX_TimePlcpCal(LenFrag+DataExtraLen,
												RateId, FlgIsGmode);

				NumFrag = BodyLen/ LenFrag;
				LenLastFrag = BodyLen % LenFrag;

				if (LenFrag <= ACMR_RTS_THRESH(pAd))
					TxTimeRtsCts = 0; /* no RTS/CTS is needed for new len */
				/* End of if */

				/* add other fragment tx time except the tail fragment */
				if (NumFrag >= 1)
				{
					TimeData = (TxTimeCtsSelf + TxTimeRtsCts +
								TimeHeader + TimeFrag +
								TxTimeAck) * NumFrag;
				} /* End of if */

				/* add tail fragment tx time */
				if (LenLastFrag > 0)
				{
					TimeData += (TxTimeCtsSelf + TxTimeRtsCts +
								TimeHeader + TxTimeAck);
					TimeData += ACM_TX_TimePlcpCal(LenLastFrag+DataExtraLen,
													RateId, FlgIsGmode);
				} /* End of if */

				/* tx time is included in TimeData */
				TxTime = 0;
			} /* End of if */
		}
		else
		{
			/* add data tx time */
			/*
				EX: data size = 208B, rate = 54Mbps,
				TimeData without header = 32us in A band.
			*/
			if (BodyLen > 0)
			{
				DataExtraLen = ACM_EncryptExtraLenGet(pAd, pCdb);
				TimeData = ACM_TX_TimePlcpCal(BodyLen+DataExtraLen,
												RateId, FlgIsGmode);
			}
			else
				TimeData = 0;
			/* End of if */

			/* sum all tx time */
			TxTime = TxTimeCtsSelf + TxTimeRtsCts;
			TxTime += TimeHeader + TxTimeAck;
		} /* End of if */
	}
	else
		TimeData = 0;
	/* End of if */

	if (pTimeNoData != NULL)
		*pTimeNoData = TxTime;
	/* End of if */

	if (pTimeHeader != NULL)
		*pTimeHeader = TimeHeader;
	/* End of if */

	TxTime += TimeData;

	/*
		EX1: data size = 208B, rate = 54Mbps,
		TxTime = 104us in A band.

		EX2: data size = 208B, rate = 6Mbps,
		In A band, TxTimeAck = 60us, TimeHeader = 64us,
		TimeData = 284us, so TxTime = 64+284+60 = 408us.
	*/
	return TxTime;
} /* End of ACM_TX_TimeCal */


#ifdef ACM_CC_FUNC_11N
/*
========================================================================
Routine Description:
	Calculate the QoS packet transmission time for HT rate.

Arguments:
	pAd					- WLAN control block pointer
	*pCdb				- the connected client data base
	BodyLen				- the data length, not include WLAN header
	McsId				- transmission MCS
	BwId				- 20 or 20/40MHz
	GIId				- regular or short GI
	FlgIsRtsEnable		- RTS/CTS flag
	FlgIsNoAckUsed		- NO ACK flag
	FlgIsOnlyData		- 1: no HT preamble time
	TxopLimit			- TXOP limitation (microseconds)
	FlgIsAmsdu			- 1: the MSDU is belong to AMSDU
	NominalAggSize		- number of MSDUs in a aggregation

	*pTimeNoData		- the tx time, not include data body
	*pTimeHeader		- the tx time, only include WLAN header
	*pTimeAck			- the tx time, BLOCK ACK
	*pTimeDataHdrOnly	- the tx time, data + BLOCK ACK
	*pTimeDataOnly		- the tx time, data

Return Value:
	transmission time (miscro second, us)

Note:
	1. Only for QoS packet.
	2. No HT rate for RTS/CTS.
========================================================================
*/
UINT32 ACM_TX_TimeCalHT(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd,
	ACM_PARAM_IN	ACMR_STA_DB         *pCdb,
	ACM_PARAM_IN	UINT32				BodyLen,
	ACM_PARAM_IN	UINT32				McsId,
	ACM_PARAM_IN	UINT32				BwId,
	ACM_PARAM_IN	UINT32				GIId,
	ACM_PARAM_IN	BOOLEAN				FlgIsRtsEnable,
	ACM_PARAM_IN	BOOLEAN				FlgIsNoAckUsed,
	ACM_PARAM_IN	BOOLEAN				FlgIsOnlyData,
	ACM_PARAM_IN	UINT32				TxopLimit,
	ACM_PARAM_IN	BOOLEAN				FlgIsAmsdu,
	ACM_PARAM_IN	UINT16				NominalAggSize,
	ACM_PARAM_OUT	UINT32				*pTimeNoData,
	ACM_PARAM_OUT	UINT32				*pTimeHeader,
	ACM_PARAM_OUT	UINT32				*pTimeAck,
	ACM_PARAM_OUT	UINT32				*pTimeDataHdrOnly,
	ACM_PARAM_OUT	UINT32				*pTimeDataOnly)
{
	UINT32 LenHeader;
	UINT32 TxTime;
	UINT32 TimeHeader, TimeData;
	UINT32 TxTimeAck;
	UINT32 DataExtraLen;
	UINT32 Nss;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/*
		<AMPDU> is used in AP or station.

		Nominal A-MPDU Size =
				Nominal MSDU Aggregation * Nominal A-MPDU Subframe Size
				- Pad Size

		Nominal A-MPDU Subframe Size =
				MPDU Delimiter Size
				+ MAC Header Size
				+ Nominal MSDU Size
				+ Security Encapsulation Size
				+ FCS Size
				+ Pad Size

		A-MPDU Subframe is as below:
			AMPDU subframe header (4) + Padding (0~3)
	*/

	/*
		<AMSDU> is used only in station.

		Nominal A-MSDU Size =
				MAC Header Size
				+ Nominal MSDU Aggregation * Nominal A-MSDU Subframe Size
				- Pad Size
				+ Security Encapsulation Size
				+ FCS Size

		Nominal A-MSDU Subframe Size =
				A-MSDU Subframe Header Size
				+ Nominal MSDU Size
				+ Pad Size

		A-MSDU Subframe is as below:
			DA(6) + SA(6) + Length(2) + MSDU + Padding (0~3)
	*/

	/* init */
	LenHeader = ACMR_FME_QOS_N_HEADER_SIZE;
	TxTime = 0;
	TxTimeAck = 0;
	TimeHeader = 0;
	DataExtraLen = 0;

	if (McsId < 8)
		Nss = 1;
	else
	{
		if (McsId < 16)
			Nss = 2;
		else
		{
			if (McsId < 24)
				Nss = 3;
			else
			{
				if (McsId < 32)
					Nss = 4;
				else
				{
					McsId = 31; /* maximum MCS */
					Nss = 1;
				} /* End of if */
			} /* End of if */
		} /* End of if */
	} /* End of if */

	/* TODO: If in green-field mode, use RIFS */

	/* SIFS + ACK */
	if (FlgIsNoAckUsed == 0)
	{
		TxTimeAck = 0;

		LMR_PREAMBL_TIME(1, 0, TxTimeAck);

		TxTimeAck += TIME_SIFSG;

		TxTimeAck += ACM_TX_TimePlcpCal(
										FRM_LENGTH_BLOCK_ACK,
										ACM_RATE_ID_24M,
										1);
	}
	else
	{
		/* use NO ACK policy so ack time = 0 */
		TxTimeAck = 0;
	} /* End of if */

	if (pTimeAck != NULL)
		*pTimeAck = TxTimeAck;
	/* End of if */

	/* If FlgIsOnlyData == 1, means we do not care HT preamble time */

	/* preamble + header */
	TimeHeader = ACM_TX_TimePlcpCalHT(
										LenHeader,
										McsId,
										Nss,
										0,
										0,
										BwId,
										GIId,
										FlgIsOnlyData);

	/* add data tx time without preamble */
	if (BodyLen > 0)
	{
		if (pCdb != NULL)
			DataExtraLen = ACM_EncryptExtraLenGet(pAd, pCdb);
		/* End of if */

		if (NominalAggSize > 0)
		{
			if (FlgIsAmsdu == TRUE)
			{
				/* AMSDU */
				/* DA(6) + SA(6) + Length(2) + MSDU + Padding (0~3) */
				DataExtraLen += FRM_LENGTH_AGG_AMSDU_HDR;
				DataExtraLen += (FRM_LENGTH_AGG_AMSDU_HDR+BodyLen) *\
														(NominalAggSize - 1);
			}
			else
			{
				/* AMPDU */
				/* 4 for AMPDU subframe header, 3 for padding */
				DataExtraLen += 4+3;
				DataExtraLen += (LenHeader+BodyLen+4+3);

				/* include security octets for each MPDU in the AMPDU */
				DataExtraLen *= (NominalAggSize - 1);
			} /* End of if */
		} /* End of if */

		/* first MPDU */
		TimeData = ACM_TX_TimePlcpCalHT(
									LenHeader+BodyLen+DataExtraLen,
									McsId,
									Nss,
									0,
									0,
									BwId,
									GIId,
									FlgIsOnlyData);

		TimeData -= TimeHeader; /* only data, no HT preamble */

		if (NominalAggSize > 1)
		{
			/* aggregation */
			if (FlgIsAmsdu == FALSE)
			{
				/* AMPDU */
				/* Minimum MPDU Start Spacing, suppose tMMSS = 16us */
				TimeData += 16 * (NominalAggSize - 1);
			} /* End of if */
		} /* End of if */
	}
	else
		TimeData = 0;
	/* End of if */

	/* sum all tx time */
	TxTime = TimeHeader + TxTimeAck;

	if (pTimeNoData != NULL)
		*pTimeNoData = TxTime;
	/* End of if */

	if (pTimeHeader != NULL)
		*pTimeHeader = TimeHeader;
	/* End of if */

	if (pTimeDataHdrOnly != NULL)
	{
		if ((pCdb != NULL) && (TimeData > 0))
			DataExtraLen = ACM_EncryptExtraLenGet(pAd, pCdb);
		/* End of if */

		if ((BodyLen > 0) && (TimeData == 0))
		{
			/* calculate time */
			*pTimeDataHdrOnly = ACM_TX_TimePlcpCalHT(
											LenHeader+BodyLen+DataExtraLen,
											McsId,
											Nss,
											0,
											0,
											BwId,
											GIId,
											FlgIsOnlyData);
		}
		else
			*pTimeDataHdrOnly = TimeHeader + TimeData;
		/* End of if */
	} /* End of if */

	if (pTimeDataOnly != NULL)
	{
		if ((pCdb != NULL) && (TimeData > 0))
			DataExtraLen = ACM_EncryptExtraLenGet(pAd, pCdb);
		/* End of if */

		if ((BodyLen > 0) && (TimeData == 0))
		{
			/* calculate time */
			*pTimeDataOnly = ACM_TX_TimePlcpCalHT(
											BodyLen+DataExtraLen,
											McsId,
											Nss,
											0,
											0,
											BwId,
											GIId,
											FlgIsOnlyData);
		}
		else
			*pTimeDataOnly = TimeData;
		/* End of if */
	} /* End of if */

	TxTime += TimeData;

	if (FlgIsRtsEnable != 0)
	{
		/* add RTS/CTS 24Mbps time */
#ifdef ACM_CC_FUNC_AUX_TX_TIME
		UINT32 TimeRtsCts;

		ACM_TX_TimeCal(pAd, NULL,
						0,						/* BodyLen = 0 */
						ACM_RATE_24M,			/* RateIndex */
						1,						/* g mode */
						0,						/* cts-self */
						1,						/* rts/cts */
						0,						/* IdPreambleNum */
						1,						/* no ack */
						0,						/* txop limit */
						NULL,					/* no data tx time */
						NULL,					/* wlan header tx time */
						NULL,					/* cts self tx time */
						&TimeRtsCts,			/* rts/cts tx time */
						NULL);					/* ack tx time */
		TxTime += TimeRtsCts;
#else

		TxTime += gAcmTxTimeOthers[ACM_PRE_TIME_ID_24M][0][2];
#endif /* ACM_CC_FUNC_AUX_TX_TIME */
	} /* End of if */

	return TxTime;
} /* End of ACM_TX_TimeCalHT */
#endif /* ACM_CC_FUNC_11N */


/*
========================================================================
Routine Description:
	Calculate the QoS packet transmission time on the fly.

Arguments:
	pAd					- WLAN control block pointer
	*pCdb				- the connected client data base
	BodyLen				- the data length, not include WLAN header
	RateIndex			- transmission rate, such as ACM_PRE_TIME_ID_1M, etc.
	FlgIsCtsEnable		- CTS-self flag
	FlgIsRtsEnable		- RTS/CTS flag
	FlgIsSpreambleUsed	- Short preamble flag
	FlgIsNoAckUsed		- NO ACK flag

Return Value:
	transmission time (miscro second, us)

Note:
	1. Only for QoS packet.
========================================================================
*/
UINT32 ACM_TX_TimeCalOnFly(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd,
	ACM_PARAM_IN	ACMR_STA_DB         *pCdb,
	ACM_PARAM_IN	UINT32				BodyLen,
	ACM_PARAM_IN	UCHAR				RateIndex,
	ACM_PARAM_IN	UCHAR				FlgIsCtsEnable,
	ACM_PARAM_IN	UCHAR				FlgIsRtsEnable,
	ACM_PARAM_IN	UCHAR				FlgIsSpreambleUsed,
	ACM_PARAM_IN	UCHAR				FlgIsNoAckUsed)
{
	UINT32 TxTime;
#ifndef ACM_CC_FUNC_AUX_TX_TIME
	UINT32 LenDataId;
	UINT32 RateRtsCtsId;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	LenDataId = BodyLen >> ACM_PRE_TIME_DATA_SIZE_OFFSET;

	if (RateIndex > ACM_PRE_TIME_ID_54M)
		RateIndex = ACM_PRE_TIME_ID_54M;
	/* End of if */

#if 0
	if (LenDataId > 0)
		LenDataId --; /* use smaller length */
	/* End of if */
#endif /* 0 */

	if (LenDataId >= ACM_PRE_TIME_DATA_SIZE_NUM)
		LenDataId = ACM_PRE_TIME_DATA_SIZE_NUM-1;
	/* End of if */

	/* sum the time: cts-self + rts/cts + preamble + header + data + ack */
	TxTime = gAcmTxTimeOthers[RateIndex][FlgIsSpreambleUsed][ACM_PRE_TIME_HEADER];
	TxTime += gAcmTxTimeBody[RateIndex][LenDataId];

	if (FlgIsCtsEnable)
	{
		RateRtsCtsId = ACMR_PHY_RATE_ID_RTS_CTS_GET(pAd);
		TxTime += gAcmTxTimeOthers[RateRtsCtsId][FlgIsSpreambleUsed][ACM_PRE_TIME_CTS_SELF];
	} /* End of if */

	if (FlgIsRtsEnable)
	{
		RateRtsCtsId = ACMR_PHY_RATE_ID_RTS_CTS_GET(pAd);
		TxTime += gAcmTxTimeOthers[RateRtsCtsId][FlgIsSpreambleUsed][ACM_PRE_TIME_RTS_CTS];
	} /* End of if */

	if (FlgIsNoAckUsed == 0)
		TxTime += gAcmTxTimeOthers[RateIndex][FlgIsSpreambleUsed][ACM_PRE_TIME_ACK];
	/* End of if */
#else

	UCHAR RateMapping[ACM_RATE_MAX_NUM] = {
			ACM_RATE_1M,		ACM_RATE_2M,	ACM_RATE_5_5M,
			ACM_RATE_11M,		ACM_RATE_6M,	ACM_RATE_9M,
			ACM_RATE_12M,		ACM_RATE_18M,	ACM_RATE_24M,
			ACM_RATE_36M,		ACM_RATE_48M,	ACM_RATE_54M
		};
	UCHAR FlgIsGmode;


	if (RateIndex <= 3)
		FlgIsGmode = 0;
	else
		FlgIsGmode = 1;
	/* End of if */

	if (FlgIsCtsEnable)
		FlgIsRtsEnable = 0;
	/* End of if */

	TxTime = ACM_TX_TimeCal(pAd, NULL,
							0,						/* BodyLen = 0 */
							RateMapping[RateIndex],	/* RateIndex */
							FlgIsGmode,				/* g mode */
							FlgIsCtsEnable,			/* cts-self */
							FlgIsRtsEnable,			/* rts/cts */
							FlgIsSpreambleUsed,		/* IdPreambleNum */
							FlgIsNoAckUsed,			/* no ack */
							0,						/* txop limit */
							NULL,					/* no data tx time */
							NULL,					/* wlan header tx time */
							NULL,					/* cts self tx time */
							NULL,					/* rts/cts tx time */
							NULL);					/* ack tx time */
#endif /* ACM_CC_FUNC_AUX_TX_TIME */

	return TxTime;
} /* End of ACM_TX_TimeCalOnFly */


#ifdef ACM_CC_FUNC_11N
/*
========================================================================
Routine Description:
	Calculate the QoS packet transmission time on the fly for HT rate.

Arguments:
	pAd					- WLAN control block pointer
	*pCdb				- the connected client data base
	*pStream			- the matched TSPEC
	Timestamp			- current Timestamp, unit: micro second
	BodyLen				- the data length, not include WLAN header
	McsId				- transmission rate, 0 ~ 31
	FlgIsNoAckUsed		- NO ACK flag

Return Value:
	transmission time (miscro second, us)

Note:
	1. Only for QoS packet.
	2. Protect in caller.
========================================================================
*/
#ifdef WMM_ACM_PKT_NUM_DEBUG
/* global variables but only for debug */
UINT32 WMM_ACM_TimeOffsetHTId = 0;
UINT32 WMM_ACM_TimeOffsetHT[WMM_ACM_DEBUG_TIME_MAX_NUM_REC][4];
#endif /* WMM_ACM_PKT_NUM_DEBUG */
UINT32 ACM_TX_TimeCalOnFlyHT(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd,
	ACM_PARAM_IN	ACMR_STA_DB         *pCdb,
	ACM_PARAM_IN	ACM_STREAM			*pStream,
	ACM_PARAM_IN	UINT64				Timestamp,
	ACM_PARAM_IN	UINT32				BodyLen,
	ACM_PARAM_IN	UINT32				McsId,
	ACM_PARAM_IN	UCHAR				FlgIsNoAckUsed)
{
#ifdef RELEASE_EXCLUDE
	/*
		One packet or AMPDU transmission rule:

		1. If the packet is the first one, backup its tx time T1 and timestamp;

		2. If the packet is not the first one,
			check if current total tx time is larger than T1;

				(1) If yes, the packet is the new first one, goto 1;

				(2) If no, add the tx time to the total tx time and
					use AMPDU rule to calculate total tx time;
	*/
#endif /* RELEASE_EXCLUDE */

	UINT32 TxTime;
	UINT32 TimeOffset;
	UINT8 NumOfBaWinSize;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	TimeOffset = (UINT32)(Timestamp - pStream->TxTimestampEnqueueHT);

	if (pStream->pTspec->TsInfo.AckPolicy == ACM_ACK_POLICY_BLOCK)
		NumOfBaWinSize = pStream->HT_BaWinSize;
	else
		NumOfBaWinSize = 0; /* not BLOCK ACK for the TSPEC */
	/* End of if */

	if (!ACMR_HT_IS_BA_BUILT(pCdb, pStream->UP))
		NumOfBaWinSize = 0; /* not yet build the BA session */
	/* End of if */

#ifdef WMM_ACM_PKT_NUM_DEBUG
	if ((NumOfBaWinSize != 0) &&
		(WMM_ACM_TimeRecId == 5) &&
		(WMM_ACM_IsTimeValid == TRUE))
	{
		/* AMPDU */
		if (WMM_ACM_TimeOffsetHTId >= WMM_ACM_DEBUG_TIME_MAX_NUM_REC)
			WMM_ACM_TimeOffsetHTId = 0;
		/* End of if */

		WMM_ACM_TimeOffsetHT[WMM_ACM_TimeOffsetHTId][0] = TimeOffset;
		WMM_ACM_TimeOffsetHT[WMM_ACM_TimeOffsetHTId][1] = pStream->TxTimeEnqueueHT;
		WMM_ACM_TimeOffsetHT[WMM_ACM_TimeOffsetHTId][2] = pStream->TxAmpduNumEnqueueHT;
	} /* End of if */
#endif /* WMM_ACM_PKT_NUM_DEBUG */

	if ((pStream->TxAmpduNumEnqueueHT == 0) ||
		(pStream->TxAmpduNumEnqueueHT > (UINT32)NumOfBaWinSize) ||
		(TimeOffset > pStream->TxTimeEnqueueHT))
	{
#ifdef RELEASE_EXCLUDE
		/*
			1. first packet before making up a AMPDU;

			2. maximum BLOCK ACK NUM is limited;

			2. offset between new packet and first packet is larger than
				the tx time of first packet, we will not put the packet
				into the AMPDU so we make up another AMPDU;
		*/
#endif /* RELEASE_EXCLUDE */

		/* sum the time: rts/cts + preamble + header + data + block ack */
		/* 802.11 header is included defaultly in ACM_TX_TimeCalHT() */
#ifdef ACM_CC_FUNC_AUX_TX_TIME
		TxTime = ACM_TX_TimeCalHT(pAd, NULL,
							BodyLen,		/* body len */
							McsId,					/* MCS Index */
							ACMR_IS_2040_STA(pCdb),	/* 20 or 20/40 MHz */
							0,				/* regular or short GI */
							1,				/* rts/cts */
							0,				/* no AMPDU or fist pkt in AMPDU */
							FlgIsNoAckUsed,	/* no ack */
							0xFFFFFFFF,		/* txop limit */
							0,				/* non-AMSDU means AMPDU */
							0,				/* do not care */
							NULL,			/* no data tx time */
							NULL,			/* wlan header tx time */
							NULL,			/* ack tx time */
							NULL,			/* data+hdr only tx time */
							NULL); 			/* data only tx time */
#else
		UINT32 LenDataId;

		LenDataId = (BodyLen+ACMR_FME_QOS_N_HEADER_SIZE);
		LenDataId >>= ACM_PRE_TIME_DATA_SIZE_OFFSET;

#if 0
		if (LenDataId > 0)
			LenDataId --; /* use smaller length */
		/* End of if */
#endif /* 0 */

		if (LenDataId >= ACM_PRE_TIME_DATA_SIZE_NUM)
			LenDataId = ACM_PRE_TIME_DATA_SIZE_NUM-1;
		/* End of if */

		/* add RTS/CTS time (maybe no RTS/CTS in the air) */
		TxTime = gAcmTxTimeOthers[ACM_PRE_TIME_ID_24M][0][2];

		/* add data time */
		TxTime += gAcmTxTimeBodyHT\
							[ACMR_IS_2040_STA(pCdb)][0][McsId][LenDataId][0];

		/* add block ack time */
		if (FlgIsNoAckUsed == 0)
			TxTime += gAcmTxTimeOthersHT;
		/* End of if */
#endif /* ACM_CC_FUNC_AUX_TX_TIME */

		/* statistics */
#ifdef ACM_CC_FUNC_STATS
		if (pStream->TxAmpduNumEnqueueHT > 1)
		{
			ACM_CTRL_PARAM *pEdcaParam;
			ACM_STATISTICS *pStats;

			pEdcaParam = &(ACMR_CB->EdcaCtrlParam);
			pStats = &pEdcaParam->Stats;

			ACM_STATS_COUNT_INC(pStats->AMPDU[pStream->TxAmpduNumEnqueueHT]);
		} /* End of if */
#endif /* ACM_CC_FUNC_STATS */

		/* backup time record */
		if (pStream->TxAmpduNumEnqueueHT == 0)
			pStream->TxAmpduNumEnqueueHT ++;
		else
		{
			pStream->TxTimestampEnqueueHT = Timestamp;
			pStream->TxTimeEnqueueHT = TxTime;
			pStream->TxAmpduNumEnqueueHT = 0;
		} /* End of if */
	}
	else
	{
		/* AMPDU handle */
		/* skip PHY preamble & ACK time */
		/* ACMR_FME_QOS_N_HEADER_SIZE + 4 for AMPDU subframe header, 3 for padding */
#ifdef ACM_CC_FUNC_AUX_TX_TIME
		UINT32 TimeData;

		/* 802.11 header is included defaultly in ACM_TX_TimeCalHT() */
		TimeData = ACM_TX_TimeCalHT(pAd, NULL,
							BodyLen+4+3,	/* body len */
							McsId,			/* MCS Index */
							ACMR_IS_2040_STA(pCdb),	/* 20 or 20/40 MHz */
							0,				/* regular or short GI */
							1,				/* rts/cts */
							1,				/* AMPDU */
							FlgIsNoAckUsed,	/* no ack */
							0,				/* txop limit */
							0,				/* non-AMSDU means AMPDU */
							0,				/* do not care */
							NULL,			/* no data tx time */
							NULL,			/* wlan header tx time */
							NULL,			/* ack tx time */
							&TxTime,		/* data+hdr only tx time */
							NULL);			/* data only tx time */
#else
		UINT32 LenDataId;

		/* 802.11 header for each MPDU of A-MPDU */
		LenDataId = (ACMR_FME_QOS_N_HEADER_SIZE + BodyLen + 4 + 3);
		LenDataId = LenDataId >> ACM_PRE_TIME_DATA_SIZE_OFFSET;

#if 0
		if (LenDataId > 0)
			LenDataId --; /* use smaller length */
		/* End of if */
#endif /* 0 */

		if (LenDataId >= ACM_PRE_TIME_DATA_SIZE_NUM)
			LenDataId = ACM_PRE_TIME_DATA_SIZE_NUM-1;
		/* End of if */

		/* we only care about tx time for header + data body */
		TxTime = gAcmTxTimeBodyHT\
							[ACMR_IS_2040_STA(pCdb)][0][McsId][LenDataId][1];

		/* TODO: we need to minus the PHY preamble time */

#endif /* ACM_CC_FUNC_AUX_TX_TIME */

		/*
			Need to add Minimum MPDU Start Spacing, suppose tMMSS = 16us.
			(0 ~ 16us)

			An HT STA shall not start the transmission of more than one MPDU
			within the time limit described in the Minimum MPDU Start Spacing
			field.

			To satisfy this requirement, the number of octets between the start
			of two consecutive MPDUs in an A-MPDU, measured at the PHY SAP,
			shall be equal or greater than:

			tMMSS * r / 8, where r = PHY Data Rate (unit: Mbps)
		*/
		TxTime += 16; /* use maximum value */

		/* maybe we can make up a AMPDU from the WLAN hardware */
		pStream->TxAmpduNumEnqueueHT ++;
	} /* End of if */

#ifdef WMM_ACM_PKT_NUM_DEBUG
	if ((NumOfBaWinSize != 0) &&
		(WMM_ACM_TimeRecId == 5) &&
		(WMM_ACM_IsTimeValid == TRUE))
	{
		WMM_ACM_TimeOffsetHT[WMM_ACM_TimeOffsetHTId][3] = TxTime;
		WMM_ACM_TimeOffsetHTId ++;
	} /* End of if */
#endif /* WMM_ACM_PKT_NUM_DEBUG */

	return TxTime;
} /* End of ACM_TX_TimeCalOnFlyHT */
#endif /* ACM_CC_FUNC_11N */


/*
========================================================================
Routine Description:
	Pre-Calculate the QoS packet transmission time.

Arguments:
	pAd				- WLAN control block pointer

Return Value:
	None

Note:
	All possible tx time are kept in a local array.
	[0] duration id [1] cts-self tx time [2] rts/cts tx time
	[3] header time [4] ack tx time

	Do NOT include "data body tx time".

	For HT packet, suppose that
	1. RTS/CTS (11M) for each packet
	2. BLOCK ACK (11M) for each packet
========================================================================
*/
VOID ACM_TX_TimeCalPre(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd)
{
#ifndef ACM_CC_FUNC_AUX_TX_TIME
	UINT32 RateMap[ACM_RATE_MAX_NUM][3] = {
					{ ACM_RATE_1M,   0, ACM_RATE_ID_1M },
					{ ACM_RATE_2M,   0, ACM_RATE_ID_2M },
					{ ACM_RATE_5_5M, 0, ACM_RATE_ID_5_5M },
					{ ACM_RATE_11M,  0, ACM_RATE_ID_11M },
					{ ACM_RATE_6M,   1, ACM_RATE_ID_6M },
					{ ACM_RATE_9M,   1, ACM_RATE_ID_9M },
					{ ACM_RATE_12M,  1, ACM_RATE_ID_12M },
					{ ACM_RATE_18M,  1, ACM_RATE_ID_18M },
					{ ACM_RATE_24M,  1, ACM_RATE_ID_24M },
					{ ACM_RATE_36M,  1, ACM_RATE_ID_36M },
					{ ACM_RATE_48M,  1, ACM_RATE_ID_48M },
					{ ACM_RATE_54M,  1, ACM_RATE_ID_54M }};
	UINT32 TimeHeader, TimeCtsSelf, TimeRtsCts, TimeAck;
	UINT32 TimeDataAck;
	UINT32 IdRateNum, IdSizeNum, IdPreambleNum, Size;

#ifdef ACM_CC_FUNC_11N
	UINT32 TimeDatHdrOnly, TimeDataOnly;
	UINT32 IdBw, IdGI;
#endif /* ACM_CC_FUNC_11N */


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* for all rates */
	ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> calculate frame body tx time...\n"));

	for(IdRateNum=0; IdRateNum<ACM_RATE_MAX_NUM; IdRateNum++)
	{
		Size = ACM_PRE_TIME_DATA_SIZE_INTERVAL-1;

		for(IdSizeNum=0; IdSizeNum<ACM_PRE_TIME_DATA_SIZE_NUM; IdSizeNum++)
		{
			TimeDataAck = ACM_TX_TimeCal(pAd, NULL,
										Size,					/* body len */
										RateMap[IdRateNum][0],	/* RateIndex */
										RateMap[IdRateNum][1],	/* g mode */
										0,						/* cts-self */
										0,				/* rts/cts */
										0,				/* short IdPreambleNum */
										1,				/* no ack */
										0,				/* txop limit */
										NULL,			/* no data tx time */
										&TimeHeader,	/* wlan header tx time */
										NULL,			/* cts self tx time */
										NULL,			/* rts/cts tx time */
										NULL);			/* ack tx time */
			TimeDataAck -= TimeHeader;
			gAcmTxTimeBody[IdRateNum][IdSizeNum] = TimeDataAck;
			Size += ACM_PRE_TIME_DATA_SIZE_INTERVAL;
		} /* End of for */
	} /* End of for */

#ifdef ACM_CC_FUNC_11N
	for(IdBw=0; IdBw<2; IdBw++)
	{
		for(IdGI=0; IdGI<2; IdGI++)
		{
			for(IdRateNum=0; IdRateNum<ACM_RATE_MAX_NUM_HT; IdRateNum++)
			{
				Size = ACM_PRE_TIME_DATA_SIZE_INTERVAL-1;

				for(IdSizeNum=0;
					IdSizeNum<ACM_PRE_TIME_DATA_SIZE_NUM;
					IdSizeNum++)
				{
					/* not include RTS/CTS time */
					TimeDataAck = ACM_TX_TimeCalHT(pAd, NULL,
										Size,			/* body len */
										IdRateNum,		/* MCS Index */
										IdBw,			/* 20 or 20/40 MHz */
										IdGI,			/* regular or short GI */
										0,				/* rts/cts */
										1,				/* no ack */
										0,				/* not AMPDU */
										0,				/* txop limit */
										0,				/* do not care */
										0,				/* do not care */
										NULL,			/* no data tx time */
										NULL,			/* wlan header tx time */
										NULL,			/* ack tx time */
										&TimeDatHdrOnly,/* data+hdr only tx time */
										&TimeDataOnly);	/* data only tx time */

					gAcmTxTimeBodyHT\
						[IdBw][IdGI][IdRateNum][IdSizeNum][0] = TimeDataAck;
					gAcmTxTimeBodyHT\
						[IdBw][IdGI][IdRateNum][IdSizeNum][1] = TimeDatHdrOnly;
					gAcmTxTimeBodyHT\
						[IdBw][IdGI][IdRateNum][IdSizeNum][2] = TimeDataOnly;

					Size += ACM_PRE_TIME_DATA_SIZE_INTERVAL;
				} /* End of for */
			} /* End of for */
		} /* End of for */
	} /* End of for */
#endif /* ACM_CC_FUNC_11N */

	ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> calculate other tx time...\n"));

	for(IdRateNum=0; IdRateNum<ACM_RATE_MAX_NUM; IdRateNum++)
	{
		/* short IdPreambleNum (0) and long IdPreambleNum (1) */
		for(IdPreambleNum=0; IdPreambleNum<2; IdPreambleNum++)
		{
			ACM_TX_TimeCal(pAd, NULL,
							1,						/* BodyLen = 1 */
							RateMap[IdRateNum][0],	/* RateIndex */
							RateMap[IdRateNum][1],	/* g mode */
							1,						/* cts-self */
							0,						/* rts/cts */
							IdPreambleNum,			/* IdPreambleNum */
							0,						/* no ack */
							0,						/* txop limit */
							NULL,					/* no data tx time */
							&TimeHeader,			/* wlan header tx time */
							&TimeCtsSelf,			/* cts self tx time */
							NULL,					/* rts/cts tx time */
							&TimeAck);				/* ack tx time */

			ACM_TX_TimeCal(pAd, NULL,
							0,						/* BodyLen = 0 */
							RateMap[IdRateNum][0],	/* RateIndex */
							RateMap[IdRateNum][1],	/* g mode */
							0,						/* cts-self */
							1,						/* rts/cts */
							IdPreambleNum,			/* IdPreambleNum */
							1,						/* no ack */
							0,						/* txop limit */
							NULL,					/* no data tx time */
							NULL,					/* wlan header tx time */
							NULL,					/* cts self tx time */
							&TimeRtsCts,			/* rts/cts tx time */
							NULL);					/* ack tx time */

			gAcmTxTimeOthers[IdRateNum][IdPreambleNum][0] = RateMap[IdRateNum][2];
			gAcmTxTimeOthers[IdRateNum][IdPreambleNum][1] = (UINT16)TimeCtsSelf;
			gAcmTxTimeOthers[IdRateNum][IdPreambleNum][2] = (UINT16)TimeRtsCts;
			gAcmTxTimeOthers[IdRateNum][IdPreambleNum][3] = (UINT16)TimeHeader;
			gAcmTxTimeOthers[IdRateNum][IdPreambleNum][4] = (UINT16)TimeAck;
		} /* End of for */
	} /* End of for */

#ifdef ACM_CC_FUNC_11N
	/* only for BLOCK ACK frame, 24Mbps */
	TimeDataAck = ACM_TX_TimeCalHT(pAd, NULL,
						FRM_LENGTH_BLOCK_ACK,		/* body len */
						0,							/* MCS Index */
						0,							/* 20 or 20/40 MHz */
						0,							/* regular or short GI */
						0,							/* rts/cts */
						0,							/* no ack */
						0,							/* not AMPDU */
						0,							/* txop limit */
						0,							/* do not care */
						0,							/* do not care */
						NULL,						/* no data tx time */
						NULL,						/* wlan header tx time */
						&TimeAck,					/* ack tx time */
						NULL,						/* data+hdr only tx time */
						NULL);						/* data only tx time */

	gAcmTxTimeOthersHT = (UINT16)TimeAck;
#endif /* ACM_CC_FUNC_11N */

	ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> pre-calculattion ok!\n"));
#endif /* ACM_CC_FUNC_AUX_TX_TIME */
} /* End of ACM_TX_TimeCalPre */


/*
========================================================================
Routine Description:
	Calculate the frame body transmission time.

Arguments:
	BodyLen			- frame body
	RateId			- frame tx time
	FlgIsGmode		- is G mode

Return Value:
	transmission time (miscro second, us)

Note:
========================================================================
*/
STATIC UINT16 ACM_TX_TimePlcpCal(
	ACM_PARAM_IN	UINT32	BodyLen,
	ACM_PARAM_IN	UINT32	RateId,
	ACM_PARAM_IN	UCHAR	FlgIsGmode)
{
	UINT32 PLCP;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	if (FlgIsGmode)
	{
#ifdef RELEASE_EXCLUDE
		/*
			EX1: BodyLen = 30B and rate = 54Mbps,
			1. additional 22 bit in PSDU
				PLCP = 30*8 + 22 = 262 bits
			2. round_up{X / 4} * 4 means OFDM symbol is in unit of 4 usec
				PLCP = (262/54) = 4.8xxx us
				4.8xxx / 4 = 1.2xxx
			3. PLCP = round up(1.2xxx) * 4 = 2 * 4 = 8 us

			EX2: BodyLen = 14B and rate = 6Mbps,
			1. additional 22 bit in PSDU
				PLCP = 14*8 + 22 = 134 bits
			2. round_up{X / 4} * 4 means OFDM symbol is in unit of 4 usec
				PLCP = (134/6) = 22.3xxx us
				22.3xxx / 4 = 5.583xxx
			3. PLCP = round up(5.583xxx) * 4 = 6 * 4 = 24 us
		*/
#endif /* RELEASE_EXCLUDE */
		PLCP = (BodyLen << 3) + 22; /* need to add 22 bits in 11g */
		PLCP = (PLCP % (gAcmRateG[RateId]<<1)) ? (PLCP/(gAcmRateG[RateId]<<1))+1 :
											(PLCP/(gAcmRateG[RateId]<<1));
		return (PLCP << 2);
	} /* End of if */

#ifdef RELEASE_EXCLUDE
	/* ex: BodyLen = 30B and rate = 11Mbps,
		PLCP = 30 * 8 / 11 = 22us */
#endif /* RELEASE_EXCLUDE */
	if (RateId > ACM_RATE_ID_1M)
		RateId = ACM_RATE_ID_1M; /* should not be here */
	/* End of if */

	return (BodyLen << 4)/gAcmRateLegacy[RateId];
} /* End of ACM_TX_TimePlcpCal */


#ifdef ACM_CC_FUNC_11N
/*
========================================================================
Routine Description:
	Calculate the frame body transmission time for HT rate.

Arguments:
	BodyLen			- frame body
	McsId			- frame tx MCS
	Nss				- Number of Spatial Streams
	Ness			- Number of Extension Spatial Streams
	FlgIsGF			- 1: Greenfield mode
	FlgIs2040		- 1: support 20/40MHz
	FlgIsSGI		- 1: use short GI
	FlgIsOnlyData	- 1: only calcualte tx time for data, no preamble

Return Value:
	transmission time (miscro second, us)

Note:
========================================================================
*/
STATIC UINT16 ACM_TX_TimePlcpCalHT(
	ACM_PARAM_IN	UINT32	BodyLen,
	ACM_PARAM_IN	UINT32	McsId,
	ACM_PARAM_IN	UINT32	Nss,
	ACM_PARAM_IN	UINT32	Ness,
	ACM_PARAM_IN	BOOLEAN FlgIsGF,
	ACM_PARAM_IN	BOOLEAN	FlgIs2040,
	ACM_PARAM_IN	BOOLEAN	FlgIsSGI,
	ACM_PARAM_IN	BOOLEAN	FlgIsOnlyData)
{
#ifdef RELEASE_EXCLUDE
	/*
		Reference to Draft802.11n_D3.07, Transmission Time =

		1. Mix mode, short GI

			TXTIME =	T_LEG_PREAMBLE + T_L_SIG + T_HT_PREAMBLE + T_HT_SIG +
						T_SYM * Ceiling{T_SYMS * N_SYM / T_SYM}

		2. Mix mode, regular GI

			TXTIME =	T_LEG_PREAMBLE + T_L_SIG + T_HT_PREAMBLE + T_HT_SIG +
						T_SYM * N_SYM

		3. GreenField mode, short GI

			TXTIME =	T_GF_HT_PREAMBLE + T_HT_SIG + T_SYMS * N_SYM

		4. GreenField mode, regular GI

			TXTIME =	T_GF_HT_PREAMBLE + T_HT_SIG + T_SYM * N_SYM

		Where

		(1) T_LEG_PREAMBLE	= T_L_STF + T_L_LTF = 8 + 8 = 16 us
		(2) T_L_SIG			= 4 us
		(3) T_HT_PREAMBLE	= T_HT_STF + T_HT_LTF1 + (N_LTF - 1) * T_HT_LTFS
							= 4 + 4 + ((N_DLTF + N_ELTF) - 1) * 4
			EX: Nss = 2, N_DLTF = 2, Ness = 0, N_ELTF = 0, T_HT_PREAMBLE = 12 us
		(4) T_HT_SIG		= 8 us
		(5) T_SYM			= 4 us
		(6) T_SYMS			= 3.6 us
		(7) N_SYM			= mSTBC * Ceil((8*len+16+6*N_ES)/(mSTBC * N_DBPS))
		(8) T_GF_HT_PREAMBLE= T_HT_GF_STF + T_HT_LTF1 + (N_LTF - 1) * T_HT_LTFS
							= 8 + 4 + ((N_DLTF + N_ELTF) - 1) * 4
	*/
#endif /* RELEASE_EXCLUDE */

	UINT32 T_LEG_PREAMBLE, T_L_SIG, T_HT_PREAMBLE, T_HT_SIG;
	UINT32 T_SYM, T_SYMS, N_SYM;
	UINT32 T_GF_HT_PREAMBLE;
	UINT32 TxTime;
	UINT32 N_DLTF[5] = { 1, 1, 2, 4, 4 };
	UINT32 N_ELTF[4] = { 0, 1, 2, 4 };
	UINT32 N_SYM_1_NUM; /* numerator of N_SYM */


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* init */
	T_LEG_PREAMBLE = 16;
	T_L_SIG = 4;
	T_HT_SIG = 8;
	T_SYM = 4;
	T_SYMS = 36; /* unit: 0.1us */
	TxTime = 0;

	/* calculate N_SYM */
	/* ex: 1538B, 1st MPDU of AMPDU, (1538 * 8 + 22)/1080 + 1 = 12 */
	/* STBC is not used, BCC is used */
	N_SYM = 1*((BodyLen << 3) + 16 + 6)/(1*gAcmRateNdbps[FlgIs2040][McsId])+1;

	/* calculate transmission time */
	if (FlgIsGF == 0)
	{
		/* ex: 1538B, 1st MPDU of AMPDU, 4 + 4 + 2*4 = 16us */
		T_HT_PREAMBLE = 4 + 4 + ((N_DLTF[Nss] + N_ELTF[Ness] - 1) << 2);

		/* ex: 1538B, 1st MPDU of AMPDU, 16 + 4 + 16 + 8 = 44us */
		if (FlgIsOnlyData == 0)
			TxTime = T_LEG_PREAMBLE + T_L_SIG + T_HT_PREAMBLE + T_HT_SIG;
		/* End of if */

		/* ex: 1538B, 1st MPDU of AMPDU, 4 * 12 = 48us */
		if (FlgIsSGI == 0)
		{
			TxTime += T_SYM * N_SYM;
		}
		else
		{
			N_SYM_1_NUM = (T_SYMS * N_SYM) / (T_SYM * 10);

			if ((T_SYMS * N_SYM) % (T_SYM * 10))
				N_SYM_1_NUM ++;
			/* End of if */

			TxTime += T_SYM * N_SYM_1_NUM;
		} /* End of if */
	}
	else
	{
		T_GF_HT_PREAMBLE = 8 + 4 + ((N_DLTF[Nss] + N_ELTF[Ness] - 1) << 2);

		if (FlgIsOnlyData == 0)
			TxTime = T_GF_HT_PREAMBLE + T_HT_SIG;
		/* End of if */

		if (FlgIsSGI == 0)
			TxTime += T_SYM * N_SYM;
		else
			TxTime += (T_SYMS * N_SYM) / 10;
		/* End of if */
	} /* End of if */

	return TxTime;
} /* End of ACM_TX_TimePlcpCalHT */
#endif /* ACM_CC_FUNC_11N */




/* =========================== Information Function ================== */
/*
========================================================================
Routine Description:
	Get TXOP information.

Arguments:
	pAd				- WLAN control block pointer
	AcId			- the AC ID

Return Value:
	TXOP

Note:
========================================================================
*/
UINT32 ACM_InfoTxopBssGet(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd,
	ACM_PARAM_IN	UINT32				AcId)
{
#ifdef CONFIG_AP_SUPPORT
	if (ACMR_IS_AP_MODE(pAd))
		return ACMR_AP_TXOP_BSS_GET(pAd, AcId);
	/* End of if */
#endif /* CONFIG_AP_SUPPORT */

#ifdef CONFIG_STA_SUPPORT
	if (ACMR_IS_STA_MODE(pAd))
		return ACMR_STA_TXOP_BSS_GET(pAd, AcId);
	/* End of if */
#endif /* CONFIG_STA_SUPPORT */

	return 0; /* impossible */
} /* End of ACM_InfoTxopBssGet */


/*
========================================================================
Routine Description:
	Get WMM information.

Arguments:
	pAd				- WLAN control block pointer

Return Value:
	TRUE			- WMM is enabled
	FALSE			- WMM is disabled

Note:
========================================================================
*/
BOOLEAN ACM_InfoWmmStatusGet(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd)
{
#ifdef CONFIG_AP_SUPPORT
	if (ACMR_IS_AP_MODE(pAd))
		return ACMR_AP_WMM_IS_ENABLED(pAd);
	/* End of if */
#endif /* CONFIG_AP_SUPPORT */

#ifdef CONFIG_STA_SUPPORT
	if (ACMR_IS_STA_MODE(pAd))
		return ACMR_STA_WMM_IS_ENABLED(pAd);
	/* End of if */
#endif /* CONFIG_STA_SUPPORT */

	return 0; /* impossible */
} /* End of ACM_InfoWmmStatusGet */


/*
========================================================================
Routine Description:
	Get AP BSSID information.

Arguments:
	pAd				- WLAN control block pointer

Return Value:
	TRUE			- WMM is enabled
	FALSE			- WMM is disabled

Note:
========================================================================
*/
UCHAR *ACM_InfoApAddrGet(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd)
{
#ifdef CONFIG_AP_SUPPORT
	if (ACMR_IS_AP_MODE(pAd))
		return pAd->CurrentAddress;
	/* End of if */
#endif /* CONFIG_AP_SUPPORT */

#ifdef CONFIG_STA_SUPPORT
	if (ACMR_IS_STA_MODE(pAd))
		return pAd->MlmeAux.Bssid;
	/* End of if */
#endif /* CONFIG_STA_SUPPORT */

	return 0; /* impossible */
} /* End of ACM_InfoApAddrGet */


/*
========================================================================
Routine Description:
	Get port secure information.

Arguments:
	pAd				- WLAN control block pointer

Return Value:
	TRUE			- port is secured
	FALSE			- port is not secured

Note:
========================================================================
*/
BOOLEAN ACM_InfoPortSecureGet(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd)
{
#ifdef CONFIG_AP_SUPPORT
	if (ACMR_IS_AP_MODE(pAd))
		return TRUE;
	/* End of if */
#endif /* CONFIG_AP_SUPPORT */

#ifdef CONFIG_STA_SUPPORT
	if (ACMR_IS_STA_MODE(pAd))
	{
		/* check if we have associated to a AP */
		return (pAd->IndicateMediaState == NdisMediaStateConnected);
	} /* End of if */
#endif /* CONFIG_STA_SUPPORT */

	return FALSE; /* impossible */
} /* End of ACM_InfoPortSecureGet */


/*
========================================================================
Routine Description:
	Get short preamble information.

Arguments:
	pAd				- WLAN control block pointer

Return Value:
	TRUE			- short preamble
	FALSE			- not short preamble

Note:
========================================================================
*/
BOOLEAN ACM_InfoShortPreambleGet(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd,
	ACM_PARAM_IN	ACMR_STA_DB			*pCdb)
{
#ifdef CONFIG_AP_SUPPORT
	if (ACMR_IS_AP_MODE(pAd))
		return (CAP_IS_SHORT_PREAMBLE_ON(pCdb->CapabilityInfo));
	/* End of if */
#endif /* CONFIG_AP_SUPPORT */

#ifdef CONFIG_STA_SUPPORT
	if (ACMR_IS_STA_MODE(pAd))
	{
		/* check if we have associated to a AP */
		return (pAd->CommonCfg.TxPreamble == Rt802_11PreambleShort);
	} /* End of if */
#endif /* CONFIG_STA_SUPPORT */

	return FALSE; /* impossible */
} /* End of ACM_InfoShortPreambleGet */


/*
========================================================================
Routine Description:
	Get channel busy time in a TBT.

Arguments:
	pAd				- WLAN control block pointer
	*pTime			- the busy time

Return Value:
	None

Note:
========================================================================
*/
VOID ACM_InfoChanBusyGet(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd,
	ACM_PARAM_IN	UINT32				*pTime)
{
#ifdef CONFIG_AP_SUPPORT
	if (ACMR_IS_AP_MODE(pAd))
		*pTime = pAd->QloadLatestChannelBusyTimePri;
	/* End of if */
#endif /* CONFIG_AP_SUPPORT */

#ifdef CONFIG_STA_SUPPORT
	if (ACMR_IS_STA_MODE(pAd))
		*pTime = 0;
	/* End of if */
#endif /* CONFIG_STA_SUPPORT */
} /* End of ACM_InfoChanBusyGet */




/* =========================== AP Function =========================== */

#ifdef CONFIG_AP_SUPPORT
/*
========================================================================
Routine Description:
	Handle the Action Frame transmitted from QSTA.

Arguments:
	pAd				- WLAN control block pointer
	*pCdb			- the source QSTA
	pMblk			- the received frame
	PhyRate			- the physical tx rate for the frame

Return Value:
	None

Note:
	1. Only for QAP.
	2. PhyRate is used to calculate medium time or polled TXOP
		when min phy rate in the TSPEC is not assigned.
	3. No QoS Control field (2B) in the action management frame.
	4. No any packet free here.
========================================================================
*/
STATIC VOID ACM_ActionHandleByQAP(
	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd,
	ACM_PARAM_IN	ACMR_STA_DB         *pCdb,
	ACM_PARAM_IN	UCHAR				*pMblk,
	ACM_PARAM_IN	UINT32				PktLen,
	ACM_PARAM_IN	UINT32				PhyRate)
{
	ACM_WME_NOT_FRAME *pNotFrame;
	UCHAR *pActFrame;
	UCHAR Category, Action;
	UINT32 BodyLen;
	UCHAR StatusCode;
	UINT16 MediumTime;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* points to the action frame WLAN header */
	pActFrame = (UCHAR *)pMblk;
	BodyLen = PktLen - ACMR_FME_LEG_HEADER_SIZE;

	/* get Category & Action field */
	pActFrame += ACMR_FME_LEG_HEADER_SIZE;
	Category = *pActFrame;
	Action = *(pActFrame+1);

	/* sanity check for peer */
#ifdef ACM_CC_FUNC_MBSS
	if ((Action != ACM_ACTION_WME_BW_ANN) && (pCdb == NULL))
#else
	if (pCdb == NULL)
#endif /* ACM_CC_FUNC_MBSS */
		goto label_exit; /* wrong packet source */
	/* End of if */

	/* handle it by Category field */
	if (Category == ACM_CATEGORY_WME)
	{
#ifdef ACM_CC_FUNC_MBSS
		if (Action != ACM_ACTION_WME_BW_ANN)
#endif /* ACM_CC_FUNC_MBSS */
		{
			if (!ACMR_IS_WMM_STA(pCdb))
			{
				ACMR_DEBUG(ACMR_DEBUG_ERR,
							("acm_err> A WME frame is RCV from a non-WME STA! "
							"Discard the frame! ActionHandleByQAP()\n"));
				goto label_exit; /* wrong packet type */
			} /* End of if */
		} /* End of if */

		/* WME action frame */
		switch(Action)
		{
			case ACM_ACTION_WME_SETUP_REQ: /* ADDTS Request */
				ACMR_DEBUG(ACMR_DEBUG_TRACE,
						("acm_msg> A WME Setup Request frame is received!\n"));

				ACM_WME_ActionHandle(pAd,
									pCdb,
									pActFrame,
									BodyLen,
									PhyRate,
									ACM_ACTION_WME_SETUP_REQ,
									&StatusCode,
									&MediumTime);
				break;

			case ACM_ACTION_WME_TEAR_DOWN: /* DELTS */
				ACMR_DEBUG(ACMR_DEBUG_TRACE,
						("acm_msg> A WME Teardown frame is received!\n"));

				ACM_WME_ActionHandle(pAd,
									pCdb,
									pActFrame,
									BodyLen,
									0, /* dont care phy rate */
									ACM_ACTION_WME_TEAR_DOWN,
									&StatusCode,
									&MediumTime);
				break;

#ifdef ACM_CC_FUNC_MBSS
			case ACM_ACTION_WME_BW_ANN: /* BW ANN */
				ACMR_DEBUG(ACMR_DEBUG_TRACE,
						("acm_msg> A WME BW ANN frame is received!\n"));

				ACM_MBSS_BwAnnHandle(pAd, pActFrame, BodyLen);

				/* not forward the public frame to other BSSs to avoid loop */
				goto label_exit;
#endif /* ACM_CC_FUNC_MBSS */

			default:
				goto label_exit;
		} /* End of switch */

		/* check whether we need to reply a ADDTS Response frame;
			if TRUE, send out one */
		if (Action == ACM_ACTION_WME_SETUP_REQ)
		{
			/* if the ACM of the AC is disabled, we do not need to response */
			if (StatusCode != ACM_STATUS_CODE_PRIVATE_ACM_DISABLED)
			{
				UCHAR MacRa[6], MacTa[6];


				ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> prepare to response...\n"));

				/* init response frame */
				StatusCode = ACM_11E_WMM_StatusTranslate(StatusCode);

				pNotFrame = (ACM_WME_NOT_FRAME *)pActFrame;
				pNotFrame->Action = ACM_ACTION_WME_SETUP_RSP;
				pNotFrame->StatusCode = StatusCode;

				/*
					Test Event 1 found:
					we need to re-assign the allowed medium time.
				*/
				pNotFrame->ElmTspec.Tspec.MediumTime = MediumTime;

				/* swap DA & SA */
				ACMR_WLAN_PKT_RA_GET(pMblk, MacRa);
				ACMR_WLAN_PKT_TA_GET(pMblk, MacTa);
				ACMR_WLAN_PKT_RA_SET(pMblk, MacTa);
				ACMR_WLAN_PKT_TA_SET(pMblk, MacRa);

				/* send out it */
				ACM_ADDRSP_SEND(pAd, pMblk, PktLen);

				/* only test use */
				if (gAcmTestFlag)
					ACM_ADDRSP_SEND(pAd, pMblk, PktLen);
				/* End of if */

				ACMR_DEBUG(ACMR_DEBUG_TRACE,
						("acm_msg> Send a WME Setup response (len=%d).\n", PktLen));
			} /* End of if */
		} /* End of if */

		/* send a private public ACTION frame to advise used ACM time in AP */
		ACMP_FrameBwAnnSend(pAd, FALSE);
	} /* End of if */

label_exit:
	return;
} /* End of ACM_ActionHandleByQAP */


/*
========================================================================
Routine Description:
	Handle a TSPEC request from the QSTA.

Arguments:
	pAd						- WLAN control block pointer
	*pCdb					- the QSTA database
	StreamType				- the stream type: ACM_STREAM_TYPE_WIFI
	DialogToken				- the TSPEC ID
	*pTspec					- the requested TSPEC pointer
	TclasNum				- the number of TCLASS, max 5
	*pTclas[]				- the requested TCLASS array pointer
	TclasProcessing			- the TCLAS Processing element
	PhyRate					- the observed physical transmit rate (bps)
	*pStatusCode			- response status code
	*pMediumTime			- the allowed medium time

Return Value:
	ACM_RTN_OK				- request is accepted
	ACM_RTN_FAIL			- semaphore lock fail or others
	ACM_RTN_NULL_POINTER	- null pointer
	ACM_RTN_ALLOC_ERR		- TSPEC request structure allocation fail
	ACM_RTN_DISALLOW		- the request is not allowed

Note:
	1. Only for Root AP Mode.
	2. pTclasSrc is limited by ACM_TSPEC_TCLAS_MAX_NUM.
	3. DLP is not allowed because we can not monitor traffic
		on the direct link when inactivity timeout != 0.
		We allow DLP protocol but we dont allow DLP TSPEC.
	4. for uplink, dst_cdb_p == NULL means the destination STA is QAP;
		for direct link, dst_cdb must NOT be NULL;
		for dnlink, dst_cdb_p == NULL;
		for bidirectional link, dst_cdb_p = NULL.
	5. TSPEC parameters that must be non-zero to pass in ADDTS Rsp:
		(1) Mean Data Rate;
		(2) Nominal MSDU Size;
		(3) Maximum Service Interval;
		(4) Minimum PHY Rate.
	6. TCLAS number can be 0 for EDCA/WMM streams.
========================================================================
*/
STATIC ACM_FUNC_STATUS ACM_TC_ReqHandle(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC	pAd,
	ACM_PARAM_IN	ACMR_STA_DB			*pCdb,
	ACM_PARAM_IN	UCHAR				StreamType,
	ACM_PARAM_IN	UINT16				DialogToken,
	ACM_PARAM_IN	ACM_TSPEC			*pTspec,
	ACM_PARAM_IN	UINT32				TclasNum,
	ACM_PARAM_IN	ACM_TCLAS			*pTclas[],
	ACM_PARAM_IN	UCHAR				TclasProcessing,
	ACM_PARAM_IN	UINT32				PhyRate,
	ACM_PARAM_OUT	UCHAR				*pStatusCode,
	ACM_PARAM_OUT	UINT16				*pMediumTime)
{
	ACM_STREAM *pNewStream;
	ACM_STREAM *pOldStreamIn, *pOldStreamOut, *pOldStreamDiffAc;
	ACM_FUNC_STATUS RtnCode, Status;
	UCHAR StatusCode;
	UCHAR Policy, UserPriority;
	UCHAR StmAcId, ApsdAcId, FlgIsApsdEnable;
	UCHAR FlgIsSupRate;
	ULONG SplFlags;
#ifdef ACM_CC_FUNC_TCLAS
	UINT32 IdTclasNum;
#endif /* ACM_CC_FUNC_TCLAS */
#ifdef ACM_CC_FUNC_HCCA
	UINT32 MinServInt;
#endif /* ACM_CC_FUNC_HCCA */
	UCHAR FlgAcmStatus[ACM_DEV_NUM_OF_AC];


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* ---- Sanity check for input parameters ---- */
	*pStatusCode = ACM_STATUS_CODE_UNSPECIFIED_FAILURE;

	if (pTspec == NULL)
	{
		/* TSPEC element shall NOT be NULL */
		return ACM_RTN_NULL_POINTER;
	} /* End of if */

	ACM_TSPEC_SEM_LOCK_CHK_RTN(pAd, SplFlags, LabelSemErr, ACM_RTN_FAIL);

	if (!ACM_MR_TSPEC_IS_ALLOWED(pAd))
	{
		ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);
		ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> ACM is not allowed!\n"));
		return ACM_RTN_DISALLOW;
	} /* End of if */

#ifdef ACM_CC_FUNC_ACL
	/* ACL check */
	if (ACM_MR_ACL_IS_ENABLED(pAd))
	{
		BOOLEAN FlgIsAllowed;

		ACM_ACL_ENTRY_GET(pAd, ACMR_CLIENT_MAC(pCdb), FlgIsAllowed, NULL);
		if (FlgIsAllowed == FALSE)
		{
			/* not in ACL list so we reject it */
			ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);
			return ACM_RTN_DISALLOW;
		} /* End of if */
	}
#endif /* ACM_CC_FUNC_ACL */

	ACMR_MEM_COPY(FlgAcmStatus, ACMR_CB->EdcaCtrlParam.FlgAcmStatus,
					sizeof(FlgAcmStatus));

	ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);

	/* init */
	pOldStreamIn = NULL;
	pOldStreamOut = NULL;
	pOldStreamDiffAc = NULL;
	RtnCode = ACM_RTN_FAIL;
	StatusCode = ACM_STATUS_CODE_UNSPECIFIED_FAILURE;
	ApsdAcId = 0;
	FlgIsApsdEnable = 0;
	*pMediumTime = 0;
	FlgIsSupRate = 0;

	/* allocate a TSPEC request TS */
	ACMR_MEM_ALLOC(pNewStream, sizeof(ACM_STREAM), (ACM_STREAM *));

	if (pNewStream == NULL)
		return ACM_RTN_ALLOC_ERR;
	/* End of if */

	ACMR_MEM_ZERO(pNewStream, sizeof(ACM_STREAM));

	/* init client database pointer, Dialog Token, TSPEC, TCLASS, and
		TCLASS Processing */
	ACM_STREAM_CDB_COPY(pNewStream, pCdb);
	pNewStream->DialogToken = DialogToken;
	pNewStream->StreamType = StreamType;

	/* allocate/copy a TSPEC */
	ACMR_MEM_ALLOC(pNewStream->pTspec, sizeof(ACM_TSPEC), (ACM_TSPEC *));

	if (pNewStream->pTspec == NULL)
		goto LabelErrAlloc;
	/* End of if */

	ACM_TSPEC_COPY(pNewStream->pTspec, pTspec);

#ifdef ACM_CC_FUNC_TCLAS
	/* continue to check input TSPEC parameters */
	if ((TclasNum > 1) && (TclasProcessing == ACM_TCLAS_PROCESSING_NOT_EXIST))
	{
		/* ERR! no TCLAS Processing element is found when num of TCLAS >= 2 */
		ACMR_DEBUG(ACMR_DEBUG_TRACE,
					("acm_msg> No tclas processing element!\n"));
		StatusCode = ACM_STATUS_CODE_DECLINED;
		RtnCode = ACM_RTN_INVALID_PARAM;
		goto LabelRspErr;
	} /* End of if */
#endif /* ACM_CC_FUNC_TCLAS */

	if (pTspec->TsInfo.AccessPolicy != ACM_ACCESS_POLICY_EDCA)
	{
		/* ERR! we do NOT support HCCA */
		ACMR_DEBUG(ACMR_DEBUG_TRACE,
					("acm_msg> Access policy %d != EDCA %d!\n",
					pTspec->TsInfo.AccessPolicy,
					ACM_ACCESS_POLICY_EDCA));
		StatusCode = ACM_STATUS_CODE_DECLINED;
		RtnCode = ACM_RTN_INVALID_PARAM;
		goto LabelRspErr;
	} /* End of if */

#ifdef ACM_CC_FUNC_TCLAS
	if ((TclasProcessing != ACM_WME_TCLAS_PROCESSING_ALL) &&
		(TclasProcessing != ACM_WME_TCLAS_PROCESSING_ONE) &&
		(TclasProcessing != ACM_TCLAS_PROCESSING_NOT_EXIST))
	{
		/* ERR! invalid TCLAS Processing value in D6.0 */
		ACMR_DEBUG(ACMR_DEBUG_TRACE,
					("acm_msg> Wrong tclas process value!\n"));
		RtnCode = ACM_RTN_INVALID_PARAM;
		goto LabelRspErr;
	} /* End of if */
#endif /* ACM_CC_FUNC_TCLAS */


	/* ---- Create and init a stream structure ---- */

#ifdef RELEASE_EXCLUDE
	/* =====================================================================
		1. Specification Interval must be greater than or equal to
		Delay Bound;

		2. Service Interval must be less than or equal to Maximum Service
		Interval and greater than or equal to Minimum Service Interval;

		3. The total sum of maximum service durations of all service
		intervals within the specification interval must be greater than
		((MDR * SI)/NMS) * D, where MDR = Mean Data Rate,
		SI = Specification Interval, NMS = Nominal MSDU Size and
		D = the duration to transmit a MSDU of size equal to Nominal MSDU
		Size at Minimum PHY Rate.
	===================================================================== */
#endif /* RELEASE_EXCLUDE */

#ifdef ACM_CC_FUNC_TCLAS
	/* allocate/copy TCLAS */
	for(IdTclasNum=0; IdTclasNum<TclasNum; IdTclasNum++)
	{
		if (pTclas[IdTclasNum] != NULL)
		{
			ACMR_MEM_ALLOC(pNewStream->pTclas[IdTclasNum],
							sizeof(ACM_TCLAS), (ACM_TCLAS *));

			if (pNewStream->pTclas[IdTclasNum] == NULL)
			{
				RtnCode = ACM_RTN_ALLOC_ERR;
				goto LabelRspErr;
			} /* End of if */

			ACM_TCLAS_COPY(pNewStream->pTclas[IdTclasNum], pTclas[IdTclasNum]);
			continue;
		} /* End of if */

		/* OK! all TCLAS are copied */
		break;
	} /* End of for */
#endif /* ACM_CC_FUNC_TCLAS */

	/* init other parameters */
	pNewStream->TclasProcessing = TclasProcessing;
	pNewStream->TimeoutDelts = ACM_DELTS_TIMEOUT/ACM_TIMEOUT_CHECK_BASE;


	/* ---- Sanity check for TSPEC parameters ---- */
	/* check source STA */
	if ((ACMR_IS_QSTA(pCdb) == 0) && (!ACMR_IS_WMM_STA(pCdb)))
	{
		/* ERR! source STA is not a QSTA and not a WMM STA */
		StatusCode = ACM_STATUS_CODE_DECLINED;
		goto LabelRspErr;
	} /* End of if */

	/* check TCLSS parameters and get user priority */
	UserPriority = ACM_UP_UNKNOWN;

#ifdef ACM_CC_FUNC_TCLAS
	for(IdTclasNum=0; IdTclasNum<TclasNum; IdTclasNum++)
	{
		if (pTclas[IdTclasNum] != NULL)
		{
			/* check classifier type */
			if (pTclas[IdTclasNum]->ClassifierType > ACM_TCLAS_TYPE_MAX)
			{
				/* classifier type is illegal */
				StatusCode = ACM_STATUS_CODE_INVALID_PARAMETERS;
				goto LabelRspErr;
			} /* End of if */

			/* check user priority, priority for each TCLAS must be the same */
			if (UserPriority == ACM_UP_UNKNOWN)
				UserPriority = pTclas[IdTclasNum]->UserPriority;
			else
			{
				if (pTclas[IdTclasNum]->UserPriority != UserPriority)
				{
					/* user priority for all TCLASS element shall be the same */
					StatusCode = ACM_STATUS_CODE_INVALID_PARAMETERS;
					goto LabelRspErr;
				} /* End of if */
			} /* End of if */
		} /* End of if */
	} /* End of for */
#endif /* ACM_CC_FUNC_TCLAS */


	/* ---- Check for Direct Link TSPEC ---- */
	if (pTspec->TsInfo.Direction == ACM_DIRECTION_DIRECT_LINK)
	{
		/* DLP is not allowed */
		ACMR_DEBUG(ACMR_DEBUG_TRACE,
					("acm_msg> Direct Link is not allowed!\n"));
		StatusCode = ACM_STATUS_CODE_DIRECT_LINK_IS_NOT_ALLOWED;
		goto LabelRspErr;
	} /* End of if */


	/* ---- Update the UP ---- */
	if (UserPriority == ACM_UP_UNKNOWN)
	{
		/* use the UP in the TSPEC if no any TCLAS exists */
		UserPriority = pTspec->TsInfo.UP;
	} /* End of if */

	pNewStream->UP = UserPriority;

	ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> UP = %d\n", UserPriority));


	/* ---- non-ACM TSPEC check ---- */
	StmAcId = ACM_MR_EDCA_AC(UserPriority);

	/* we need to keep the TSPEC even it is a NULL TSPEC so mark the code */
#if 0
	if ((FlgAcmStatus[StmAcId] == ACM_FLG_FUNC_DISABLED)
#ifdef ACM_CC_FUNC_SPEC_CHANGE_TG
		/*
			WMM Spec changes for TSPECs on non-ACM ACs:

			Note that the Mean Data Rate field in a TSPEC request may be set
			to zero, indicating that no admitted time is being requested.
			This might be used, for example, to modify the U-APSD settings
			for the AC.
		*/
		|| (pTspec->MeanDataRate == 0)
#endif /* ACM_CC_FUNC_SPEC_CHANGE_TG */
		)
	{
		/* change PS mode only because ACM is disabled */

		/* reset to Legacy or UAPSD PS */
		ACM_APSD_Ctrl(pAd, pCdb, StmAcId,
						pNewStream->pTspec->TsInfo.Direction,
						1, pTspec->TsInfo.APSD);

#ifdef ACM_CC_FUNC_SPEC_CHANGE_TG
		if (pTspec->MeanDataRate == 0)
		{
			ACMR_DEBUG(ACMR_DEBUG_TRACE,
				("acm_msg> Mean Data Rate == 0! Only change PS mechanism!\n"));
		}
		else
#endif /* ACM_CC_FUNC_SPEC_CHANGE_TG */
			ACMR_DEBUG(ACMR_DEBUG_TRACE,
				("acm_msg> ACM is disabled! Only change PS mechanism!\n"));

		StatusCode = ACM_STATUS_CODE_SUCCESS;
		RtnCode = ACM_RTN_OK;
		goto LabelErrAlloc;
	} /* End of if */
#endif

	/* ---- Sanity check for TSPEC parameters (after non-ACM TSPEC) ---- */
	if (pTspec->MinPhyRate == 0)
	{
		/* use observed physical rate of the ADDTS request frame */
		pTspec->MinPhyRate = PhyRate;
	} /* End of if */

#ifdef ACM_CC_FUNC_TCLAS
	if ((pTspec->MinPhyRate == 0) ||
		(TclasNum > ACM_TSPEC_TCLAS_MAX_NUM))
#else
	if (pTspec->MinPhyRate == 0)
#endif /* ACM_CC_FUNC_TCLAS */
	{
		/* in WMM ACM WIFI test spec., min phy rate can NOT be 0 */
		ACMR_DEBUG(ACMR_DEBUG_TRACE,
					("acm_msg> min phy rate = 0 or tclas is too many!\n"));
		RtnCode = ACM_RTN_INVALID_PARAM;
		goto LabelRspErr;
	} /* End of if */

	ACM_TG_CMT_UAPSD_SETTING_ON_NON_ACM_AC;

	/* check if the TSPEC is for non-ACM AC */
#ifdef ACM_CC_FUNC_SPEC_CHANGE_TG
	if (FlgAcmStatus[StmAcId] != ACM_FLG_FUNC_DISABLED)
#endif /* ACM_CC_FUNC_SPEC_CHANGE_TG */
	{
		UINT32 MaxRate;


		ACM_TG_CMT_MIN_PHY_RATE;

		/* check min phy rate can not larger than maximum supported rate */
		ACMR_SUP_RATE_MAX_GET(pAd, pCdb, MaxRate);

		if (pTspec->MinPhyRate > MaxRate)
		{
			ACMR_DEBUG(ACMR_DEBUG_TRACE,
						("acm_msg> min phy rate %dbps > "
						"maximum supported rate %dbps!\n",
						pTspec->MinPhyRate, MaxRate));
			RtnCode = ACM_RTN_INVALID_PARAM;
			goto LabelRspErr;
		} /* End of if */

		/* check min phy rate must be one of supported rates non-11n */
#ifdef ACM_CC_FUNC_11N
		if (!ACMR_IS_11N_ONLY_SUPPORT(pAd, pCdb))
		/* if only 11n is supported, no need to check non-11n supported rate */
#endif /* ACM_CC_FUNC_11N */
		{
			ACMR_SUP_RATE_CHECK(pAd, pTspec->MinPhyRate, FlgIsSupRate);
		}

#ifdef ACM_CC_FUNC_11N_AGG
		/* set maximum BA Window Size */
		pNewStream->HT_BaWinSize = 64; /* default maximum */
		ACMR_STA_MAX_BACK_NUM_GET(pAd, pCdb, pNewStream->HT_BaWinSize);

		/* get packet aggregation information */
		if (pTspec->MaxBurstSize > 0)
		{
			if (pTspec->TsInfo.AckPolicy == ACM_ACK_POLICY_BLOCK)
			{
				/* double check the number of MPDUs */
				if (pTspec->TsInfo.Direction != ACM_DIRECTION_UP_LINK)
				{
					/* no need to check for uplink of QSTA */
					if (pTspec->MaxBurstSize > pNewStream->HT_BaWinSize)
						pTspec->MaxBurstSize = pNewStream->HT_BaWinSize;
					/* End of if */
				} /* End of if */

				ACMR_DEBUG(ACMR_DEBUG_TRACE,
						("acm_msg> number of MPDUs in a AMPDU = %d\n",
						ACMR_11N_AGG_NUM_OF_MSDUS_GET(pTspec->MaxBurstSize)));
			} /* End of if */

			/* maybe update modified MaxBurstSize for 11n */
			pNewStream->pTspec->MaxBurstSize = pTspec->MaxBurstSize;
		}
		else
		{
			/*
				If the TS Info Ack Policy field is set to the value for
				HT-immediate block acknowledgement, then the MaxBurstSize
				shall be larger than 0.
			*/
			if (pTspec->TsInfo.AckPolicy == ACM_ACK_POLICY_BLOCK)
				pTspec->TsInfo.AckPolicy = ACM_ACK_POLICY_NORMAL;
			/* End of if */
		} /* End of if */

		if (ACMR_IS_11N_SUPPORT(pAd) && ACMR_STA_IS_11N_SUPPORT(pCdb))
			;
		else
		{
			/* reset parameters for non-11n devices (AP or STA) */
			if (pTspec->TsInfo.AckPolicy == ACM_ACK_POLICY_BLOCK)
				pTspec->TsInfo.AckPolicy = ACM_ACK_POLICY_NORMAL;
			/* End of if */

			ACMR_DEBUG(ACMR_DEBUG_TRACE,
						("acm_msg> AP or STA is not 11n so reset "
						"AckPolicy to Normal ACK\n"));
		} /* End of if */
#endif /* ACM_CC_FUNC_11N_AGG */

		if (FlgIsSupRate == 0)
		{
#ifdef ACM_CC_FUNC_11N
			/* check if AP & STA support 11n mode */
			if (ACMR_IS_11N_SUPPORT(pAd) && ACMR_STA_IS_11N_SUPPORT(pCdb))
			{
				/* check min phy rate must be one of supported rates 11n */
				ACMR_SUP_RATE_CHECK_11N(pAd,
										pCdb,
										pTspec->MinPhyRate,
										FlgIsSupRate);
			} /* End of if */
#endif /* ACM_CC_FUNC_11N */

#if 0
			/*
				Too many vendors use non-supported minimum physical rate in
				Plugfest#6, so I disable the function to avoid our customers
				complains it in the future.
			*/
			if (FlgIsSupRate == 0)
			{
				/* not supported rate */
				ACMR_DEBUG(ACMR_DEBUG_TRACE,
							("acm_msg> min phy rate %d is not a supported rate!!!\n",
							pTspec->MinPhyRate));
				RtnCode = ACM_RTN_INVALID_PARAM;
				goto LabelRspErr;
			} /* End of if */
#endif /* 0 */
		} /* End of if */

		ACM_TG_CMT_B0_TS_INFO;

		/* check minimum TSPEC parameters, reference to Annex H.1 in D6.0 */
		Policy = pTspec->TsInfo.AccessPolicy;

		if (Policy == ACM_ACCESS_POLICY_EDCA)
		{
			/* Contention Based CBR Traffic, EDCA */
			if ((pTspec->NominalMsduSize == 0) ||
#ifndef ACM_CC_FUNC_SPEC_CHANGE_TG
				(pTspec->MeanDataRate == 0) ||
#endif /* ACM_CC_FUNC_SPEC_CHANGE_TG */
				(pTspec->SurplusBandwidthAllowance == 0) ||
				(pTspec->TsInfo.Aggregation != ACM_AGGREGATION_DISABLE) ||
				(pTspec->TsInfo.Schedule != ACM_SCHEDULE_NO))
			{
				/* some parameters are invalid */
				ACMR_DEBUG(ACMR_DEBUG_TRACE,
							("acm_msg> nominal msdu size = %d or "
							"mean data rate = %d or "
							"surplus bandwidth allowance = %d or "
							"aggregation = %d or "
							"schedule = %d!\n",
							pTspec->NominalMsduSize,
							pTspec->MeanDataRate,
							pTspec->SurplusBandwidthAllowance,
							pTspec->TsInfo.Aggregation,
							pTspec->TsInfo.Schedule));
				StatusCode = ACM_STATUS_CODE_INVALID_PARAMETERS;
				goto LabelRspErr;
			} /* End of if */
		}
		else
		{
			/* non-EDCA is not support */
			StatusCode = ACM_STATUS_CODE_INVALID_PARAMETERS;
			goto LabelRspErr;
		} /* End of if */

#ifdef ACM_CC_FUNC_HCCA
		/* check suspension interval */
		if ((pTspec->SuspensionInt != ACM_TSPEC_SUSPENSION_DISABLE) &&
			(pTspec->InactivityInt != ACM_TSPEC_INACTIVITY_DISABLE))
		{
			/* suspension and inactivity interval are enabled */
			if (pTspec->SuspensionInt >= pTspec->InactivityInt)
			{
				/* error: suspension interval > inactivity interval */
				StatusCode = ACM_STATUS_CODE_INVALID_PARAMETERS;
				goto LabelRspErr;
			} /* End of if */
		} /* End of if */

		/* check TS Info */
		if (pTspec->TsInfo.AckPolicy != ACM_ACK_POLICY_NORMAL)
		{
			ACM_TG_CMT_NO_ACK_POLICY;

			/*
				1. Block Ack state change is not supported for WMM ACM;
				2. No ACK is not supported for WMM ACM;
				3. No schedule is needed because no scheduled EDCA APSD
				is supported.
			*/
			StatusCode = ACM_STATUS_CODE_INVALID_PARAMETERS;
			goto LabelRspErr;
		} /* End of if */

		/* get QOS MIB database */
		if (pTspec->TsInfo.Aggregation == 1)
		{
			/* not support */
		} /* End of if */

		/* check service start time */
		if (pTspec->ServiceStartTime != 0)
		{
			/* not support */
		} /* End of if */

		/* check minimum service interval */
		if (pTspec->MinServInt != 0)
		{
			/* mininum service interval is enabled */
			if (pTspec->MinServInt < ACM_TSPEC_MIN_SERV_INT_LIMIT)
			{
				/* minimum service interval is too small */
				pTspec->MinServInt = ACM_TSPEC_MIN_SERV_INT_LIMIT;
				StatusCode = ACM_STATUS_CODE_SUGGESTED_TSPEC;
				goto LabelRspErr;
			} /* End of if */
		} /* End of if */

		if (pTspec->MinServInt > ACM_TSPEC_INACTIVITY_MIN)
			MinServInt = (pTspec->MinServInt<<1); /* for safe, *2 */
		else
			MinServInt = ACM_TSPEC_INACTIVITY_MIN;
		/* End of if */


		/* check minimum inactivity interval (must > minimum SI) */
		if (pTspec->InactivityInt != ACM_TSPEC_INACTIVITY_DISABLE)
		{
			/* inactivity interval is enabled */
			if (pTspec->InactivityInt < MinServInt)
			{
				/* inactivity interval is too small */
				pTspec->InactivityInt = MinServInt;
				StatusCode = ACM_STATUS_CODE_SUGGESTED_TSPEC;
				goto LabelRspErr;
			} /* End of if */
		} /* End of if */


		/* check minimum suspension interval */
		if (pTspec->SuspensionInt != ACM_TSPEC_SUSPENSION_DISABLE)
		{
			/* suspension interval is enabled */
			if (pTspec->SuspensionInt < ACM_TSPEC_SUSPENSION_MIN)
			{
				/* suspension interval is too small */
				pTspec->SuspensionInt = ACM_TSPEC_SUSPENSION_MIN;
				StatusCode = ACM_STATUS_CODE_SUGGESTED_TSPEC;
				goto LabelRspErr;
			} /* End of if */
		} /* End of if */
#endif /* ACM_CC_FUNC_HCCA */
	} /* End of if */


	/* ---- Handle TSPEC ---- */
/* #ifndef ACM_CC_FUNC_SPEC_CHANGE_TG */
	/* adjust min phy rate and get minimum phy mode & mcs */
	ACM_PacketPhyModeMCSSet(pAd, pNewStream);
/* #endif */

	if (pTspec->TsInfo.AccessPolicy == ACM_ACCESS_POLICY_EDCA)
	{
		/* no aggregation support for EDCA */
		if (pTspec->TsInfo.Aggregation == ACM_AGGREGATION_ENABLE)
		{
			pTspec->TsInfo.Aggregation = ACM_AGGREGATION_DISABLE;
			StatusCode = ACM_STATUS_CODE_SUGGESTED_TSPEC;
			goto LabelRspErr;
		} /* End of if */

		RtnCode = ACM_RTN_SEM_GET_ERR;

		/* semaphore protection */
		ACM_TSPEC_SEM_LOCK_CHK_RTN(pAd, SplFlags, LabelSemErr, ACM_RTN_FAIL);

		/* check if the request is a negotiated request */
		Status = ACM_TC_RenegotiationCheck(
											pAd,
											ACMR_CLIENT_MAC(pCdb),
											UserPriority,
											&pTspec->TsInfo,
											&pOldStreamIn,
											&pOldStreamOut,
											&pOldStreamDiffAc);

#ifdef ACM_CC_FUNC_REPLACE_RULE_TG
#ifdef RELEASE_EXCLUDE
		/* apply replacement check rule from WMM ACM Task Group */
		/* but some rules can be accepted in IEEE802.11e spec. */

		/*
			We can not accept a replacement TSPEC if
			1. same TID, but not same AC;
		*/
#endif /* RELEASE_EXCLUDE */

		if (Status == ACM_RTN_OK)
		{
			if (ACM_TC_ReplacementCheck(pAd,
										ACMR_CLIENT_MAC(pCdb),
										UserPriority,
										&pTspec->TsInfo) != ACM_RTN_OK)
			{
				ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);
				ACMR_DEBUG(ACMR_DEBUG_TRACE,
							("acm_msg> Reject the TSPEC due to "
							"replacement rule check!\n"));
				StatusCode = ACM_STATUS_CODE_INVALID_PARAMETERS;
				goto LabelRspErr;
			} /* End of if */
		} /* End of if */
#endif /* ACM_CC_FUNC_REPLACE_RULE_TG */

		switch(Status)
		{
			case ACM_RTN_FAIL:
				/* this is a new request */
				ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> new TSPEC!\n"));
				break;

			case ACM_RTN_OK:
				/* this is negotiated request */
				ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> same TSPEC!\n"));
				pNewStream->ReNegotiation = 1; /* this is a nego TSPEC */
				break;

			case ACM_RTN_FATAL_ERR:
			case ACM_RTN_RENO_IN_REQ_LIST:
			default:
				/*
					Only these TSPEC whose Status == DELETING will exist in
					the requested list, means the old one is in DELETING state,
					we do NOT handle any negotiated request for this one.
				*/
				StatusCode = ACM_STATUS_CODE_UNSPECIFIED_FAILURE;
				goto LabelErr;
		} /* End of switch */

		/*
			Assign TS ID:
			AcmAcId: for dnlink or bidirectional link, it is the physical TS
			or AC ID; otherwise it is the backup array ID.
		*/
		pNewStream->AcmAcId = ACM_MR_EDCA_AC(pNewStream->UP);

		/*
			Check ok so calculate needed medium time.

			Note: if no ACM is needed for the AC, ACM_EDCA_ReqHandle() will
			return error.
		*/
		StatusCode = ACM_EDCA_ReqHandle(pAd,
										pCdb,
										pNewStream,
										pOldStreamIn,
										pOldStreamOut,
										pOldStreamDiffAc);

		if (StatusCode == ACM_STATUS_CODE_SUCCESS)
		{
			/* add the device to the entry list */
			ACM_PeerDeviceAdd(pAd, ACMR_CLIENT_MAC(pCdb));

			/* get UAPSD parameters */
			ApsdAcId = pNewStream->AcmAcId;
			FlgIsApsdEnable = pTspec->TsInfo.APSD;

			/* assign return medium time */
			if (pTspec->TsInfo.Direction != ACM_DIRECTION_DOWN_LINK)
				*pMediumTime = pNewStream->pTspec->MediumTime;
			/* End of if */
		} /* End of if */

		if (StatusCode != ACM_STATUS_CODE_SUCCESS)
		{
			ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);
			goto LabelRspErr;
		} /* End of if */

		goto LabelOK;
	} /* End of if */


	/* ---- Handle non-EDCA TSPEC ---- */
	/* HCCA is not support */
	ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> HCCA is not supported!\n"));
	StatusCode = ACM_STATUS_CODE_DECLINED;
	RtnCode = ACM_RTN_DISALLOW;
	goto LabelRspErr;

LabelOK:
	/* active stream check timer for any stream */
	ACMR_TIMER_ENABLE(ACMR_CB->FlgStreamAliveCheckEnable,
						ACMR_CB->TimerStreamAliveCheck,
						ACM_STREAM_CHECK_OFFSET);

#ifdef RELEASE_EXCLUDE
	/*
		If Station is in PS mode, we need to reset APSD state after the queued
		ADDTS Response frame is sent to the PS station.
	*/
#endif /* RELEASE_EXCLUDE */
	if (ACMR_STA_IS_IN_ACTIVE_MODE(pCdb))
	{
		/* reset UAPSD state only in ACTIVE state */
		ACM_APSD_Ctrl(pAd, pCdb, ApsdAcId,
						pNewStream->pTspec->TsInfo.Direction,
						1, FlgIsApsdEnable);
	}
	else
	{
		/* mark a UAPSD unhandled flag for TSPEC */
		pNewStream->FlgUapsdHandleNeed = 1;
	} /* End of if */

	ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);

	/* response TSPEC to QSTA with code = success */
	*pStatusCode = ACM_STATUS_CODE_SUCCESS;

	ACMR_DEBUG(ACMR_DEBUG_TRACE,
				("\nacm_msg> A request is accepted (Status=%d)!\n",
                *pStatusCode));
	return ACM_RTN_OK;

LabelErr:
	ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);

LabelRspErr:
LabelSemErr:
	/* response TSPEC to QSTA with error code */
	ACMR_DEBUG(ACMR_DEBUG_TRACE,
				("acm_msg> A request is rejected (%d)!\n", StatusCode));

LabelErrAlloc:
	/* free local allocated memory */
	ACM_FREE_TS(pNewStream);
	*pStatusCode = StatusCode;
	return RtnCode;
} /* End of ACM_TC_ReqHandle */

#endif /* CONFIG_AP_SUPPORT */




#ifdef CONFIG_STA_SUPPORT
/* =========================== Station Function =========================== */

/*
========================================================================
Routine Description:
	Handle the Action Frame transmitted from QAP.

Arguments:
	pAd					- WLAN control block pointer
	*pCdb				- the source QSTA
	*pMblk				- the received frame
	PktLen				- the frame length

Return Value:
	None

Note:
========================================================================
*/
STATIC VOID ACM_ActionHandleByQSTA(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	ACMR_STA_DB				*pCdb,
	ACM_PARAM_IN	UCHAR					*pMblk,
	ACM_PARAM_IN	UINT32					PktLen)
{
	ACM_WME_NOT_FRAME *pNotFrame;
	UCHAR *pActFrame;
	UCHAR Category, Action;
	UINT32 BodyLen;
	UCHAR StatusCode;
	UINT16 MediumTime; /* no use */


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* points to the action frame WLAN header */
	pActFrame = (UCHAR *)pMblk;

	/* get Category & Action field */
	BodyLen = PktLen - ACMR_FME_LEG_HEADER_SIZE;
	pActFrame += ACMR_FME_LEG_HEADER_SIZE;
	Category = *pActFrame;
	Action = *(pActFrame+1);

	/* sanity check for peer */
#ifdef ACM_CC_FUNC_MBSS
	if ((Action != ACM_ACTION_WME_BW_ANN) && (pCdb == NULL))
#else
	if (pCdb == NULL)
#endif /* ACM_CC_FUNC_MBSS */
		goto label_exit; /* wrong packet source */
	/* End of if */

	/* handle by Category */
	if (Category == ACM_CATEGORY_WME)
	{
		/* WME action frame */
		switch(Action)
		{
			case ACM_ACTION_WME_SETUP_RSP: /* ADDTS Response */
				ACM_WME_ActionHandle(pAd,
									pCdb,
									pActFrame,
									BodyLen,
									0, /* dont care */
									ACM_ACTION_WME_SETUP_RSP,
									&StatusCode,
									&MediumTime);
				break;

			case ACM_ACTION_WME_TEAR_DOWN: /* DELTS */
				ACM_WME_ActionHandle(pAd,
									pCdb,
									pActFrame,
									BodyLen,
									0, /* dont care */
									ACM_ACTION_WME_TEAR_DOWN,
									&StatusCode,
									&MediumTime);
				break;

#ifdef ACM_CC_FUNC_MBSS
			case ACM_ACTION_WME_BW_ANN: /* BW ANN */
				ACMR_DEBUG(ACMR_DEBUG_TRACE,
							("acm_msg> A WME BW ANN frame is received!\n"));

				ACM_MBSS_BwAnnHandle(pAd, pActFrame, BodyLen);

				ACMR_DEBUG(ACMR_DEBUG_TRACE,
							("acm_msg> Forward the bandwidth announce...\n"));

				/* forward the public frame to other BSSs */
				ACM_MBSS_BwAnnForward(pAd, pMblk, PktLen);
				goto label_exit;
#endif /* ACM_CC_FUNC_MBSS */

			default:
				goto label_exit;
		} /* End of switch */

		/* check if we need to reply a DELTS frame; if TRUE, send out one */
		if (Action == ACM_ACTION_WME_SETUP_RSP)
		{
			StatusCode = ACM_11E_WMM_StatusTranslate(StatusCode);

			if (StatusCode == WLAN_STATUS_CODE_WME_REFUSED)
			{
				UCHAR MAC_RA[6], MAC_TA[6];

				/* init response frame */
				pNotFrame = (ACM_WME_NOT_FRAME *)pActFrame;
				pNotFrame->Action = ACM_ACTION_WME_TEAR_DOWN;
				pNotFrame->DialogToken = 0;
				pNotFrame->StatusCode = 0;

				/* swap DA & SA */
				ACMR_WLAN_PKT_RA_GET(pMblk, MAC_RA);
				ACMR_WLAN_PKT_TA_GET(pMblk, MAC_TA);
				ACMR_WLAN_PKT_RA_SET(pMblk, MAC_TA);
				ACMR_WLAN_PKT_TA_SET(pMblk, MAC_RA);

				/* send out it */
				ACMR_MGMT_PKT_TX(pAd, pMblk, PktLen);

				ACMR_DEBUG(ACMR_DEBUG_TRACE,
						("acm_msg> TX a DELTS frame.\n"));
			} /* End of if */
		} /* End of if */
	} /* End of if */

label_exit:
	return;
} /* End of ACM_ActionHandleByQSTA */


/*
========================================================================
Routine Description:
	Handle a TSPEC response from the QAP.

Arguments:
	pAd						- WLAN control block pointer
	*pCdb					- the client database
	DialogToken				- the TSPEC ID
	StatusCode				- the response status
	*pTspec					- the requested TSPEC pointer
	*pTsDelay				- the TS Delay element (no use)
	*pStatusCode			- response status code

Return Value:
	ACM_RTN_OK				- request is accepted
	ACM_RTN_FATAL_ERR		- error operation mode
	ACM_RTN_FAIL			- semaphore lock fail or others
	ACM_RTN_NULL_POINTER	- null pointer
	ACM_RTN_ALLOC_ERR		- TSPEC request structure allocation fail
	ACM_RTN_DELTS			- need to send a DELTS to the peer

Note:
	1. Only for Client Mode.
	2. DLP is not allowed.
	3. *pTspec can not be freed here.
========================================================================
*/
STATIC ACM_FUNC_STATUS ACM_TC_RspHandle(
 	ACM_PARAM_IN	ACMR_PWLAN_STRUC		pAd,
	ACM_PARAM_IN	ACMR_STA_DB				*pCdb,
	ACM_PARAM_IN	UCHAR					DialogToken,
	ACM_PARAM_IN	UCHAR					StatusCode,
	ACM_PARAM_IN	ACM_TSPEC				*pTspec,
	ACM_PARAM_IN	ACM_ELM_TS_DELAY		*pTsDelay,
	ACM_PARAM_OUT	UCHAR					*pStatusCode)
{
#define TC_LMR_STREAM_DESTROY(__pAd, __StmAcId, __pStreamReq)	\
	__StmAcId = __pStreamReq->AcmAcId;							\
	ACM_TC_Destroy(__pAd, __pStreamReq, 0);

	ACM_STREAM *pStreamReq, *pOldStreamIn, *pOldStreamOut, *pOldStreamDiffAc;
	ACM_FUNC_STATUS RtnCode, Status;
	UINT32 Direction, RetryCountOldSettings;
	UCHAR StmAcId, ApsdAcId, FlgIsApsdEnable;
	ULONG SplFlags;


	WMM_ACM_FUNC_NAME_PRINT("IN");

	/* init */
	*pStatusCode = ACM_STATUS_CODE_SUCCESS;
	RtnCode = ACM_RTN_FAIL;

	RetryCountOldSettings = 0xFFFFFFFF;
	ACMR_RETRY_GET(pAd, RetryCountOldSettings);

	/* semaphore protection */
	ACM_TSPEC_SEM_LOCK_CHK_RTN(pAd, SplFlags, LabelSemErr, ACM_RTN_FAIL);

	/* find its requested TSPEC in the requested list */
	pStreamReq = ACMR_CB->TspecListReq.pHead;

	while(pStreamReq != NULL)
	{
		if (pStreamReq->DialogToken == DialogToken)
			break; /* find it */
		/* End of if */

		pStreamReq = pStreamReq->pNext;
	} /* End of while */

	if (pStreamReq == NULL)
	{
		/* maybe error response Dialog Token, try to find the correct TSPEC */
		pStreamReq = ACM_TC_FindInReq(\
								pAd, ACMR_CLIENT_MAC(pCdb), &pTspec->TsInfo);
		if (pStreamReq != NULL)
			pStreamReq->Cause = TSPEC_CAUSE_REJ_INVALID_TOKEN;
		/* End of if */

		pStreamReq = ACM_TC_FindInPeer(\
								pAd, ACMR_CLIENT_MAC(pCdb), &pTspec->TsInfo);
		if (pStreamReq != NULL)
		{
			/* already exist in ACTIVE TSPECs */
			ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);

			ACMR_DEBUG(ACMR_DEBUG_TRACE,
						("acm_err> Find exist same active TSPEC. Skip the response. "
						"TC_RspHandle()\n"));
			goto LabelHandleOK;
		} /* End of if */

		ACMR_DEBUG(ACMR_DEBUG_TRACE,
					("acm_err> Can not find any request/active TSPEC! "
					"TC_RspHandle()\n"));

		pStreamReq = NULL;
		goto LabelHandleErr;
	} /* End of if */

	/* check if the status of our requested TSPEC is DELETING */
	if ((pStreamReq->Status == TSPEC_STATUS_REQ_DELETING) ||
		(pStreamReq->Status == TSPEC_STATUS_ACT_DELETING))
	{
		goto LabelHandleErr;
	} /* End of if */

	if ((pStreamReq->Status != TSPEC_STATUS_REQUEST) &&
		(pStreamReq->Status != TSPEC_STATUS_RENEGOTIATING))
	{
		/* must be new TSPEC request or TSPEC negotiate */
		goto LabelHandleErr;
	} /* End of if */

#ifdef ACM_CC_FUNC_SPEC_CHANGE_TG
#ifdef RELEASE_EXCLUDE
	/*
		WMM Spec changes for TSPECs on non-ACM ACs:

		1. Verify the following bits: TID (b4-b1), DIR (b6-b5), b7=1, b8=0,
			PSB (b10), UP (b13-b11)
		2. Reserved (b23 ~ b16, b9) are 0
		3. Verify b0 is identical to b0 in the TS_INFO field of the
			TSPEC in the ADDTS Request frame
		4. Verify b15-b14 are identical to b15-b14 in the TS_INFO field of the
			TSPEC in the ADDTS Request frame.
	*/
#endif /* RELEASE_EXCLUDE */

	if ((pTspec->TsInfo.AccessPolicy != ACM_ACCESS_POLICY_EDCA) ||
		(pTspec->TsInfo.Aggregation != ACM_AGGREGATION_DISABLE) ||
		(pTspec->TsInfo.Schedule != ACM_SCHEDULE_NO) ||
		(pTspec->TsInfo.TrafficType != pStreamReq->pTspec->TsInfo.TrafficType) ||
		(pTspec->TsInfo.AckPolicy != pStreamReq->pTspec->TsInfo.AckPolicy))
	{
		goto LabelHandleErr;
	} /* End of if */
#endif /* ACM_CC_FUNC_SPEC_CHANGE_TG */

	/* check the response status code */
	if (pStreamReq->StreamType == ACM_SM_TYPE_11E)
	{
		/* 11E ADDTS response */
		switch(StatusCode)
		{
			case ACM_STATUS_CODE_SUGGESTED_TSPEC:
				/* QAP modify our requested TSPEC */
				ACMR_DEBUG(ACMR_DEBUG_TRACE,
							("acm_err> QAP gives me a suggested TSPEC! "
							"TC_RspHandle()\n"));
				pStreamReq->Cause = TSPEC_CAUSE_SUGGESTED_TSPEC;
				goto LabelErr;

			case ACM_STATUS_CODE_DECLINED:
				/* QAP declines us */
				ACMR_DEBUG(ACMR_DEBUG_TRACE,
							("acm_err> QAP declines my request! "
							"TC_RspHandle()\n"));

				pStreamReq->Cause = TSPEC_CAUSE_REJECTED;
				goto LabelErr;

			case ACM_STATUS_CODE_SUCCESS:
				/* QAP accept us */
				ACMR_DEBUG(ACMR_DEBUG_TRACE,
							("acm_msg> QAP accepts my request! "
							"TC_RspHandle()\n"));
				break;

			default:
				/* other error status code */
				ACMR_DEBUG(ACMR_DEBUG_TRACE,
							("acm_msg> QAP rejects my request with unknown "
							"status! TC_RspHandle()\n"));
				pStreamReq->Cause = TSPEC_CAUSE_UNKNOWN_STATUS;
				goto LabelErr;
		} /* End of switch */
	}
	else
	{
		/* WMM ADDTS response */
		switch(StatusCode)
		{
			case ACM_STATUS_CODE_WMM_INVALID_PARAMETERS:
				/* QAP modify our requested TSPEC */
				ACMR_DEBUG(ACMR_DEBUG_TRACE,
							("acm_err> QAP refuses my request due to invalid "
							"parameters! TC_RspHandle()\n"));
				pStreamReq->Cause = TSPEC_CASUE_REJ_INVALID_PARAM;
				goto LabelErr;

			case ACM_STATUS_CODE_WMM_REFUSED:
				/* QAP declines us */
				ACMR_DEBUG(ACMR_DEBUG_TRACE,
							("acm_err> QAP refuses my request! "
							"TC_RspHandle()\n"));

				pStreamReq->Cause = TSPEC_CAUSE_REJECTED;
				goto LabelErr;

			case ACM_STATUS_CODE_WMM_SUCCESS:
				/* QAP accept us */
				ACMR_DEBUG(ACMR_DEBUG_TRACE,
							("acm_msg> QAP accepts my request! "
							"TC_RspHandle()\n"));
				break;

			default:
				/* other error status code */
				ACMR_DEBUG(ACMR_DEBUG_TRACE,
							("acm_msg> QAP rejects my request with unknown "
							"status! TC_RspHandle()\n"));
				pStreamReq->Cause = TSPEC_CAUSE_UNKNOWN_STATUS;
				goto LabelErr;
		} /* End of switch */
	} /* End of if */

	/* remove it from the "requested" list, not free it */
	ACM_TC_ReqRemove(pAd, pStreamReq);

	/* we need to keep the TSPEC even it is a NULL TSPEC */
#ifndef ACM_CC_FUNC_SPEC_CHANGE_TG
	/* check if ACM is needed for the AC */
	if (ACMR_CB->EdcaCtrlParam.FlgAcmStatus[ACM_MR_EDCA_AC(pStreamReq->UP)] == 0)
	{
		ApsdAcId = pStreamReq->AcmAcId;
		FlgIsApsdEnable = pStreamReq->pTspec->TsInfo.APSD;

		/* change PS mode only because ACM is disabled */
		ACMR_DEBUG(ACMR_DEBUG_TRACE, ("acm_msg> Only change PS mode!\n"));

		/* reset UAPSD state */
		ACM_APSD_Ctrl(pAd, pCdb, ApsdAcId,
						pStreamReq->pTspec->TsInfo.Direction,
						1, FlgIsApsdEnable);

		/* free the request */
		ACM_TC_Free(pAd, pStreamReq);

		ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);

		/* return power save right to system */
		ACMP_StaPsCtrlRightReturn(pAd);

		goto LabelHandleOK;
	} /* End of if */
#endif /* ACM_CC_FUNC_SPEC_CHANGE_TG */

	/* response status is SUCCESS so active the stream */
	if (pStreamReq->pTspec->TsInfo.AccessPolicy == ACM_ACCESS_POLICY_EDCA)
	{
		/* update the medium time allocated from QAP */
		pStreamReq->pTspec->MediumTime = pTspec->MediumTime;

		/* get old input or output TSPEC if exists */
		if (pStreamReq->ReNegotiation == 0)
		{
			/* new stream is accepted */
			pOldStreamIn = NULL;
			pOldStreamOut = NULL;
			pOldStreamDiffAc = NULL;
		}
		else
		{
			/* negotiated stream is accepted */
			Status = ACM_TC_RenegotiationCheck(pAd,
											ACMR_CLIENT_MAC(pCdb),
											pStreamReq->UP,
											&pStreamReq->pTspec->TsInfo,
											&pOldStreamIn,
											&pOldStreamOut,
											&pOldStreamDiffAc);
		} /* End of if */

		/* update stream status and inactivity time */
		pStreamReq->Status = TSPEC_STATUS_ACTIVE;
		pStreamReq->InactivityCur = pStreamReq->pTspec->InactivityInt;

		/* delete old TSPEC if exists */
		if (pOldStreamIn != NULL)
			ACM_TC_Discard(pAd, pOldStreamIn);
		/* End of if */

		if (pOldStreamOut != NULL)
			ACM_TC_Discard(pAd, pOldStreamOut);
		/* End of if */

		if (pOldStreamDiffAc != NULL)
			ACM_TC_Discard(pAd, pOldStreamDiffAc);
		/* End of if */

		/* active the new or negotiated stream */
		if (ACM_TC_Active(pAd, pStreamReq) != ACM_RTN_OK)
		{
			/* should be not here */
			*pStatusCode = ACM_STATUS_CODE_DECLINED;
			goto LabelErr;
		} /* End of if */

		/* update new used ACM time */
		Direction = pStreamReq->pTspec->TsInfo.Direction;

		if (pStreamReq->pTspec->MediumTime > 0)
		{
			ACM_EDCA_Param_ACM_Update(pAd, pStreamReq->AcmAcId,
										Direction,
										pStreamReq->UP,
										pStreamReq->pTspec->MediumTime<<5,
										0,
#ifdef RELEASE_EXCLUDE
										ACM_DATL_NO_BORROW,
#else
										0,
#endif /* RELEASE_EXCLUDE */
										0);

			/* update to CSR if needed */
			if ((Direction == ACM_DIRECTION_UP_LINK) ||
				(Direction == ACM_DIRECTION_DIRECT_LINK))
			{
				StmAcId = pStreamReq->AcmAcId;

				ACM_ASIC_ACM_Reset(pAd, StmAcId,
								ACMR_CB->EdcaCtrlParam.AcmOutTime[StmAcId]);
			} /* End of if */
		} /* End of if */

		/* backup peer device MAC */
		ACM_PeerDeviceAdd(pAd, ACMR_CLIENT_MAC(pCdb));
	}
	else
	{
		/* non-EDCA access policy is not supported */
		TC_LMR_STREAM_DESTROY(pAd, StmAcId, pStreamReq);
		goto LabelHandleErr;
	} /* End of if */

	/* calculate minimum phy mode & mcs to the pStreamReq */
	ACM_PacketPhyModeMCSSet(pAd, pStreamReq);

#ifdef ACM_CC_FUNC_11N
	/* set maximum BA Window Size */
	pStreamReq->HT_BaWinSize = 64; /* maximum */
	ACMR_STA_MAX_BACK_NUM_GET(pAd, pCdb, pStreamReq->HT_BaWinSize);

	ACMR_DEBUG(ACMR_DEBUG_TRACE,
				("acm_msg> BA Win Size = %d\n", pStreamReq->HT_BaWinSize));
#endif /* ACM_CC_FUNC_11N */

	/* get UAPSD parameters */
	ApsdAcId = pStreamReq->AcmAcId;
	FlgIsApsdEnable = pStreamReq->pTspec->TsInfo.APSD;

	/* active stream check timer for any stream */
	ACMR_TIMER_ENABLE(ACMR_CB->FlgStreamAliveCheckEnable,
						ACMR_CB->TimerStreamAliveCheck,
						ACM_STREAM_CHECK_OFFSET);

	/* reset UAPSD state */
	ACM_APSD_Ctrl(pAd, pCdb, ApsdAcId,
					pStreamReq->pTspec->TsInfo.Direction,
					1, FlgIsApsdEnable);

	if (RetryCountOldSettings != 0xFFFFFFFF)
		ACMR_CB->RetryCountOldSettings = RetryCountOldSettings;
	/* End of if */

	ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);

	/* return power save right to system */
	ACMP_StaPsCtrlRightReturn(pAd);

	/* retry count change */
	ACMR_RETRY_DISABLE(pAd);

	ACMR_DEBUG(ACMR_DEBUG_TRACE,
				("acm_msg> My request is successfully %d! TC_RspHandle()\n",
				*pStatusCode));

LabelHandleOK:
	return ACM_RTN_OK;

LabelHandleErr:
	ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);

#ifdef RELEASE_EXCLUDE
	/*
		The ADDTS response handle fail, send a DELTS frame to the QAP due to
		parsing error.

		Reply a DELTS frame to QAP and the stream will be deleted after
		the DELTS frame is ACKed.
	*/
#endif /* RELEASE_EXCLUDE */
	*pStatusCode = ACM_STATUS_CODE_DECLINED;

LabelSemErr:
	/* return power save right to system */
	ACMP_StaPsCtrlRightReturn(pAd);
	ACMR_DEBUG(ACMR_DEBUG_TRACE,
				("acm_msg> Response Handle ERR! TC_RspHandle()\n"));
	return RtnCode;

LabelErr:
	/* delete the allocated stream */
	TC_LMR_STREAM_DESTROY(pAd, StmAcId, pStreamReq);

	ACM_TSPEC_SEM_UNLOCK(pAd, LabelSemErr);

#if 0 /* 2009/07/24: dont recover UAPSD state when ADDTS request fails */
	/* recover UAPSD state */
	ACM_APSD_Ctrl(pAd, pCdb, StmAcId,
					pStreamReq->pTspec->TsInfo.Direction, 0, 0);
#endif /* 0 */

	goto LabelSemErr;
} /* End of ACM_TC_RspHandle */

#endif /* CONFIG_STA_SUPPORT */

#endif /* WMM_ACM_SUPPORT */

/* End of acm_comm.c */
