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

    Module Name:
    UTIL/rt_linux.c

    Abstract:
	All functions provided from OS module are put here.

	Note:
	1. Can not use sizeof() for a structure with any parameter included
	by any compile option, such as RTMP_ADAPTER.

	Because the RTMP_ADAPTER size in the UTIL module is different with
	DRIVER/NETIF.

	2. Do not use any structure with any parameter included by PCI/USB/RBUS/
	AP/STA.

	Because the structure size in the UTIL module is different with
	DRIVER/NETIF.

	3. Do not use any structure defined in DRIVER module, EX: pAd.
	So we can do module partition.

	Revision History:
	Who        When          What
	---------  ----------    -------------------------------------------

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

#define RTMP_MODULE_OS
#define RTMP_MODULE_OS_UTIL


#include "rtmp_comm.h"
#include "rtmp_osabl.h"
#include "rt_os_util.h"
#include <linux/rtnetlink.h>

#if defined(CONFIG_RA_HW_NAT) || defined(CONFIG_RA_HW_NAT_MODULE)
#include "../../../../../../net/nat/hw_nat/ra_nat.h"
#include "../../../../../../net/nat/hw_nat/frame_engine.h"
#endif

/* TODO */
#undef RT_CONFIG_IF_OPMODE_ON_AP
#undef RT_CONFIG_IF_OPMODE_ON_STA

#if defined(CONFIG_AP_SUPPORT) && defined(CONFIG_STA_SUPPORT)
#define RT_CONFIG_IF_OPMODE_ON_AP(__OpMode)	if (__OpMode == OPMODE_AP)
#define RT_CONFIG_IF_OPMODE_ON_STA(__OpMode)	if (__OpMode == OPMODE_STA)
#else
#define RT_CONFIG_IF_OPMODE_ON_AP(__OpMode)
#define RT_CONFIG_IF_OPMODE_ON_STA(__OpMode)
#endif

ULONG RTDebugLevel = RT_DEBUG_ERROR;
ULONG RTDebugFunc = 0;

#ifdef OS_ABL_FUNC_SUPPORT
ULONG RTPktOffsetData = 0, RTPktOffsetLen = 0, RTPktOffsetCB = 0;
#endif /* OS_ABL_FUNC_SUPPORT */


#ifdef VENDOR_FEATURE4_SUPPORT
ULONG OS_NumOfMemAlloc = 0, OS_NumOfMemFree = 0;
#endif /* VENDOR_FEATURE4_SUPPORT */
#ifdef VENDOR_FEATURE2_SUPPORT
ULONG OS_NumOfPktAlloc = 0, OS_NumOfPktFree = 0;
#endif /* VENDOR_FEATURE2_SUPPORT */

/*
 * the lock will not be used in TX/RX
 * path so throughput should not be impacted
 */
BOOLEAN FlgIsUtilInit = FALSE;
OS_NDIS_SPIN_LOCK UtilSemLock;


BOOLEAN RTMP_OS_Alloc_RscOnly(VOID *pRscSrc, UINT32 RscLen);
BOOLEAN RTMP_OS_Remove_Rsc(LIST_HEADER *pRscList, VOID *pRscSrc);

/*
========================================================================
Routine Description:
	Initialize something in UTIL module.

Arguments:
	None

Return Value:
	None

Note:
========================================================================
*/
VOID RtmpUtilInit(VOID)
{
	if (FlgIsUtilInit == FALSE) {
		OS_NdisAllocateSpinLock(&UtilSemLock);
		FlgIsUtilInit = TRUE;
	}
}

/* timeout -- ms */
static inline VOID __RTMP_SetPeriodicTimer(
	IN OS_NDIS_MINIPORT_TIMER * pTimer,
	IN unsigned long timeout)
{
	timeout = ((timeout * OS_HZ) / 1000);
	pTimer->expires = jiffies + timeout;
	add_timer(pTimer);
}

/* convert NdisMInitializeTimer --> RTMP_OS_Init_Timer */
static inline VOID __RTMP_OS_Init_Timer(
	IN VOID *pReserved,
	IN OS_NDIS_MINIPORT_TIMER * pTimer,
	IN TIMER_FUNCTION function,
	IN PVOID data)
{
	if (!timer_pending(pTimer)) {
		init_timer(pTimer);
		pTimer->data = (unsigned long)data;
		pTimer->function = function;
	}
}

static inline VOID __RTMP_OS_Add_Timer(
	IN OS_NDIS_MINIPORT_TIMER * pTimer,
	IN unsigned long timeout)
{
	if (timer_pending(pTimer))
		return;

	timeout = ((timeout * OS_HZ) / 1000);
	pTimer->expires = jiffies + timeout;
	add_timer(pTimer);
}

static inline VOID __RTMP_OS_Mod_Timer(
	IN OS_NDIS_MINIPORT_TIMER * pTimer,
	IN unsigned long timeout)
{
	timeout = ((timeout * OS_HZ) / 1000);
	mod_timer(pTimer, jiffies + timeout);
}

static inline VOID __RTMP_OS_Del_Timer(
	IN OS_NDIS_MINIPORT_TIMER * pTimer,
	OUT BOOLEAN *pCancelled)
{
	if (timer_pending(pTimer))
		*pCancelled = del_timer_sync(pTimer);
	else
		*pCancelled = TRUE;
}

static inline VOID __RTMP_OS_Release_Timer(
	IN OS_NDIS_MINIPORT_TIMER * pTimer)
{
	/* nothing to do */
}


/* Unify all delay routine by using udelay */
VOID RTMPusecDelay(ULONG usec)
{
	ULONG i;

	for (i = 0; i < (usec / 50); i++)
		udelay(50);

	if (usec % 50)
		udelay(usec % 50);
}


/* Unify all delay routine by using udelay */
VOID RtmpOsUsDelay(ULONG value)
{
	ULONG i;

	udelay(value);
}

VOID RtmpOsMsDelay(ULONG msec)
{
	mdelay(msec);
}

void RTMP_GetCurrentSystemTime(LARGE_INTEGER * time)
{
	time->u.LowPart = jiffies;
}

void RTMP_GetCurrentSystemTick(ULONG *pNow)
{
	*pNow = jiffies;
}

/* pAd MUST allow to be NULL */

NDIS_STATUS os_alloc_mem(
	IN VOID *pReserved,
	OUT UCHAR **mem,
	IN ULONG size)
{
	*mem = (PUCHAR) kmalloc(size, GFP_ATOMIC);
	if (*mem) {
#ifdef VENDOR_FEATURE4_SUPPORT
		OS_NumOfMemAlloc++;
#endif /* VENDOR_FEATURE4_SUPPORT */

		return NDIS_STATUS_SUCCESS;
	} else
		return NDIS_STATUS_FAILURE;
}

NDIS_STATUS os_alloc_mem_suspend(
	IN VOID *pReserved,
	OUT UCHAR **mem,
	IN ULONG size)
{
	*mem = (PUCHAR) kmalloc(size, GFP_KERNEL);
	if (*mem) {
#ifdef VENDOR_FEATURE4_SUPPORT
		OS_NumOfMemAlloc++;
#endif /* VENDOR_FEATURE4_SUPPORT */

		return NDIS_STATUS_SUCCESS;
	} else
		return NDIS_STATUS_FAILURE;
}

/* pAd MUST allow to be NULL */
NDIS_STATUS os_free_mem(
	IN VOID *pReserved,
	IN PVOID mem)
{
	ASSERT(mem);
	kfree(mem);

#ifdef VENDOR_FEATURE4_SUPPORT
	OS_NumOfMemFree++;
#endif /* VENDOR_FEATURE4_SUPPORT */

	return NDIS_STATUS_SUCCESS;
}

#if defined(RTMP_RBUS_SUPPORT) || defined (RTMP_FLASH_SUPPORT)
/* The flag "CONFIG_RALINK_FLASH_API" is used for APSoC Linux SDK */
#ifdef CONFIG_RALINK_FLASH_API

int32_t FlashRead(
	uint32_t *dst,
	uint32_t *src,
	uint32_t count);

int32_t FlashWrite(
	uint16_t *source,
	uint16_t *destination,
	uint32_t numBytes);
#else /* CONFIG_RALINK_FLASH_API */

#ifdef RA_MTD_RW_BY_NUM
#if defined (CONFIG_RT2880_FLASH_32M)
#define MTD_NUM_FACTORY 5
#else
#define MTD_NUM_FACTORY 2
#endif
extern int ra_mtd_write(int num, loff_t to, size_t len, const u_char *buf);
extern int ra_mtd_read(int num, loff_t from, size_t len, u_char *buf);
#else
extern int ra_mtd_write_nm(char *name, loff_t to, size_t len, const u_char *buf);
extern int ra_mtd_read_nm(char *name, loff_t from, size_t len, u_char *buf);
#endif

#endif /* CONFIG_RALINK_FLASH_API */

void RtmpFlashRead(
	UCHAR *p,
	ULONG a,
	ULONG b)
{
#ifdef CONFIG_RALINK_FLASH_API
	FlashRead((uint32_t *) p, (uint32_t *) a, (uint32_t) b);
#else
#ifdef RA_MTD_RW_BY_NUM
	ra_mtd_read(MTD_NUM_FACTORY, 0, (size_t) b, p);
#else
	ra_mtd_read_nm("Factory", a&0xFFFF, (size_t) b, p);
#endif
#endif /* CONFIG_RALINK_FLASH_API */
}

void RtmpFlashWrite(
	UCHAR * p,
	ULONG a,
	ULONG b)
{
#ifdef CONFIG_RALINK_FLASH_API
	FlashWrite((uint16_t *) p, (uint16_t *) a, (uint32_t) b);
#else
#ifdef RA_MTD_RW_BY_NUM
	ra_mtd_write(MTD_NUM_FACTORY, 0, (size_t) b, p);
#else
	ra_mtd_write_nm("Factory", a&0xFFFF, (size_t) b, p);
#endif
#endif /* CONFIG_RALINK_FLASH_API */
}
#endif /* defined(RTMP_RBUS_SUPPORT) || defined (RTMP_FLASH_SUPPORT) */


PNDIS_PACKET RtmpOSNetPktAlloc(VOID *dummy, int size)
{
	struct sk_buff *skb;
	/* Add 2 more bytes for ip header alignment */
	skb = dev_alloc_skb(size + 2);
	if (skb != NULL)
		MEM_DBG_PKT_ALLOC_INC(skb);

	return ((PNDIS_PACKET) skb);
}

PNDIS_PACKET RTMP_AllocateFragPacketBuffer(VOID *dummy, ULONG len)
{
	struct sk_buff *pkt;

	pkt = dev_alloc_skb(len);

	if (pkt == NULL) {
		DBGPRINT(RT_DEBUG_ERROR,
			 ("can't allocate frag rx %ld size packet\n", len));
	}

	if (pkt) {
		MEM_DBG_PKT_ALLOC_INC(pkt);
		RTMP_SET_PACKET_SOURCE(OSPKT_TO_RTPKT(pkt), PKTSRC_NDIS);
	}

	return (PNDIS_PACKET) pkt;
}



/*
	The allocated NDIS PACKET must be freed via RTMPFreeNdisPacket()
*/
NDIS_STATUS RTMPAllocateNdisPacket(
	IN VOID *pReserved,
	OUT PNDIS_PACKET *ppPacket,
	IN UCHAR *pHeader,
	IN UINT HeaderLen,
	IN UCHAR *pData,
	IN UINT DataLen)
{
	struct sk_buff *pPacket;


	ASSERT(pData);
	ASSERT(DataLen);

	pPacket = dev_alloc_skb(HeaderLen + DataLen + RTMP_PKT_TAIL_PADDING);
	if (pPacket == NULL) {
		*ppPacket = NULL;
#ifdef DEBUG
		printk(KERN_ERR "RTMPAllocateNdisPacket Fail\n\n");
#endif
		return NDIS_STATUS_FAILURE;
	}
	MEM_DBG_PKT_ALLOC_INC(pPacket);

	/* Clone the frame content and update the length of packet */
	if (HeaderLen > 0)
		NdisMoveMemory(pPacket->data, pHeader, HeaderLen);
	if (DataLen > 0)
		NdisMoveMemory(pPacket->data + HeaderLen, pData, DataLen);
	skb_put(pPacket, HeaderLen + DataLen);
/* printk(KERN_ERR "%s : pPacket = %p, len = %d\n", __FUNCTION__, pPacket, GET_OS_PKT_LEN(pPacket));*/

	RTMP_SET_PACKET_SOURCE(pPacket, PKTSRC_NDIS);
	*ppPacket = (PNDIS_PACKET)pPacket;

	return NDIS_STATUS_SUCCESS;
}


/*
  ========================================================================
  Description:
	This routine frees a miniport internally allocated NDIS_PACKET and its
	corresponding NDIS_BUFFER and allocated memory.
  ========================================================================
*/
VOID RTMPFreeNdisPacket(
	IN VOID *pReserved,
	IN PNDIS_PACKET pPacket)
{
	dev_kfree_skb_any(RTPKT_TO_OSPKT(pPacket));
	MEM_DBG_PKT_FREE_INC(pPacket);
}


/* IRQL = DISPATCH_LEVEL */
/* NOTE: we do have an assumption here, that Byte0 and Byte1
 * always reasid at the same scatter gather buffer
 */
NDIS_STATUS Sniff2BytesFromNdisBuffer(
	IN PNDIS_BUFFER pFirstBuffer,
	IN UCHAR DesiredOffset,
	OUT PUCHAR pByte0,
	OUT PUCHAR pByte1)
{
	*pByte0 = *(PUCHAR) (pFirstBuffer + DesiredOffset);
	*pByte1 = *(PUCHAR) (pFirstBuffer + DesiredOffset + 1);

	return NDIS_STATUS_SUCCESS;
}


void RTMP_QueryPacketInfo(
	IN PNDIS_PACKET pPacket,
	OUT PACKET_INFO *info,
	OUT UCHAR **pSrcBufVA,
	OUT UINT *pSrcBufLen)
{
	info->BufferCount = 1;
	info->pFirstBuffer = (PNDIS_BUFFER) GET_OS_PKT_DATAPTR(pPacket);
	info->PhysicalBufferCount = 1;
	info->TotalPacketLength = GET_OS_PKT_LEN(pPacket);

	*pSrcBufVA = GET_OS_PKT_DATAPTR(pPacket);
	*pSrcBufLen = GET_OS_PKT_LEN(pPacket);

#ifdef TX_PKT_SG
	if (RTMP_GET_PKT_SG(pPacket)) {
		struct sk_buff *skb = (struct sk_buff *)pPacket;
		int i, nr_frags = skb_shinfo(skb)->nr_frags;
		
		info->BufferCount =  nr_frags + 1;
		info->PhysicalBufferCount = info->BufferCount;
		info->sg_list[0].data = (VOID *)GET_OS_PKT_DATAPTR(pPacket);
		info->sg_list[0].len = skb_headlen(skb);
		for (i = 0; i < nr_frags; i++) {
			skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
			
			info->sg_list[i+1].data = ((void *) page_address(frag->page) +
									frag->page_offset);
			info->sg_list[i+1].len = frag->size;
		}
	}
#endif /* TX_PKT_SG */
}




PNDIS_PACKET DuplicatePacket(
	IN PNET_DEV pNetDev,
	IN PNDIS_PACKET pPacket,
	IN UCHAR FromWhichBSSID)
{
	struct sk_buff *skb;
	PNDIS_PACKET pRetPacket = NULL;
	USHORT DataSize;
	UCHAR *pData;

	DataSize = (USHORT) GET_OS_PKT_LEN(pPacket);
	pData = (PUCHAR) GET_OS_PKT_DATAPTR(pPacket);

	skb = skb_clone(RTPKT_TO_OSPKT(pPacket), MEM_ALLOC_FLAG);
	if (skb) {
		MEM_DBG_PKT_ALLOC_INC(skb);
		skb->dev = pNetDev;	/*get_netdev_from_bssid(pAd, FromWhichBSSID); */
		pRetPacket = OSPKT_TO_RTPKT(skb);
	}

	return pRetPacket;

}


PNDIS_PACKET duplicate_pkt(
	IN PNET_DEV pNetDev,
	IN PUCHAR pHeader802_3,
	IN UINT HdrLen,
	IN PUCHAR pData,
	IN ULONG DataSize,
	IN UCHAR FromWhichBSSID)
{
	struct sk_buff *skb;
	PNDIS_PACKET pPacket = NULL;

	if ((skb =
	     __dev_alloc_skb(HdrLen + DataSize + 2, MEM_ALLOC_FLAG)) != NULL) {
		MEM_DBG_PKT_ALLOC_INC(skb);

		skb_reserve(skb, 2);
		NdisMoveMemory(skb->tail, pHeader802_3, HdrLen);
		skb_put(skb, HdrLen);
		NdisMoveMemory(skb->tail, pData, DataSize);
		skb_put(skb, DataSize);
		skb->dev = pNetDev;	/*get_netdev_from_bssid(pAd, FromWhichBSSID); */
		pPacket = OSPKT_TO_RTPKT(skb);
	}

	return pPacket;
}


#define TKIP_TX_MIC_SIZE		8
PNDIS_PACKET duplicate_pkt_with_TKIP_MIC(
	IN VOID *pReserved,
	IN PNDIS_PACKET pPacket)
{
	struct sk_buff *skb, *newskb;

	skb = RTPKT_TO_OSPKT(pPacket);
	if (skb_tailroom(skb) < TKIP_TX_MIC_SIZE) {
		/* alloc a new skb and copy the packet */
		newskb = skb_copy_expand(skb, skb_headroom(skb), TKIP_TX_MIC_SIZE, GFP_ATOMIC);

		dev_kfree_skb_any(skb);
		MEM_DBG_PKT_FREE_INC(skb);

		if (newskb == NULL) {
			DBGPRINT(RT_DEBUG_ERROR, ("Extend Tx.MIC for packet failed!, dropping packet!\n"));
			return NULL;
		}
		skb = newskb;
		MEM_DBG_PKT_ALLOC_INC(skb);
	}

	return OSPKT_TO_RTPKT(skb);


}

#ifdef CONFIG_AP_SUPPORT
PNDIS_PACKET duplicate_pkt_with_VLAN(
	IN PNET_DEV pNetDev,
	IN USHORT VLAN_VID,
	IN USHORT VLAN_Priority,
	IN PUCHAR pHeader802_3,
	IN UINT HdrLen,
	IN PUCHAR pData,
	IN ULONG DataSize,
	IN UCHAR FromWhichBSSID,
	IN UCHAR *TPID)
{
	struct sk_buff *skb;
	PNDIS_PACKET pPacket = NULL;
	UINT16 VLAN_Size;

	if ((skb = __dev_alloc_skb(HdrLen + DataSize + LENGTH_802_1Q + 2,
				   MEM_ALLOC_FLAG)) != NULL) {
		MEM_DBG_PKT_ALLOC_INC(skb);

		skb_reserve(skb, 2);

		/* copy header (maybe +VLAN tag) */
		VLAN_Size = VLAN_8023_Header_Copy(VLAN_VID, VLAN_Priority,
						  pHeader802_3, HdrLen,
						  skb->tail, FromWhichBSSID,
						  TPID);
		skb_put(skb, HdrLen + VLAN_Size);

		/* copy data body */
		NdisMoveMemory(skb->tail, pData, DataSize);
		skb_put(skb, DataSize);
		skb->dev = pNetDev;	/*get_netdev_from_bssid(pAd, FromWhichBSSID); */
		pPacket = OSPKT_TO_RTPKT(skb);
	}

	return pPacket;
}
#endif /* CONFIG_AP_SUPPORT */

/*
	========================================================================

	Routine Description:
		Send a L2 frame to upper daemon to trigger state machine

	Arguments:
		pAd - pointer to our pAdapter context

	Return Value:

	Note:

	========================================================================
*/
BOOLEAN RTMPL2FrameTxAction(
	IN VOID * pCtrlBkPtr,
	IN PNET_DEV pNetDev,
	IN RTMP_CB_8023_PACKET_ANNOUNCE _announce_802_3_packet,
	IN UCHAR apidx,
	IN PUCHAR pData,
	IN UINT32 data_len,
	IN	UCHAR			OpMode)
{
	struct sk_buff *skb = dev_alloc_skb(data_len + 2);

	if (!skb) {
		DBGPRINT(RT_DEBUG_ERROR,
			 ("%s : Error! Can't allocate a skb.\n", __FUNCTION__));
		return FALSE;
	}

	MEM_DBG_PKT_ALLOC_INC(skb);
	/*get_netdev_from_bssid(pAd, apidx)); */
	SET_OS_PKT_NETDEV(skb, pNetDev);

	/* 16 byte align the IP header */
	skb_reserve(skb, 2);

	/* Insert the frame content */
	NdisMoveMemory(GET_OS_PKT_DATAPTR(skb), pData, data_len);

	/* End this frame */
	skb_put(GET_OS_PKT_TYPE(skb), data_len);

	DBGPRINT(RT_DEBUG_TRACE, ("%s doen\n", __FUNCTION__));

	_announce_802_3_packet(pCtrlBkPtr, skb, OpMode);

	return TRUE;

}


PNDIS_PACKET ExpandPacket(
	IN VOID *pReserved,
	IN PNDIS_PACKET pPacket,
	IN UINT32 ext_head_len,
	IN UINT32 ext_tail_len)
{
	struct sk_buff *skb, *newskb;

	skb = RTPKT_TO_OSPKT(pPacket);
	if (skb_cloned(skb) || (skb_headroom(skb) < ext_head_len)
	    || (skb_tailroom(skb) < ext_tail_len)) {
		UINT32 head_len =
		    (skb_headroom(skb) <
		     ext_head_len) ? ext_head_len : skb_headroom(skb);
		UINT32 tail_len =
		    (skb_tailroom(skb) <
		     ext_tail_len) ? ext_tail_len : skb_tailroom(skb);

		/* alloc a new skb and copy the packet */
		newskb = skb_copy_expand(skb, head_len, tail_len, GFP_ATOMIC);

		dev_kfree_skb_any(skb);
		MEM_DBG_PKT_FREE_INC(skb);

		if (newskb == NULL) {
			DBGPRINT(RT_DEBUG_ERROR,
				 ("Extend Tx buffer for WPI failed!, dropping packet!\n"));
			return NULL;
		}
		skb = newskb;
		MEM_DBG_PKT_ALLOC_INC(skb);
	}

	return OSPKT_TO_RTPKT(skb);

}

PNDIS_PACKET ClonePacket(
	IN VOID *pReserved,
	IN PNDIS_PACKET pPacket,
	IN PUCHAR pData,
	IN ULONG DataSize)
{
	struct sk_buff *pRxPkt;
	struct sk_buff *pClonedPkt;

	ASSERT(pPacket);
	pRxPkt = RTPKT_TO_OSPKT(pPacket);

	/* clone the packet */
	pClonedPkt = skb_clone(pRxPkt, MEM_ALLOC_FLAG);

	if (pClonedPkt) {
		/* set the correct dataptr and data len */
		MEM_DBG_PKT_ALLOC_INC(pClonedPkt);
		pClonedPkt->dev = pRxPkt->dev;
		pClonedPkt->data = pData;
		pClonedPkt->len = DataSize;
		pClonedPkt->tail = pClonedPkt->data + pClonedPkt->len;
		ASSERT(DataSize < 1530);
	}
	return pClonedPkt;
}

VOID RtmpOsPktInit(
	IN PNDIS_PACKET pNetPkt,
	IN PNET_DEV pNetDev,
	IN UCHAR *pData,
	IN USHORT DataSize)
{
	PNDIS_PACKET pRxPkt;

	pRxPkt = RTPKT_TO_OSPKT(pNetPkt);

	SET_OS_PKT_NETDEV(pRxPkt, pNetDev);
	SET_OS_PKT_DATAPTR(pRxPkt, pData);
	SET_OS_PKT_LEN(pRxPkt, DataSize);
	SET_OS_PKT_DATATAIL(pRxPkt, pData, DataSize);
}


void wlan_802_11_to_802_3_packet(
	IN PNET_DEV pNetDev,
	IN UCHAR OpMode,
	IN USHORT VLAN_VID,
	IN USHORT VLAN_Priority,
	IN PNDIS_PACKET pRxPacket,
	IN UCHAR *pData,
	IN ULONG DataSize,
	IN PUCHAR pHeader802_3,
	IN UCHAR FromWhichBSSID,
	IN UCHAR *TPID)
{
	struct sk_buff *pOSPkt;

	ASSERT(pHeader802_3);

	pOSPkt = RTPKT_TO_OSPKT(pRxPacket);

	/*get_netdev_from_bssid(pAd, FromWhichBSSID); */
	pOSPkt->dev = pNetDev;
	pOSPkt->data = pData;
	pOSPkt->len = DataSize;
	pOSPkt->tail = pOSPkt->data + pOSPkt->len;

	/* copy 802.3 header */
#ifdef CONFIG_AP_SUPPORT
	RT_CONFIG_IF_OPMODE_ON_AP(OpMode)
	{
		/* maybe insert VLAN tag to the received packet */
		UCHAR VLAN_Size = 0;
		UCHAR *data_p;

		if (VLAN_VID != 0)
			VLAN_Size = LENGTH_802_1Q;

		data_p = skb_push(pOSPkt, LENGTH_802_3 + VLAN_Size);

		VLAN_8023_Header_Copy(VLAN_VID, VLAN_Priority,
				      pHeader802_3, LENGTH_802_3,
				      data_p, FromWhichBSSID, TPID);
	}
#endif /* CONFIG_AP_SUPPORT */

#ifdef CONFIG_STA_SUPPORT
	RT_CONFIG_IF_OPMODE_ON_STA(OpMode)
	{
	    NdisMoveMemory(skb_push(pOSPkt, LENGTH_802_3), pHeader802_3, LENGTH_802_3);
	}
#endif /* CONFIG_STA_SUPPORT */

}


#ifdef HDR_TRANS_SUPPORT
VOID RtmpOsSetPacket(
	IN PNET_DEV pNetDev,
	IN PNDIS_PACKET pRxPacket,
	IN UCHAR *pData,
	IN ULONG DataSize)
{

	struct sk_buff *pOSPkt;

	pOSPkt = RTPKT_TO_OSPKT(pRxPacket);

	pOSPkt->dev = pNetDev;
	pOSPkt->data = pData;
	pOSPkt->len = DataSize;
	pOSPkt->tail = pOSPkt->data + pOSPkt->len;
}

#endif /* HDR_TRANS_SUPPORT */


void hex_dump(char *str, UCHAR *pSrcBufVA, UINT SrcBufLen)
{
#ifdef DBG
	unsigned char *pt;
	int x;

	if (RTDebugLevel < RT_DEBUG_TRACE)
		return;

	pt = pSrcBufVA;
	printk("%s: %p, len = %d\n", str, pSrcBufVA, SrcBufLen);
	for (x = 0; x < SrcBufLen; x++) {
		if (x % 16 == 0)
			printk("0x%04x : ", x);
		printk("%02x ", ((unsigned char)pt[x]));
		if (x % 16 == 15)
			printk("\n");
	}
	printk("\n");
#endif /* DBG */
}

#ifdef SYSTEM_LOG_SUPPORT
/*
	========================================================================

	Routine Description:
		Send log message through wireless event

		Support standard iw_event with IWEVCUSTOM. It is used below.

		iwreq_data.data.flags is used to store event_flag that is
		defined by user. iwreq_data.data.length is the length of the
		event log.

		The format of the event log is composed of the entry's MAC
		address and the desired log message (refer to
		pWirelessEventText).

			ex: 11:22:33:44:55:66 has associated successfully

		p.s. The requirement of Wireless Extension is v15 or newer.

	========================================================================
*/
VOID RtmpOsSendWirelessEvent(
	IN VOID *pAd,
	IN USHORT Event_flag,
	IN PUCHAR pAddr,
	IN UCHAR BssIdx,
	IN CHAR Rssi,
	IN RTMP_OS_SEND_WLAN_EVENT pFunc)
{
#if WIRELESS_EXT >= 15
	pFunc(pAd, Event_flag, pAddr, BssIdx, Rssi);
#else
	DBGPRINT(RT_DEBUG_ERROR,
		 ("%s : The Wireless Extension MUST be v15 or newer.\n",
		  __FUNCTION__));
#endif /* WIRELESS_EXT >= 15 */
}
#endif /* SYSTEM_LOG_SUPPORT */

#ifdef CONFIG_AP_SUPPORT
VOID SendSignalToDaemon(
	IN INT sig,
	RTMP_OS_PID pid,
	unsigned long pid_no)
{
}
#endif /* CONFIG_AP_SUPPORT */

#ifdef CONFIG_STA_SUPPORT
INT32 ralinkrate[] = {
2, 4, 11, 22,		/* CCK */
12, 18, 24, 36, 48, 72, 96, 108,	/* OFDM */
/* 20MHz, 800ns GI, MCS: 0 ~ 15 */
13, 26, 39, 52, 78, 104, 117, 130, 26, 52, 78, 104, 156, 208, 234, 260,
39, 78, 117, 156, 234, 312, 351, 390,	/* 20MHz, 800ns GI, MCS: 16 ~ 23 */
/* 40MHz, 800ns GI, MCS: 0 ~ 15 */
27, 54, 81, 108, 162, 216, 243, 270, 54, 108, 162, 216, 324, 432, 486, 540,
81, 162, 243, 324, 486, 648, 729, 810,	/* 40MHz, 800ns GI, MCS: 16 ~ 23 */
/* 20MHz, 400ns GI, MCS: 0 ~ 15 */
14, 29, 43, 57, 87, 115, 130, 144, 29, 59, 87, 115, 173, 230, 260, 288,
43, 87, 130, 173, 260, 317, 390, 433,	/* 20MHz, 400ns GI, MCS: 16 ~ 23 */
/* 40MHz, 400ns GI, MCS: 0 ~ 15 */
30, 60, 90, 120, 180, 240, 270, 300, 60, 120, 180, 240, 360, 480, 540, 600,
90, 180, 270, 360, 540, 720, 810, 900};	/* 40MHz, 400ns GI, MCS: 16 ~ 23 */

UINT32 RT_RateSize = sizeof (ralinkrate);

void send_monitor_packets(IN PNET_DEV pNetDev,
			  IN PNDIS_PACKET pRxPacket,
			  IN PHEADER_802_11 pHeader,
			  IN UCHAR * pData,
			  IN USHORT DataSize,
			  IN UCHAR L2PAD,
			  IN UCHAR PHYMODE,
			  IN UCHAR BW,
			  IN UCHAR ShortGI,
			  IN UCHAR MCS,
			  IN UCHAR AMPDU,
			  IN UCHAR STBC,
			  IN UCHAR RSSI1,
			  IN UCHAR BssMonitorFlag11n,
			  IN UCHAR * pDevName,
			  IN UCHAR Channel,
			  IN UCHAR CentralChannel,
			  IN UINT32 MaxRssi) {
	struct sk_buff *pOSPkt;
	wlan_ng_prism2_header *ph;
#ifdef MONITOR_FLAG_11N_SNIFFER_SUPPORT
	ETHEREAL_RADIO h,
	*ph_11n33;		/* for new 11n sniffer format */
#endif /* MONITOR_FLAG_11N_SNIFFER_SUPPORT */
	int rate_index = 0;
	USHORT header_len = 0;
	UCHAR temp_header[40] = {
	0};

	MEM_DBG_PKT_FREE_INC(pRxPacket);


	pOSPkt = RTPKT_TO_OSPKT(pRxPacket);	/*pRxBlk->pRxPacket); */
	pOSPkt->dev = pNetDev;	/*get_netdev_from_bssid(pAd, BSS0); */
	if (pHeader->FC.Type == BTYPE_DATA) {
		DataSize -= LENGTH_802_11;
		if ((pHeader->FC.ToDs == 1) && (pHeader->FC.FrDs == 1))
			header_len = LENGTH_802_11_WITH_ADDR4;
		else
			header_len = LENGTH_802_11;

		/* QOS */
		if (pHeader->FC.SubType & 0x08) {
			header_len += 2;
			/* Data skip QOS contorl field */
			DataSize -= 2;
		}

		/* Order bit: A-Ralink or HTC+ */
		if (pHeader->FC.Order) {
			header_len += 4;
			/* Data skip HTC contorl field */
			DataSize -= 4;
		}

		/* Copy Header */
		if (header_len <= 40)
			NdisMoveMemory(temp_header, pData, header_len);

		/* skip HW padding */
		if (L2PAD)
			pData += (header_len + 2);
		else
			pData += header_len;
	}

	if (DataSize < pOSPkt->len) {
		skb_trim(pOSPkt, DataSize);
	} else {
		skb_put(pOSPkt, (DataSize - pOSPkt->len));
	}

	if ((pData - pOSPkt->data) > 0) {
		skb_put(pOSPkt, (pData - pOSPkt->data));
		skb_pull(pOSPkt, (pData - pOSPkt->data));
	}

	if (skb_headroom(pOSPkt) < (sizeof (wlan_ng_prism2_header) + header_len)) {
		if (pskb_expand_head(pOSPkt, (sizeof (wlan_ng_prism2_header) + header_len), 0, GFP_ATOMIC)) {
			DBGPRINT(RT_DEBUG_ERROR,
				 ("%s : Reallocate header size of sk_buff fail!\n",
				  __FUNCTION__));
			goto err_free_sk_buff;
		}
	}

	if (header_len > 0)
		NdisMoveMemory(skb_push(pOSPkt, header_len), temp_header,
			       header_len);

#ifdef MONITOR_FLAG_11N_SNIFFER_SUPPORT
	if (BssMonitorFlag11n == 0)
#endif /* MONITOR_FLAG_11N_SNIFFER_SUPPORT */
	{
		ph = (wlan_ng_prism2_header *) skb_push(pOSPkt,
							sizeof(wlan_ng_prism2_header));
		NdisZeroMemory(ph, sizeof(wlan_ng_prism2_header));

		ph->msgcode = DIDmsg_lnxind_wlansniffrm;
		ph->msglen = sizeof (wlan_ng_prism2_header);
		strcpy((PSTRING) ph->devname, (PSTRING) pDevName);

		ph->hosttime.did = DIDmsg_lnxind_wlansniffrm_hosttime;
		ph->hosttime.status = 0;
		ph->hosttime.len = 4;
		ph->hosttime.data = jiffies;

		ph->mactime.did = DIDmsg_lnxind_wlansniffrm_mactime;
		ph->mactime.status = 0;
		ph->mactime.len = 0;
		ph->mactime.data = 0;

		ph->istx.did = DIDmsg_lnxind_wlansniffrm_istx;
		ph->istx.status = 0;
		ph->istx.len = 0;
		ph->istx.data = 0;

		ph->channel.did = DIDmsg_lnxind_wlansniffrm_channel;
		ph->channel.status = 0;
		ph->channel.len = 4;

		ph->channel.data = (u_int32_t) Channel;

		ph->rssi.did = DIDmsg_lnxind_wlansniffrm_rssi;
		ph->rssi.status = 0;
		ph->rssi.len = 4;
		ph->rssi.data = MaxRssi;
		ph->signal.did = DIDmsg_lnxind_wlansniffrm_signal;
		ph->signal.status = 0;
		ph->signal.len = 4;
		ph->signal.data = 0;	/*rssi + noise; */

		ph->noise.did = DIDmsg_lnxind_wlansniffrm_noise;
		ph->noise.status = 0;
		ph->noise.len = 4;
		ph->noise.data = 0;

#ifdef DOT11_N_SUPPORT
		if (PHYMODE >= MODE_HTMIX) {
			rate_index = 12 + ((UCHAR) BW * 24) + ((UCHAR) ShortGI * 48) + ((UCHAR) MCS);
		} else
#endif /* DOT11_N_SUPPORT */
		if (PHYMODE == MODE_OFDM)
			rate_index = (UCHAR) (MCS) + 4;
		else
			rate_index = (UCHAR) (MCS);

		if (rate_index < 0)
			rate_index = 0;
		if (rate_index >= (sizeof (ralinkrate) / sizeof (ralinkrate[0])))
			rate_index = (sizeof (ralinkrate) / sizeof (ralinkrate[0])) - 1;

		ph->rate.did = DIDmsg_lnxind_wlansniffrm_rate;
		ph->rate.status = 0;
		ph->rate.len = 4;
		/* real rate = ralinkrate[rate_index] / 2 */
		ph->rate.data = ralinkrate[rate_index];

		ph->frmlen.did = DIDmsg_lnxind_wlansniffrm_frmlen;
		ph->frmlen.status = 0;
		ph->frmlen.len = 4;
		ph->frmlen.data = (u_int32_t) DataSize;
	}
#ifdef MONITOR_FLAG_11N_SNIFFER_SUPPORT
	else {
		ph_11n33 = &h;
		NdisZeroMemory((unsigned char *)ph_11n33,
			       sizeof (ETHEREAL_RADIO));

		/*802.11n fields */
		if (MCS > 15)
			ph_11n33->Flag_80211n |= WIRESHARK_11N_FLAG_3x3;

		if (PHYMODE == MODE_HTGREENFIELD)
			ph_11n33->Flag_80211n |= WIRESHARK_11N_FLAG_GF;

		if (BW == 1) {
			ph_11n33->Flag_80211n |= WIRESHARK_11N_FLAG_BW40;
		} else if (Channel < CentralChannel) {
			ph_11n33->Flag_80211n |= WIRESHARK_11N_FLAG_BW20U;
		} else if (Channel > CentralChannel) {
			ph_11n33->Flag_80211n |= WIRESHARK_11N_FLAG_BW20D;
		} else {
			ph_11n33->Flag_80211n |=
			    (WIRESHARK_11N_FLAG_BW20U |
			     WIRESHARK_11N_FLAG_BW20D);
		}

		if (ShortGI == 1)
			ph_11n33->Flag_80211n |= WIRESHARK_11N_FLAG_SGI;

		if (AMPDU)
			ph_11n33->Flag_80211n |= WIRESHARK_11N_FLAG_AMPDU;

		if (STBC)
			ph_11n33->Flag_80211n |= WIRESHARK_11N_FLAG_STBC;

		ph_11n33->signal_level = (UCHAR) RSSI1;

		/* data_rate is the rate index in the wireshark rate table */
		if (PHYMODE >= MODE_HTMIX) {
			if (MCS == 32) {
				if (ShortGI)
					ph_11n33->data_rate = 16;
				else
					ph_11n33->data_rate = 4;
			} else if (MCS > 15)
				ph_11n33->data_rate =
				    (16 * 4 + ((UCHAR) BW * 16) +
				     ((UCHAR) ShortGI * 32) + ((UCHAR) MCS));
			else
				ph_11n33->data_rate =
				    16 + ((UCHAR) BW * 16) +
				    ((UCHAR) ShortGI * 32) + ((UCHAR) MCS);
		} else if (PHYMODE == MODE_OFDM)
			ph_11n33->data_rate = (UCHAR) (MCS) + 4;
		else
			ph_11n33->data_rate = (UCHAR) (MCS);

		/*channel field */
		ph_11n33->channel = (UCHAR) Channel;

		NdisMoveMemory(skb_put(pOSPkt, sizeof (ETHEREAL_RADIO)),
			       (UCHAR *) ph_11n33, sizeof (ETHEREAL_RADIO));
	}
#endif /* MONITOR_FLAG_11N_SNIFFER_SUPPORT */

	pOSPkt->pkt_type = PACKET_OTHERHOST;
	pOSPkt->protocol = eth_type_trans(pOSPkt, pOSPkt->dev);
	pOSPkt->ip_summed = CHECKSUM_NONE;
	netif_rx(pOSPkt);

	return;

      err_free_sk_buff:
	RELEASE_NDIS_PACKET(NULL, pRxPacket, NDIS_STATUS_FAILURE);
	return;

}
#endif /* CONFIG_STA_SUPPORT */


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

	File open/close related functions.

 *******************************************************************************/
RTMP_OS_FD RtmpOSFileOpen(char *pPath, int flag, int mode)
{
	struct file *filePtr;

	if (flag == RTMP_FILE_RDONLY)
		flag = O_RDONLY;
	else if (flag == RTMP_FILE_WRONLY)
		flag = O_WRONLY;
	else if (flag == RTMP_FILE_CREAT)
		flag = O_CREAT;
	else if (flag == RTMP_FILE_TRUNC)
		flag = O_TRUNC;

	filePtr = filp_open(pPath, flag, 0);
	if (IS_ERR(filePtr)) {
		DBGPRINT(RT_DEBUG_ERROR,
			 ("%s(): Error %ld opening %s\n", __FUNCTION__,
			  -PTR_ERR(filePtr), pPath));
	}

	return (RTMP_OS_FD) filePtr;
}

int RtmpOSFileClose(RTMP_OS_FD osfd)
{
	filp_close(osfd, NULL);
	return 0;
}

void RtmpOSFileSeek(RTMP_OS_FD osfd, int offset)
{
	osfd->f_pos = offset;
}


int RtmpOSFileRead(RTMP_OS_FD osfd, char *pDataPtr, int readLen)
{
    DBGPRINT(RT_DEBUG_ERROR, ("add: %p %p\n", osfd->f_op, osfd->f_op->read));
        /* The object must have a read method */
        if (osfd->f_op /*&& osfd->f_op->read*/) {
                //return osfd->f_op->read(osfd, pDataPtr, readLen, &osfd->f_pos);
                return vfs_read(osfd, pDataPtr, readLen, &osfd->f_pos);
        } else {
                DBGPRINT(RT_DEBUG_ERROR, ("no file read method\n"));
                return -1;
        }
}

int RtmpOSFileWrite(RTMP_OS_FD osfd, char *pDataPtr, int writeLen)
{
	return osfd->f_op->write(osfd, pDataPtr, (size_t) writeLen, &osfd->f_pos);
}

static inline void __RtmpOSFSInfoChange(OS_FS_INFO * pOSFSInfo, BOOLEAN bSet)
{
	if (bSet) {
		/* Save uid and gid used for filesystem access. */
		/* Set user and group to 0 (root) */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)
		pOSFSInfo->fsuid = current->fsuid;
		pOSFSInfo->fsgid = current->fsgid;
		current->fsuid = current->fsgid = 0;
#else
		pOSFSInfo->fsuid = *(int*)&current_fsuid();
		pOSFSInfo->fsgid = *(int*)&current_fsgid();
#endif
		pOSFSInfo->fs = get_fs();
		set_fs(KERNEL_DS);
	} else {
		set_fs(pOSFSInfo->fs);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)
		current->fsuid = pOSFSInfo->fsuid;
		current->fsgid = pOSFSInfo->fsgid;
#endif
	}
}


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

	Task create/management/kill related functions.

 *******************************************************************************/
static inline NDIS_STATUS __RtmpOSTaskKill(OS_TASK *pTask)
{
	int ret = NDIS_STATUS_FAILURE;

#ifdef KTHREAD_SUPPORT
	if (pTask->kthread_task) {
		kthread_stop(pTask->kthread_task);
		ret = NDIS_STATUS_SUCCESS;
	}
#else
	CHECK_PID_LEGALITY(pTask->taskPID) {
		DBGPRINT(RT_DEBUG_TRACE,
			 ("Terminate the task(%s) with pid(%d)!\n",
			  pTask->taskName, GET_PID_NUMBER(pTask->taskPID)));
		mb();
		pTask->task_killed = 1;
		mb();
		ret = KILL_THREAD_PID(pTask->taskPID, SIGTERM, 1);
		if (ret) {
			printk(KERN_WARNING
			       "kill task(%s) with pid(%d) failed(retVal=%d)!\n",
			       pTask->taskName, GET_PID_NUMBER(pTask->taskPID),
			       ret);
		} else {
			wait_for_completion(&pTask->taskComplete);
			pTask->taskPID = THREAD_PID_INIT_VALUE;
			pTask->task_killed = 0;
			RTMP_SEM_EVENT_DESTORY(&pTask->taskSema);
			ret = NDIS_STATUS_SUCCESS;
		}
	}
#endif

	return ret;

}

static inline INT __RtmpOSTaskNotifyToExit(OS_TASK *pTask)
{
#ifndef KTHREAD_SUPPORT
	pTask->taskPID = THREAD_PID_INIT_VALUE;
	complete_and_exit(&pTask->taskComplete, 0);
#endif

	return 0;
}

static inline void __RtmpOSTaskCustomize(OS_TASK *pTask)
{
#ifndef KTHREAD_SUPPORT

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
	daemonize((PSTRING) & pTask->taskName[0] /*"%s",pAd->net_dev->name */ );

	allow_signal(SIGTERM);
	allow_signal(SIGKILL);
	current->flags |= PF_NOFREEZE;
#else
	unsigned long flags;

	daemonize();
	reparent_to_init();
	strcpy(current->comm, &pTask->taskName[0]);

	siginitsetinv(&current->blocked, sigmask(SIGTERM) | sigmask(SIGKILL));

	/* Allow interception of SIGKILL only
	 * Don't allow other signals to interrupt the transmission */
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,22)
	spin_lock_irqsave(&current->sigmask_lock, flags);
	flush_signals(current);
	recalc_sigpending(current);
	spin_unlock_irqrestore(&current->sigmask_lock, flags);
#endif
#endif

	RTMP_GET_OS_PID(pTask->taskPID, current->pid);

	/* signal that we've started the thread */
	complete(&pTask->taskComplete);

#endif
}

static inline NDIS_STATUS __RtmpOSTaskAttach(
	IN OS_TASK *pTask,
	IN RTMP_OS_TASK_CALLBACK fn,
	IN ULONG arg)
{
	NDIS_STATUS status = NDIS_STATUS_SUCCESS;
#ifndef KTHREAD_SUPPORT
	pid_t pid_number = -1;
#endif /* KTHREAD_SUPPORT */

#ifdef KTHREAD_SUPPORT
	pTask->task_killed = 0;
	pTask->kthread_task = NULL;
	pTask->kthread_task =
	    kthread_run((cast_fn) fn, (void *)arg, pTask->taskName);
	if (IS_ERR(pTask->kthread_task))
		status = NDIS_STATUS_FAILURE;
#else
	pid_number =
	    kernel_thread((cast_fn) fn, (void *)arg, RTMP_OS_MGMT_TASK_FLAGS);
	if (pid_number < 0) {
		DBGPRINT(RT_DEBUG_ERROR,
			 ("Attach task(%s) failed!\n", pTask->taskName));
		status = NDIS_STATUS_FAILURE;
	} else {
		/* Wait for the thread to start */
		wait_for_completion(&pTask->taskComplete);
		status = NDIS_STATUS_SUCCESS;
	}
#endif
	return status;
}

static inline NDIS_STATUS __RtmpOSTaskInit(
	IN OS_TASK *pTask,
	IN PSTRING pTaskName,
	IN VOID *pPriv,
	IN LIST_HEADER *pSemList)
{
	int len;

	ASSERT(pTask);

#ifndef KTHREAD_SUPPORT
	NdisZeroMemory((PUCHAR) (pTask), sizeof (OS_TASK));
#endif

	len = strlen(pTaskName);
	len = len > (RTMP_OS_TASK_NAME_LEN - 1) ? (RTMP_OS_TASK_NAME_LEN - 1) : len;
	NdisMoveMemory(&pTask->taskName[0], pTaskName, len);
	pTask->priv = pPriv;

#ifndef KTHREAD_SUPPORT
	RTMP_SEM_EVENT_INIT_LOCKED(&(pTask->taskSema), pSemList);
	pTask->taskPID = THREAD_PID_INIT_VALUE;
	init_completion(&pTask->taskComplete);
#endif

#ifdef KTHREAD_SUPPORT
	init_waitqueue_head(&(pTask->kthread_q));
#endif /* KTHREAD_SUPPORT */

	return NDIS_STATUS_SUCCESS;
}

BOOLEAN __RtmpOSTaskWait(
	IN VOID *pReserved,
	IN OS_TASK *pTask,
	IN INT32 *pStatus)
{
#ifdef KTHREAD_SUPPORT
	RTMP_WAIT_EVENT_INTERRUPTIBLE((*pStatus), pTask);

	if ((pTask->task_killed == 1) || ((*pStatus) != 0))
		return FALSE;
#else

	RTMP_SEM_EVENT_WAIT(&(pTask->taskSema), (*pStatus));

	/* unlock the device pointers */
	if ((*pStatus) != 0) {
/*		RTMP_SET_FLAG_(*pFlags, fRTMP_ADAPTER_HALT_IN_PROGRESS); */
		return FALSE;
	}
#endif /* KTHREAD_SUPPORT */

	return TRUE;
}


#if LINUX_VERSION_CODE <= 0x20402	/* Red Hat 7.1 */
struct net_device *alloc_netdev(
	int sizeof_priv,
	const char *mask,
	void (*setup) (struct net_device *))
{
	struct net_device *dev;
	INT alloc_size;

	/* ensure 32-byte alignment of the private area */
	alloc_size = sizeof (*dev) + sizeof_priv + 31;

	dev = (struct net_device *)kmalloc(alloc_size, GFP_KERNEL);
	if (dev == NULL) {
		DBGPRINT(RT_DEBUG_ERROR,
			 ("alloc_netdev: Unable to allocate device memory.\n"));
		return NULL;
	}

	memset(dev, 0, alloc_size);

	if (sizeof_priv)
		dev->priv = (void *)(((long)(dev + 1) + 31) & ~31);

	setup(dev);
	strcpy(dev->name, mask);

	return dev;
}
#endif /* LINUX_VERSION_CODE */


static UINT32 RtmpOSWirelessEventTranslate(IN UINT32 eventType)
{
	switch (eventType) {
	case RT_WLAN_EVENT_CUSTOM:
		eventType = IWEVCUSTOM;
		break;

	case RT_WLAN_EVENT_CGIWAP:
		eventType = SIOCGIWAP;
		break;

#if WIRELESS_EXT > 17
	case RT_WLAN_EVENT_ASSOC_REQ_IE:
		eventType = IWEVASSOCREQIE;
		break;
#endif /* WIRELESS_EXT */

#if WIRELESS_EXT >= 14
	case RT_WLAN_EVENT_SCAN:
		eventType = SIOCGIWSCAN;
		break;
#endif /* WIRELESS_EXT */

	case RT_WLAN_EVENT_EXPIRED:
		eventType = IWEVEXPIRED;
		break;
#ifdef P2P_SUPPORT
	case RT_WLAN_EVENT_SHOWPIN:
		eventType = 0x8C05; /* IWEVP2PKEYSHOWPIN; */
		break;
	case RT_WLAN_EVENT_PIN:
		eventType = 0x8C06; /* IWEVP2PKEYPIN; */
		break;
#endif /* P2P_SUPPORT */

	default:
		printk("Unknown event: 0x%x\n", eventType);
		break;
	}

	return eventType;
}

int RtmpOSWrielessEventSend(
	IN PNET_DEV pNetDev,
	IN UINT32 eventType,
	IN INT flags,
	IN PUCHAR pSrcMac,
	IN PUCHAR pData,
	IN UINT32 dataLen)
{
	union iwreq_data wrqu;

	/* translate event type */
	eventType = RtmpOSWirelessEventTranslate(eventType);

	memset(&wrqu, 0, sizeof (wrqu));

	if (flags > -1)
		wrqu.data.flags = flags;

	if (pSrcMac)
		memcpy(wrqu.ap_addr.sa_data, pSrcMac, MAC_ADDR_LEN);

	if ((pData != NULL) && (dataLen > 0))
		wrqu.data.length = dataLen;
	else
		wrqu.data.length = 0;

	wireless_send_event(pNetDev, eventType, &wrqu, (char *)pData);
	return 0;
}

int RtmpOSWrielessEventSendExt(
	IN PNET_DEV pNetDev,
	IN UINT32 eventType,
	IN INT flags,
	IN PUCHAR pSrcMac,
	IN PUCHAR pData,
	IN UINT32 dataLen,
	IN UINT32 family)
{
	union iwreq_data wrqu;

	/* translate event type */
	eventType = RtmpOSWirelessEventTranslate(eventType);

	/* translate event type */
	memset(&wrqu, 0, sizeof (wrqu));

	if (flags > -1)
		wrqu.data.flags = flags;

	if (pSrcMac)
		memcpy(wrqu.ap_addr.sa_data, pSrcMac, MAC_ADDR_LEN);

	if ((pData != NULL) && (dataLen > 0))
		wrqu.data.length = dataLen;

	wrqu.addr.sa_family = family;

	wireless_send_event(pNetDev, eventType, &wrqu, (char *)pData);
	return 0;
}

int RtmpOSNetDevAddrSet(
	IN UCHAR OpMode,
	IN PNET_DEV pNetDev,
	IN PUCHAR pMacAddr,
	IN PUCHAR dev_name)
{
	struct net_device *net_dev;

	net_dev = pNetDev;
/*	GET_PAD_FROM_NET_DEV(pAd, net_dev); */

#ifdef CONFIG_STA_SUPPORT
	/* work-around for the SuSE due to it has it's own interface name management system. */
	RT_CONFIG_IF_OPMODE_ON_STA(OpMode) {
/*		NdisZeroMemory(pAd->StaCfg.dev_name, 16); */
/*		NdisMoveMemory(pAd->StaCfg.dev_name, net_dev->name, strlen(net_dev->name)); */
		if (dev_name != NULL) {
			NdisZeroMemory(dev_name, 16);
			NdisMoveMemory(dev_name, net_dev->name, strlen(net_dev->name));
		}
	}
#endif /* CONFIG_STA_SUPPORT */

	NdisMoveMemory(net_dev->dev_addr, pMacAddr, 6);

	return 0;
}

/*
  *	Assign the network dev name for created Ralink WiFi interface.
  */
static int RtmpOSNetDevRequestName(
	IN INT32 MC_RowID,
	IN UINT32 *pIoctlIF,
	IN PNET_DEV dev,
	IN PSTRING pPrefixStr,
	IN INT devIdx)
{
	PNET_DEV existNetDev;
	STRING suffixName[IFNAMSIZ];
	STRING desiredName[IFNAMSIZ];
	int ifNameIdx,
	 prefixLen,
	 slotNameLen;
	int Status;

	prefixLen = strlen(pPrefixStr);
	ASSERT((prefixLen < IFNAMSIZ));

	for (ifNameIdx = devIdx; ifNameIdx < 32; ifNameIdx++) {
		memset(suffixName, 0, IFNAMSIZ);
		memset(desiredName, 0, IFNAMSIZ);
		strncpy(&desiredName[0], pPrefixStr, prefixLen);

#ifdef MULTIPLE_CARD_SUPPORT
		if (MC_RowID >= 0)
			sprintf(suffixName, "%02d_%d", MC_RowID, ifNameIdx);
		else
#endif /* MULTIPLE_CARD_SUPPORT */
			sprintf(suffixName, "%d", ifNameIdx);

		slotNameLen = strlen(suffixName);
		ASSERT(((slotNameLen + prefixLen) < IFNAMSIZ));
		strcat(desiredName, suffixName);

		existNetDev = RtmpOSNetDevGetByName(dev, &desiredName[0]);
		if (existNetDev == NULL)
			break;
		else
			RtmpOSNetDeviceRefPut(existNetDev);
	}

	if (ifNameIdx < 32) {
#ifdef HOSTAPD_SUPPORT
		*pIoctlIF = ifNameIdx;
#endif /*HOSTAPD_SUPPORT */
		strcpy(&dev->name[0], &desiredName[0]);
		Status = NDIS_STATUS_SUCCESS;
	} else {
		DBGPRINT(RT_DEBUG_ERROR,
			 ("Cannot request DevName with preifx(%s) and in range(0~32) as suffix from OS!\n",
			  pPrefixStr));
		Status = NDIS_STATUS_FAILURE;
	}

	return Status;
}

void RtmpOSNetDevClose(PNET_DEV pNetDev)
{
	dev_close(pNetDev);
}

void RtmpOSNetDevFree(PNET_DEV pNetDev)
{
	DEV_PRIV_INFO *pDevInfo = NULL;


	ASSERT(pNetDev);

	/* free assocaited private information */
	pDevInfo = _RTMP_OS_NETDEV_GET_PRIV(pNetDev);
	if (pDevInfo != NULL)
		os_free_mem(NULL, pDevInfo);

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
	free_netdev(pNetDev);
#else
	kfree(pNetDev);
#endif

#ifdef VENDOR_FEATURE4_SUPPORT
	printk("OS_NumOfMemAlloc = %ld, OS_NumOfMemFree = %ld\n",
			OS_NumOfMemAlloc, OS_NumOfMemFree);
#endif /* VENDOR_FEATURE4_SUPPORT */
#ifdef VENDOR_FEATURE2_SUPPORT
	printk("OS_NumOfPktAlloc = %ld, OS_NumOfPktFree = %ld\n",
			OS_NumOfPktAlloc, OS_NumOfPktFree);
#endif /* VENDOR_FEATURE2_SUPPORT */
}

INT RtmpOSNetDevAlloc(
	IN PNET_DEV *new_dev_p,
	IN UINT32 privDataSize)
{
	*new_dev_p = NULL;

	DBGPRINT(RT_DEBUG_TRACE,
		 ("Allocate a net device with private data size=%d!\n",
		  privDataSize));
#if LINUX_VERSION_CODE <= 0x20402	/* Red Hat 7.1 */
	*new_dev_p = alloc_netdev(privDataSize, "eth%d", ether_setup);
#else
	*new_dev_p = alloc_etherdev(privDataSize);
#endif /* LINUX_VERSION_CODE */

	if (*new_dev_p)
		return NDIS_STATUS_SUCCESS;
	else
		return NDIS_STATUS_FAILURE;
}


#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31)
INT RtmpOSNetDevOpsAlloc(PVOID *pNetDevOps)
{
	*pNetDevOps = (PVOID) vmalloc(sizeof (struct net_device_ops));
	if (*pNetDevOps) {
		NdisZeroMemory(*pNetDevOps, sizeof (struct net_device_ops));
		return NDIS_STATUS_SUCCESS;
	} else {
		return NDIS_STATUS_FAILURE;
	}
}
#endif


PNET_DEV RtmpOSNetDevGetByName(PNET_DEV pNetDev, PSTRING pDevName)
{
	PNET_DEV pTargetNetDev = NULL;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
	pTargetNetDev = dev_get_by_name(dev_net(pNetDev), pDevName);
#else
	ASSERT(pNetDev);
	pTargetNetDev = dev_get_by_name(pNetDev->nd_net, pDevName);
#endif
#else
	pTargetNetDev = dev_get_by_name(pDevName);
#endif /* KERNEL_VERSION(2,6,24) */

#else
	int devNameLen;

	devNameLen = strlen(pDevName);
	ASSERT((devNameLen <= IFNAMSIZ));

	for (pTargetNetDev = dev_base; pTargetNetDev != NULL;
	     pTargetNetDev = pTargetNetDev->next) {
		if (strncmp(pTargetNetDev->name, pDevName, devNameLen) == 0)
			break;
	}
#endif /* KERNEL_VERSION(2,5,0) */

	return pTargetNetDev;
}


void RtmpOSNetDeviceRefPut(PNET_DEV pNetDev)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
	/* 
	   every time dev_get_by_name is called, and it has returned a valid struct 
	   net_device*, dev_put should be called afterwards, because otherwise the 
	   machine hangs when the device is unregistered (since dev->refcnt > 1).
	 */
	if (pNetDev)
		dev_put(pNetDev);
#endif /* LINUX_VERSION_CODE */
}


INT RtmpOSNetDevDestory(VOID *pReserved, PNET_DEV pNetDev)
{

	/* TODO: Need to fix this */
	printk("WARNING: This function(%s) not implement yet!!!\n",
	       __FUNCTION__);
	return 0;
}


void RtmpOSNetDevDetach(PNET_DEV pNetDev)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31)
	struct net_device_ops *pNetDevOps = (struct net_device_ops *)pNetDev->netdev_ops;
#endif
	int ret;

	 ret = rtnl_trylock();

	unregister_netdevice(pNetDev);

	if ( ret )
		rtnl_unlock();

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31)
	vfree(pNetDevOps);
#endif
}


void RtmpOSNetDevProtect(BOOLEAN lock_it)
{

/*
	if (lock_it)
		rtnl_lock();
	else
		rtnl_unlock();
*/
}


#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
static void RALINK_ET_DrvInfoGet(
	struct net_device *pDev,
	struct ethtool_drvinfo *pInfo)
{
	strcpy(pInfo->driver, "RALINK WLAN");


	sprintf(pInfo->bus_info, "CSR 0x%lx", pDev->base_addr);
}

static struct ethtool_ops RALINK_Ethtool_Ops = {
	.get_drvinfo = RALINK_ET_DrvInfoGet,
};
#endif


int RtmpOSNetDevAttach(
	IN UCHAR OpMode,
	IN PNET_DEV pNetDev,
	IN RTMP_OS_NETDEV_OP_HOOK *pDevOpHook)
{
	int ret,
	 rtnl_locked = FALSE;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31)
	struct net_device_ops *pNetDevOps = (struct net_device_ops *)pNetDev->netdev_ops;
#endif

	DBGPRINT(RT_DEBUG_TRACE, ("RtmpOSNetDevAttach()--->\n"));

	/* If we need hook some callback function to the net device structrue, now do it. */
	if (pDevOpHook) {
/*		PRTMP_ADAPTER pAd = NULL; */

/*		GET_PAD_FROM_NET_DEV(pAd, pNetDev); */

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31)
		pNetDevOps->ndo_open = pDevOpHook->open;
		pNetDevOps->ndo_stop = pDevOpHook->stop;
		pNetDevOps->ndo_start_xmit =
		    (HARD_START_XMIT_FUNC) (pDevOpHook->xmit);
		pNetDevOps->ndo_do_ioctl = pDevOpHook->ioctl;
#else
		pNetDev->open = pDevOpHook->open;
		pNetDev->stop = pDevOpHook->stop;
		pNetDev->hard_start_xmit =
		    (HARD_START_XMIT_FUNC) (pDevOpHook->xmit);
		pNetDev->do_ioctl = pDevOpHook->ioctl;
#endif

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
		pNetDev->ethtool_ops = &RALINK_Ethtool_Ops;
#endif

		/* if you don't implement get_stats, just leave the callback function as NULL, a dummy 
		   function will make kernel panic.
		 */
		if (pDevOpHook->get_stats)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31)
			pNetDevOps->ndo_get_stats = pDevOpHook->get_stats;
#else
			pNetDev->get_stats = pDevOpHook->get_stats;
#endif

		/* OS specific flags, here we used to indicate if we are virtual interface */
/*		pNetDev->priv_flags = pDevOpHook->priv_flags; */
		RT_DEV_PRIV_FLAGS_SET(pNetDev, pDevOpHook->priv_flags);

#if (WIRELESS_EXT < 21) && (WIRELESS_EXT >= 12)
/*		pNetDev->get_wireless_stats = rt28xx_get_wireless_stats; */
		pNetDev->get_wireless_stats = pDevOpHook->get_wstats;
#endif

#ifdef CONFIG_STA_SUPPORT
#if WIRELESS_EXT >= 12
		if (OpMode == OPMODE_STA) {
/*			pNetDev->wireless_handlers = &rt28xx_iw_handler_def; */
			pNetDev->wireless_handlers = pDevOpHook->iw_handler;
		}
#endif /*WIRELESS_EXT >= 12 */
#endif /* CONFIG_STA_SUPPORT */

#ifdef CONFIG_APSTA_MIXED_SUPPORT
#if WIRELESS_EXT >= 12
		if (OpMode == OPMODE_AP) {
/*			pNetDev->wireless_handlers = &rt28xx_ap_iw_handler_def; */
			pNetDev->wireless_handlers = pDevOpHook->iw_handler;
		}
#endif /*WIRELESS_EXT >= 12 */
#endif /* CONFIG_APSTA_MIXED_SUPPORT */

		/* copy the net device mac address to the net_device structure. */
		NdisMoveMemory(pNetDev->dev_addr, &pDevOpHook->devAddr[0],
			       MAC_ADDR_LEN);

		rtnl_locked = pDevOpHook->needProtcted;

	}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31)
	pNetDevOps->ndo_validate_addr = NULL;
	/*pNetDev->netdev_ops = ops; */
#else
	pNetDev->validate_addr = NULL;
#endif
#endif

	if (rtnl_locked)
		ret = register_netdevice(pNetDev);
	else
		ret = register_netdev(pNetDev);

	netif_stop_queue(pNetDev);

	DBGPRINT(RT_DEBUG_TRACE, ("<---RtmpOSNetDevAttach(), ret=%d\n", ret));
	if (ret == 0)
		return NDIS_STATUS_SUCCESS;
	else
		return NDIS_STATUS_FAILURE;
}

PNET_DEV RtmpOSNetDevCreate(
	IN INT32 MC_RowID,
	IN UINT32 *pIoctlIF,
	IN INT devType,
	IN INT devNum,
	IN INT privMemSize,
	IN PSTRING pNamePrefix)
{
	struct net_device *pNetDev = NULL;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31)
	struct net_device_ops *pNetDevOps = NULL;
#endif
	int status;

	/* allocate a new network device */
	status = RtmpOSNetDevAlloc(&pNetDev, 0 /*privMemSize */ );
	if (status != NDIS_STATUS_SUCCESS) {
		/* allocation fail, exit */
		DBGPRINT(RT_DEBUG_ERROR, ("Allocate network device fail (%s)...\n", pNamePrefix));
		return NULL;
	}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31)
	status = RtmpOSNetDevOpsAlloc((PVOID) & pNetDevOps);
	if (status != NDIS_STATUS_SUCCESS) {
		/* error! no any available ra name can be used! */
		DBGPRINT(RT_DEBUG_TRACE, ("Allocate net device ops fail!\n"));
		RtmpOSNetDevFree(pNetDev);

		return NULL;
	} else {
		DBGPRINT(RT_DEBUG_TRACE, ("Allocate net device ops success!\n"));
		pNetDev->netdev_ops = pNetDevOps;
	}
#endif
	/* find a available interface name, max 32 interfaces */
	status = RtmpOSNetDevRequestName(MC_RowID, pIoctlIF, pNetDev, pNamePrefix, devNum);
	if (status != NDIS_STATUS_SUCCESS) {
		/* error! no any available ra name can be used! */
		DBGPRINT(RT_DEBUG_ERROR, ("Assign interface name (%s with suffix 0~32) failed...\n",
			  pNamePrefix));
		RtmpOSNetDevFree(pNetDev);

		return NULL;
	} else {
		DBGPRINT(RT_DEBUG_TRACE, ("The name of the new %s interface is %s...\n",
			  pNamePrefix, pNetDev->name));
	}

	return pNetDev;
}



#ifdef CONFIG_AP_SUPPORT
UCHAR VLAN_8023_Header_Copy(
	IN USHORT VLAN_VID,
	IN USHORT VLAN_Priority,
	IN PUCHAR pHeader802_3,
	IN UINT HdrLen,
	OUT PUCHAR pData,
	IN UCHAR FromWhichBSSID,
	IN UCHAR *TPID)
{
	UINT16 TCI;
	UCHAR VLAN_Size = 0;

	if (VLAN_VID != 0) {
		/* need to insert VLAN tag */
		VLAN_Size = LENGTH_802_1Q;

		/* make up TCI field */
		TCI = (VLAN_VID & 0x0fff) | ((VLAN_Priority & 0x7) << 13);

#ifndef RT_BIG_ENDIAN
		TCI = SWAP16(TCI);
#endif /* RT_BIG_ENDIAN */

		/* copy dst + src MAC (12B) */
		memcpy(pData, pHeader802_3, LENGTH_802_3_NO_TYPE);

		/* copy VLAN tag (4B) */
		/* do NOT use memcpy to speed up */
		*(UINT16 *) (pData + LENGTH_802_3_NO_TYPE) = *(UINT16 *) TPID;
		*(UINT16 *) (pData + LENGTH_802_3_NO_TYPE + 2) = TCI;

		/* copy type/len (2B) */
		*(UINT16 *) (pData + LENGTH_802_3_NO_TYPE + LENGTH_802_1Q) =
		    *(UINT16 *) & pHeader802_3[LENGTH_802_3 -
					       LENGTH_802_3_TYPE];

		/* copy tail if exist */
		if (HdrLen > LENGTH_802_3)
			memcpy(pData + LENGTH_802_3 + LENGTH_802_1Q, pHeader802_3 + LENGTH_802_3, HdrLen - LENGTH_802_3);
	}
	else
	{
		/* no VLAN tag is needed to insert */
		memcpy(pData, pHeader802_3, HdrLen);
	}
	
	return VLAN_Size;
}				
#endif /* CONFIG_AP_SUPPORT */


/*
========================================================================
Routine Description:
    Allocate memory for adapter control block.

Arguments:
    pAd					Pointer to our adapter

Return Value:
	NDIS_STATUS_SUCCESS
	NDIS_STATUS_FAILURE
	NDIS_STATUS_RESOURCES

Note:
========================================================================
*/
NDIS_STATUS AdapterBlockAllocateMemory(VOID *handle, VOID **ppAd, UINT32 SizeOfpAd)
{
#ifdef OS_ABL_FUNC_SUPPORT
	/* get offset for sk_buff */
	{
		struct sk_buff *pPkt = NULL;

		pPkt = kmalloc(sizeof (struct sk_buff), GFP_ATOMIC);
		if (pPkt == NULL) {
			*ppAd = NULL;
			return NDIS_STATUS_FAILURE;
		}

		RTPktOffsetData = (ULONG) (&(pPkt->data)) - (ULONG) pPkt;
		RTPktOffsetLen = (ULONG) (&(pPkt->len)) - (ULONG) pPkt;
		RTPktOffsetCB = (ULONG) (pPkt->cb) - (ULONG) pPkt;
		kfree(pPkt);

		DBGPRINT(RT_DEBUG_TRACE, ("packet> data offset = %lu\n", RTPktOffsetData));
		DBGPRINT(RT_DEBUG_TRACE, ("packet> len offset = %lu\n", RTPktOffsetLen));
		DBGPRINT(RT_DEBUG_TRACE, ("packet> cb offset = %lu\n", RTPktOffsetCB));
	}
#endif /* OS_ABL_FUNC_SUPPORT */

/*	*ppAd = (PVOID)vmalloc(sizeof(RTMP_ADAPTER)); //pci_alloc_consistent(pci_dev, sizeof(RTMP_ADAPTER), phy_addr); */
	*ppAd = (PVOID) vmalloc(SizeOfpAd);	/*pci_alloc_consistent(pci_dev, sizeof(RTMP_ADAPTER), phy_addr); */
	if (*ppAd) {
		NdisZeroMemory(*ppAd, SizeOfpAd);
		return NDIS_STATUS_SUCCESS;
	} else
		return NDIS_STATUS_FAILURE;
}


/* ========================================================================== */

UINT RtmpOsWirelessExtVerGet(VOID)
{
	return WIRELESS_EXT;
}


VOID RtmpDrvAllMacPrint(
	IN VOID *pReserved,
	IN UINT32 *pBufMac,
	IN UINT32 AddrStart,
	IN UINT32 AddrEnd,
	IN UINT32 AddrStep)
{
	struct file *file_w;
	PSTRING fileName = "MacDump.txt";
	mm_segment_t orig_fs;
	STRING *msg;
	UINT32 macAddr = 0, macValue = 0;

	os_alloc_mem(NULL, (UCHAR **)&msg, 1024);
	if (!msg)
		return;
	
	orig_fs = get_fs();
	set_fs(KERNEL_DS);

	/* open file */
	file_w = filp_open(fileName, O_WRONLY | O_CREAT, 0);
	if (IS_ERR(file_w)) {
		DBGPRINT(RT_DEBUG_TRACE,
			 ("-->2) %s: Error %ld opening %s\n", __FUNCTION__,
			  -PTR_ERR(file_w), fileName));
	} else {
		if (file_w->f_op && file_w->f_op->write) {
			file_w->f_pos = 0;
			macAddr = AddrStart;

			while (macAddr <= AddrEnd) {
/*				RTMP_IO_READ32(pAd, macAddr, &macValue); // sample */
				macValue = *pBufMac++;
				sprintf(msg, "%04x = %08x\n", macAddr, macValue);

				/* write data to file */
				file_w->f_op->write(file_w, msg, strlen(msg), &file_w->f_pos);

				printk("%s", msg);
				macAddr += AddrStep;
			}
			sprintf(msg, "\nDump all MAC values to %s\n", fileName);
		}
		filp_close(file_w, NULL);
	}
	set_fs(orig_fs);
	os_free_mem(NULL, msg);
}


VOID RtmpDrvAllE2PPrint(
	IN VOID *pReserved,
	IN USHORT *pMacContent,
	IN UINT32 AddrEnd,
	IN UINT32 AddrStep)
{
	struct file *file_w;
	PSTRING fileName = "EEPROMDump.txt";
	mm_segment_t orig_fs;
	STRING *msg;
	USHORT eepAddr = 0;
	USHORT eepValue;

	os_alloc_mem(NULL, (UCHAR **)&msg, 1024);
	if (!msg)
		return;
	
	orig_fs = get_fs();
	set_fs(KERNEL_DS);
	
	/* open file */
	file_w = filp_open(fileName, O_WRONLY | O_CREAT, 0);
	if (IS_ERR(file_w)) {
		DBGPRINT(RT_DEBUG_TRACE,
			 ("-->2) %s: Error %ld opening %s\n", __FUNCTION__,
			  -PTR_ERR(file_w), fileName));
	} else {
		if (file_w->f_op && file_w->f_op->write) {
			file_w->f_pos = 0;
			eepAddr = 0x00;

			while (eepAddr <= AddrEnd) {
				eepValue = *pMacContent;
				sprintf(msg, "%08x = %04x\n", eepAddr, eepValue);

				/* write data to file */
				file_w->f_op->write(file_w, msg, strlen(msg), &file_w->f_pos);

				printk("%s", msg);
				eepAddr += AddrStep;
				pMacContent += (AddrStep >> 1);
			}
			sprintf(msg, "\nDump all EEPROM values to %s\n",
				fileName);
		}
		filp_close(file_w, NULL);
	}
	set_fs(orig_fs);
	os_free_mem(NULL, msg);
}


VOID RtmpDrvAllRFPrint(
	IN VOID *pReserved,
	IN UINT32 *pBuf,
	IN UINT32 BufLen)
{
	struct file *file_w;
	PSTRING fileName = "RFDump.txt";
	mm_segment_t orig_fs;
	UINT32 macAddr = 0, macValue = 0;
	
	orig_fs = get_fs();
	set_fs(KERNEL_DS);

	/* open file */
	file_w = filp_open(fileName, O_WRONLY | O_CREAT, 0);
	if (IS_ERR(file_w)) {
		DBGPRINT(RT_DEBUG_TRACE,
			 ("-->2) %s: Error %ld opening %s\n", __FUNCTION__,
			  -PTR_ERR(file_w), fileName));
	} else {
		if (file_w->f_op && file_w->f_op->write) {
			file_w->f_pos = 0;
			/* write data to file */
			file_w->f_op->write(file_w, pBuf, BufLen, &file_w->f_pos);
		}
		filp_close(file_w, NULL);
	}
	set_fs(orig_fs);
}

/*
========================================================================
Routine Description:
	Check if the network interface is up.

Arguments:
	*pDev			- Network Interface

Return Value:
	None

Note:
========================================================================
*/
BOOLEAN RtmpOSNetDevIsUp(VOID *pDev)
{
	struct net_device *pNetDev = (struct net_device *)pDev;

	if ((pNetDev == NULL) || !(pNetDev->flags & IFF_UP))
		return FALSE;

	return TRUE;
}


/*
========================================================================
Routine Description:
	Wake up the command thread.

Arguments:
	pAd				- WLAN control block pointer

Return Value:
	None

Note:
========================================================================
*/
VOID RtmpOsCmdUp(RTMP_OS_TASK *pCmdQTask)
{
	OS_TASK *pTask = RTMP_OS_TASK_GET(pCmdQTask);
#ifdef KTHREAD_SUPPORT
	pTask->kthread_running = TRUE;
	wake_up(&pTask->kthread_q);
#else
	CHECK_PID_LEGALITY(pTask->taskPID) {
		RTMP_SEM_EVENT_UP(&(pTask->taskSema));
	}
#endif /* KTHREAD_SUPPORT */
}


/*
========================================================================
Routine Description:
	Wake up USB Mlme thread.

Arguments:
	pAd				- WLAN control block pointer

Return Value:
	None

Note:
========================================================================
*/
VOID RtmpOsMlmeUp(IN RTMP_OS_TASK *pMlmeQTask)
{
#ifdef RTMP_USB_SUPPORT
	OS_TASK *pTask = RTMP_OS_TASK_GET(pMlmeQTask);

#ifdef KTHREAD_SUPPORT
	if ((pTask != NULL) && (pTask->kthread_task)) {
		pTask->kthread_running = TRUE;
		wake_up(&pTask->kthread_q);
	}
#else
	if (pTask != NULL) {
		CHECK_PID_LEGALITY(pTask->taskPID) {
			RTMP_SEM_EVENT_UP(&(pTask->taskSema));
		}
	}
#endif /* KTHREAD_SUPPORT */
#endif /* RTMP_USB_SUPPORT */
}


/*
========================================================================
Routine Description:
	Check if the file is error.

Arguments:
	pFile			- the file

Return Value:
	OK or any error

Note:
	rt_linux.h, not rt_drv.h
========================================================================
*/
INT32 RtmpOsFileIsErr(IN VOID *pFile)
{
	return IS_FILE_OPEN_ERR(pFile);
}

int RtmpOSIRQRelease(
	IN PNET_DEV pNetDev,
	IN UINT32 infType,
	IN PPCI_DEV pci_dev,
	IN BOOLEAN *pHaveMsi)
{
	struct net_device *net_dev = (struct net_device *)pNetDev;



	return 0;
}


/*
========================================================================
Routine Description:
	Enable or disable wireless event sent.

Arguments:
	pReserved		- Reserved
	FlgIsWEntSup	- TRUE or FALSE

Return Value:
	None

Note:
========================================================================
*/
VOID RtmpOsWlanEventSet(
	IN VOID *pReserved,
	IN BOOLEAN *pCfgWEnt,
	IN BOOLEAN FlgIsWEntSup)
{
#if WIRELESS_EXT >= 15
/*	pAd->CommonCfg.bWirelessEvent = FlgIsWEntSup; */
	*pCfgWEnt = FlgIsWEntSup;
#else
	*pCfgWEnt = 0;		/* disable */
#endif
}

/*
========================================================================
Routine Description:
	vmalloc

Arguments:
	Size			- memory size

Return Value:
	the memory

Note:
========================================================================
*/
VOID *RtmpOsVmalloc(ULONG Size)
{
	return vmalloc(Size);
}

/*
========================================================================
Routine Description:
	vfree

Arguments:
	pMem			- the memory

Return Value:
	None

Note:
========================================================================
*/
VOID RtmpOsVfree(VOID *pMem)
{
	if (pMem != NULL)
		vfree(pMem);
}

/*
========================================================================
Routine Description:
	Get network interface name.

Arguments:
	pDev			- the device

Return Value:
	the name

Note:
========================================================================
*/
char *RtmpOsGetNetDevName(VOID *pDev)
{
	return ((PNET_DEV) pDev)->name;
}

/*
========================================================================
Routine Description:
	Assign protocol to the packet.

Arguments:
	pPkt			- the packet

Return Value:
	None

Note:
========================================================================
*/
VOID RtmpOsPktProtocolAssign(PNDIS_PACKET pNetPkt)
{
	struct sk_buff *pRxPkt = RTPKT_TO_OSPKT(pNetPkt);
	pRxPkt->protocol = eth_type_trans(pRxPkt, pRxPkt->dev);
}


BOOLEAN RtmpOsStatsAlloc(
	IN VOID **ppStats,
	IN VOID **ppIwStats)
{
	os_alloc_mem(NULL, (UCHAR **) ppStats, sizeof (struct net_device_stats));
	if ((*ppStats) == NULL)
		return FALSE;
	NdisZeroMemory((UCHAR *) *ppStats, sizeof (struct net_device_stats));

#if WIRELESS_EXT >= 12
	os_alloc_mem(NULL, (UCHAR **) ppIwStats, sizeof (struct iw_statistics));
	if ((*ppIwStats) == NULL) {
		os_free_mem(NULL, *ppStats);
		return FALSE;
	}
	NdisZeroMemory((UCHAR *)* ppIwStats, sizeof (struct iw_statistics));
#endif

	return TRUE;
}

/*
========================================================================
Routine Description:
	Pass the received packet to OS.

Arguments:
	pPkt			- the packet

Return Value:
	None

Note:
========================================================================
*/
VOID RtmpOsPktRcvHandle(PNDIS_PACKET pNetPkt)
{
	struct sk_buff *pRxPkt = RTPKT_TO_OSPKT(pNetPkt);

#ifdef CONFIG_RX_CSO_SUPPORT

	if (RTMP_GET_TCP_CHKSUM_FAIL(pNetPkt))
		pRxPkt->ip_summed = CHECKSUM_NONE;
	else
		pRxPkt->ip_summed = CHECKSUM_UNNECESSARY;

#endif

	netif_rx(pRxPkt);
}


VOID RtmpOsTaskPidInit(RTMP_OS_PID *pPid)
{
	*pPid = THREAD_PID_INIT_VALUE;
}

/*
========================================================================
Routine Description:
	Get the network interface for the packet.

Arguments:
	pPkt			- the packet

Return Value:
	None

Note:
========================================================================
*/
PNET_DEV RtmpOsPktNetDevGet(VOID *pPkt)
{
	return GET_OS_PKT_NETDEV(pPkt);
}


#ifdef IAPP_SUPPORT
/* Layer 2 Update frame to switch/bridge */
/* For any Layer2 devices, e.g., bridges, switches and other APs, the frame
   can update their forwarding tables with the correct port to reach the new
   location of the STA */
typedef struct GNU_PACKED _RT_IAPP_L2_UPDATE_FRAME {

	UCHAR DA[ETH_ALEN];	/* broadcast MAC address */
	UCHAR SA[ETH_ALEN];	/* the MAC address of the STA that has just associated
				   or reassociated */
	USHORT Len;		/* 8 octets */
	UCHAR DSAP;		/* null */
	UCHAR SSAP;		/* null */
	UCHAR Control;		/* reference to IEEE Std 802.2 */
	UCHAR XIDInfo[3];	/* reference to IEEE Std 802.2 */
} RT_IAPP_L2_UPDATE_FRAME, *PRT_IAPP_L2_UPDATE_FRAME;


PNDIS_PACKET RtmpOsPktIappMakeUp(
	IN PNET_DEV pNetDev,
	IN UINT8 *pMac)
{
	RT_IAPP_L2_UPDATE_FRAME frame_body;
	INT size = sizeof (RT_IAPP_L2_UPDATE_FRAME);
	PNDIS_PACKET pNetBuf;

	if (pNetDev == NULL)
		return NULL;

	pNetBuf = RtmpOSNetPktAlloc(NULL, size);
	if (!pNetBuf) {
		DBGPRINT(RT_DEBUG_ERROR, ("Error! Can't allocate a skb.\n"));
		return NULL;
	}

	/* init the update frame body */
	NdisZeroMemory(&frame_body, size);

	memset(frame_body.DA, 0xFF, ETH_ALEN);
	memcpy(frame_body.SA, pMac, ETH_ALEN);

	frame_body.Len = OS_HTONS(ETH_ALEN);
	frame_body.DSAP = 0;
	frame_body.SSAP = 0x01;
	frame_body.Control = 0xAF;

	frame_body.XIDInfo[0] = 0x81;
	frame_body.XIDInfo[1] = 1;
	frame_body.XIDInfo[2] = 1 << 1;

	SET_OS_PKT_NETDEV(pNetBuf, pNetDev);
	skb_reserve(pNetBuf, 2);
	memcpy(skb_put(pNetBuf, size), &frame_body, size);
	return pNetBuf;
}
#endif /* IAPP_SUPPORT */


VOID RtmpOsPktNatMagicTag(PNDIS_PACKET pNetPkt)
{
}

VOID RtmpOsPktNatNone(PNDIS_PACKET pNetPkt)
{
}


#ifdef RT_CFG80211_SUPPORT
/* all available channels */
UCHAR Cfg80211_Chan[] = {
	1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,

	/* 802.11 UNI / HyperLan 2 */
	36, 38, 40, 44, 46, 48, 52, 54, 56, 60, 62, 64,

	/* 802.11 HyperLan 2 */
	100, 104, 108, 112, 116, 118, 120, 124, 126, 128, 132, 134, 136,

	/* 802.11 UNII */
	140, 149, 151, 153, 157, 159, 161, 165, 167, 169, 171, 173,

	/* Japan */
	184, 188, 192, 196, 208, 212, 216,
};

/*
	Array of bitrates the hardware can operate with
	in this band. Must be sorted to give a valid "supported
	rates" IE, i.e. CCK rates first, then OFDM.

	For HT, assign MCS in another structure, ieee80211_sta_ht_cap.
*/
const struct ieee80211_rate Cfg80211_SupRate[12] = {
	{
		.flags = IEEE80211_RATE_SHORT_PREAMBLE,
		.bitrate = 10,
		.hw_value = 0,
		.hw_value_short = 0,
	},
	{
		.flags = IEEE80211_RATE_SHORT_PREAMBLE,
		.bitrate = 20,
		.hw_value = 1,
		.hw_value_short = 1,
	},
	{
		.flags = IEEE80211_RATE_SHORT_PREAMBLE,
		.bitrate = 55,
		.hw_value = 2,
		.hw_value_short = 2,
	},
	{
		.flags = IEEE80211_RATE_SHORT_PREAMBLE,
		.bitrate = 110,
		.hw_value = 3,
		.hw_value_short = 3,
	},
	{
		.flags = 0,
		.bitrate = 60,
		.hw_value = 4,
		.hw_value_short = 4,
	},
	{
		.flags = 0,
		.bitrate = 90,
		.hw_value = 5,
		.hw_value_short = 5,
	},
	{
		.flags = 0,
		.bitrate = 120,
		.hw_value = 6,
		.hw_value_short = 6,
	},
	{
		.flags = 0,
		.bitrate = 180,
		.hw_value = 7,
		.hw_value_short = 7,
	},
	{
		.flags = 0,
		.bitrate = 240,
		.hw_value = 8,
		.hw_value_short = 8,
	},
	{
		.flags = 0,
		.bitrate = 360,
		.hw_value = 9,
		.hw_value_short = 9,
	},
	{
		.flags = 0,
		.bitrate = 480,
		.hw_value = 10,
		.hw_value_short = 10,
	},
	{
		.flags = 0,
		.bitrate = 540,
		.hw_value = 11,
		.hw_value_short = 11,
	},
};

static const UINT32 CipherSuites[] = {
	WLAN_CIPHER_SUITE_WEP40,
	WLAN_CIPHER_SUITE_WEP104,
	WLAN_CIPHER_SUITE_TKIP,
	WLAN_CIPHER_SUITE_CCMP,
};

/*
========================================================================
Routine Description:
	UnRegister MAC80211 Module.

Arguments:
	pCB				- CFG80211 control block pointer
	pNetDev			- Network device

Return Value:
	NONE

Note:
========================================================================
*/
VOID CFG80211OS_UnRegister(
	IN VOID *pCB,
	IN VOID *pNetDevOrg)
{
	CFG80211_CB *pCfg80211_CB = (CFG80211_CB *)pCB;
	struct net_device *pNetDev = (struct net_device *)pNetDevOrg;


	/* unregister */
	if (pCfg80211_CB->pCfg80211_Wdev != NULL)
	{
		DBGPRINT(RT_DEBUG_ERROR, ("80211> unregister/free wireless device\n"));

		/*
			Must unregister, or you will suffer problem when you change
			regulatory domain by using iw.
		*/
		
#ifdef RFKILL_HW_SUPPORT
		wiphy_rfkill_stop_polling(pCfg80211_CB->pCfg80211_Wdev->wiphy);
#endif /* RFKILL_HW_SUPPORT */
		wiphy_unregister(pCfg80211_CB->pCfg80211_Wdev->wiphy);
		wiphy_free(pCfg80211_CB->pCfg80211_Wdev->wiphy);
		kfree(pCfg80211_CB->pCfg80211_Wdev);

		if (pCfg80211_CB->pCfg80211_Channels != NULL)
			kfree(pCfg80211_CB->pCfg80211_Channels);

		if (pCfg80211_CB->pCfg80211_Rates != NULL)
			kfree(pCfg80211_CB->pCfg80211_Rates);

		pCfg80211_CB->pCfg80211_Wdev = NULL;
		pCfg80211_CB->pCfg80211_Channels = NULL;
		pCfg80211_CB->pCfg80211_Rates = NULL;

		/* must reset to NULL; or kernel will panic in unregister_netdev */
		pNetDev->ieee80211_ptr = NULL;
		SET_NETDEV_DEV(pNetDev, NULL);
	}

	os_free_mem(NULL, pCfg80211_CB);
}


/*
========================================================================
Routine Description:
	Initialize wireless channel in 2.4GHZ and 5GHZ.

Arguments:
	pAd				- WLAN control block pointer
	pWiphy			- WLAN PHY interface
	pChannels		- Current channel info
	pRates			- Current rate info

Return Value:
	TRUE			- init successfully
	FALSE			- init fail

Note:
	TX Power related:

	1. Suppose we can send power to 15dBm in the board.
	2. A value 0x0 ~ 0x1F for a channel. We will adjust it based on 15dBm/
		54Mbps. So if value == 0x07, the TX power of 54Mbps is 15dBm and
		the value is 0x07 in the EEPROM.
	3. Based on TX power value of 54Mbps/channel, adjust another value
		0x0 ~ 0xF for other data rate. (-6dBm ~ +6dBm)

	Other related factors:
	1. TX power percentage from UI/users;
	2. Maximum TX power limitation in the regulatory domain.
========================================================================
*/
BOOLEAN CFG80211_SupBandInit(
	IN VOID *pCB,
	IN CFG80211_BAND *pBandInfo,
	IN VOID *pWiphyOrg,
	IN VOID *pChannelsOrg,
	IN VOID *pRatesOrg)
{
	CFG80211_CB *pCfg80211_CB = (CFG80211_CB *)pCB;
	struct wiphy *pWiphy = (struct wiphy *)pWiphyOrg;
	struct ieee80211_channel *pChannels = (struct ieee80211_channel *)pChannelsOrg;
	struct ieee80211_rate *pRates = (struct ieee80211_rate *)pRatesOrg;
	struct ieee80211_supported_band *pBand;
	UINT32 NumOfChan, NumOfRate;
	UINT32 IdLoop;
	UINT32 CurTxPower;


	/* sanity check */
	if (pBandInfo->RFICType == 0)
		pBandInfo->RFICType = RFIC_24GHZ | RFIC_5GHZ;

	CFG80211DBG(RT_DEBUG_ERROR, ("80211> RFICType = %d\n",
				pBandInfo->RFICType));

	/* init */
	if (pBandInfo->RFICType & RFIC_5GHZ)
		NumOfChan = CFG80211_NUM_OF_CHAN_2GHZ + CFG80211_NUM_OF_CHAN_5GHZ;
	else
		NumOfChan = CFG80211_NUM_OF_CHAN_2GHZ;

	if (pBandInfo->FlgIsBMode == TRUE)
		NumOfRate = 4;
	else
		NumOfRate = 4 + 8;

	if (pChannels == NULL)
	{
		pChannels = kzalloc(sizeof(*pChannels) * NumOfChan, GFP_KERNEL);
		if (!pChannels)
		{
			DBGPRINT(RT_DEBUG_ERROR, ("80211> ieee80211_channel allocation fail!\n"));
			return FALSE;
		}
	}

	CFG80211DBG(RT_DEBUG_ERROR, ("80211> Number of channel = %d\n",
				CFG80211_NUM_OF_CHAN_5GHZ));

	if (pRates == NULL)
	{
		pRates = kzalloc(sizeof(*pRates) * NumOfRate, GFP_KERNEL);
		if (!pRates)
		{
			os_free_mem(NULL, pChannels);
			DBGPRINT(RT_DEBUG_ERROR, ("80211> ieee80211_rate allocation fail!\n"));
			return FALSE;
		}
	}

	CFG80211DBG(RT_DEBUG_ERROR, ("80211> Number of rate = %d\n", NumOfRate));

	/* get TX power */
#ifdef SINGLE_SKU
	CurTxPower = pBandInfo->DefineMaxTxPwr; /* dBm */
#else
	CurTxPower = 0; /* unknown */
#endif /* SINGLE_SKU */

	CFG80211DBG(RT_DEBUG_ERROR, ("80211> CurTxPower = %d dBm\n", CurTxPower));

	/* init channel */
	for(IdLoop=0; IdLoop<NumOfChan; IdLoop++)
	{
		pChannels[IdLoop].center_freq = \
					ieee80211_channel_to_frequency(Cfg80211_Chan[IdLoop]);
		pChannels[IdLoop].hw_value = IdLoop;

		if (IdLoop < CFG80211_NUM_OF_CHAN_2GHZ)
			pChannels[IdLoop].max_power = CurTxPower;
		else
			pChannels[IdLoop].max_power = CurTxPower;

		pChannels[IdLoop].max_antenna_gain = 0xff;
	}

	/* init rate */
	for(IdLoop=0; IdLoop<NumOfRate; IdLoop++)
		memcpy(&pRates[IdLoop], &Cfg80211_SupRate[IdLoop], sizeof(*pRates));

	pBand = &pCfg80211_CB->Cfg80211_bands[IEEE80211_BAND_2GHZ];
	if (pBandInfo->RFICType & RFIC_24GHZ)
	{
		pBand->n_channels = CFG80211_NUM_OF_CHAN_2GHZ;
		pBand->n_bitrates = NumOfRate;
		pBand->channels = pChannels;
		pBand->bitrates = pRates;

#ifdef DOT11_N_SUPPORT
		/* for HT, assign pBand->ht_cap */
		pBand->ht_cap.ht_supported = true;
		pBand->ht_cap.cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
					       IEEE80211_HT_CAP_SM_PS |
					       IEEE80211_HT_CAP_SGI_40 |
					       IEEE80211_HT_CAP_DSSSCCK40;
		pBand->ht_cap.ampdu_factor = 3; /* 2 ^ 16 */
		pBand->ht_cap.ampdu_density = pBandInfo->MpduDensity;

		memset(&pBand->ht_cap.mcs, 0, sizeof(pBand->ht_cap.mcs));
		CFG80211DBG(RT_DEBUG_ERROR,
					("80211> TxStream = %d\n", pBandInfo->TxStream));

		switch(pBandInfo->TxStream)
		{
			case 1:
			default:
				pBand->ht_cap.mcs.rx_mask[0] = 0xff;
				break;

			case 2:
				pBand->ht_cap.mcs.rx_mask[0] = 0xff;
				pBand->ht_cap.mcs.rx_mask[1] = 0xff;
				break;

			case 3:
				pBand->ht_cap.mcs.rx_mask[0] = 0xff;
				pBand->ht_cap.mcs.rx_mask[1] = 0xff;
				pBand->ht_cap.mcs.rx_mask[2] = 0xff;
				break;
		}

		pBand->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
#endif /* DOT11_N_SUPPORT */

		pWiphy->bands[IEEE80211_BAND_2GHZ] = pBand;
	}
	else
	{
		pWiphy->bands[IEEE80211_BAND_2GHZ] = NULL;
		pBand->channels = NULL;
		pBand->bitrates = NULL;
	}

	pBand = &pCfg80211_CB->Cfg80211_bands[IEEE80211_BAND_5GHZ];
	if (pBandInfo->RFICType & RFIC_5GHZ)
	{
		pBand->n_channels = CFG80211_NUM_OF_CHAN_5GHZ;
		pBand->n_bitrates = NumOfRate - 4;
		pBand->channels = &pChannels[CFG80211_NUM_OF_CHAN_2GHZ];
		pBand->bitrates = &pRates[4];

		/* for HT, assign pBand->ht_cap */
#ifdef DOT11_N_SUPPORT
		/* for HT, assign pBand->ht_cap */
		pBand->ht_cap.ht_supported = true;
		pBand->ht_cap.cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
					       IEEE80211_HT_CAP_SM_PS |
					       IEEE80211_HT_CAP_SGI_40 |
					       IEEE80211_HT_CAP_DSSSCCK40;
		pBand->ht_cap.ampdu_factor = 3; /* 2 ^ 16 */
		pBand->ht_cap.ampdu_density = pBandInfo->MpduDensity;

		memset(&pBand->ht_cap.mcs, 0, sizeof(pBand->ht_cap.mcs));
		switch(pBandInfo->RxStream)
		{
			case 1:
			default:
				pBand->ht_cap.mcs.rx_mask[0] = 0xff;
				break;

			case 2:
				pBand->ht_cap.mcs.rx_mask[0] = 0xff;
				pBand->ht_cap.mcs.rx_mask[1] = 0xff;
				break;

			case 3:
				pBand->ht_cap.mcs.rx_mask[0] = 0xff;
				pBand->ht_cap.mcs.rx_mask[1] = 0xff;
				pBand->ht_cap.mcs.rx_mask[2] = 0xff;
				break;
		}

		pBand->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
#endif /* DOT11_N_SUPPORT */

		pWiphy->bands[IEEE80211_BAND_5GHZ] = pBand;
	}
	else
	{
		pWiphy->bands[IEEE80211_BAND_5GHZ] = NULL;
		pBand->channels = NULL;
		pBand->bitrates = NULL;
	}

	pCfg80211_CB->pCfg80211_Channels = pChannels;
	pCfg80211_CB->pCfg80211_Rates = pRates;

	return TRUE;
}


/*
========================================================================
Routine Description:
	Re-Initialize wireless channel/PHY in 2.4GHZ and 5GHZ.

Arguments:
	pCB				- CFG80211 control block pointer
	pBandInfo		- Band information

Return Value:
	TRUE			- re-init successfully
	FALSE			- re-init fail

Note:
	CFG80211_SupBandInit() is called in xx_probe().
	But we do not have complete chip information in xx_probe() so we
	need to re-init bands in xx_open().
========================================================================
*/
BOOLEAN CFG80211OS_SupBandReInit(
	IN VOID *pCB,
	IN CFG80211_BAND *pBandInfo)
{
	CFG80211_CB *pCfg80211_CB = (CFG80211_CB *)pCB;
	struct wiphy *pWiphy;


	if ((pCfg80211_CB == NULL) || (pCfg80211_CB->pCfg80211_Wdev == NULL))
		return FALSE;

	pWiphy = pCfg80211_CB->pCfg80211_Wdev->wiphy;

	if (pWiphy != NULL)
	{
		CFG80211DBG(RT_DEBUG_ERROR, ("80211> re-init bands...\n"));

		/* re-init bands */
		CFG80211_SupBandInit(pCfg80211_CB, pBandInfo, pWiphy,
							pCfg80211_CB->pCfg80211_Channels,
							pCfg80211_CB->pCfg80211_Rates);

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32))
		/* re-init PHY */
		pWiphy->rts_threshold = pBandInfo->RtsThreshold;
		pWiphy->frag_threshold = pBandInfo->FragmentThreshold;
		pWiphy->retry_short = pBandInfo->RetryMaxCnt & 0xff;
		pWiphy->retry_long = (pBandInfo->RetryMaxCnt & 0xff00)>>8;
#endif /* LINUX_VERSION_CODE */

		return TRUE;
	}

	return FALSE;
}


/*
========================================================================
Routine Description:
	Hint to the wireless core a regulatory domain from driver.

Arguments:
	pAd				- WLAN control block pointer
	pCountryIe		- pointer to the country IE
	CountryIeLen	- length of the country IE

Return Value:
	NONE

Note:
	Must call the function in kernel thread.
========================================================================
*/
VOID CFG80211OS_RegHint(
	IN VOID *pCB,
	IN UCHAR *pCountryIe,
	IN ULONG CountryIeLen)
{
	CFG80211_CB *pCfg80211_CB = (CFG80211_CB *)pCB;


	CFG80211DBG(RT_DEBUG_ERROR,
			("crda> regulatory domain hint: %c%c\n",
			pCountryIe[0], pCountryIe[1]));

	if ((pCfg80211_CB->pCfg80211_Wdev == NULL) || (pCountryIe == NULL))
	{
		CFG80211DBG(RT_DEBUG_ERROR, ("crda> regulatory domain hint not support!\n"));
		return;
	}

	/* hints a country IE as a regulatory domain "without" channel/power info. */
/*	regulatory_hint(pCfg80211_CB->pMac80211_Hw->wiphy, pCountryIe); */
	regulatory_hint(pCfg80211_CB->pCfg80211_Wdev->wiphy, (const char *)pCountryIe);
}


/*
========================================================================
Routine Description:
	Hint to the wireless core a regulatory domain from country element.

Arguments:
	pAdCB			- WLAN control block pointer
	pCountryIe		- pointer to the country IE
	CountryIeLen	- length of the country IE

Return Value:
	NONE

Note:
	Must call the function in kernel thread.
========================================================================
*/
VOID CFG80211OS_RegHint11D(
	IN VOID *pCB,
	IN UCHAR *pCountryIe,
	IN ULONG CountryIeLen)
{
	/* no regulatory_hint_11d() in 2.6.32 */
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32))
	CFG80211_CB *pCfg80211_CB = (CFG80211_CB *)pCB;


	if ((pCfg80211_CB->pCfg80211_Wdev == NULL) || (pCountryIe == NULL))
	{
		CFG80211DBG(RT_DEBUG_ERROR, ("crda> regulatory domain hint not support!\n"));
		return;
	}

	CFG80211DBG(RT_DEBUG_ERROR,
				("crda> regulatory domain hint: %c%c\n",
				pCountryIe[0], pCountryIe[1]));

	/*
		hints a country IE as a regulatory domain "with" channel/power info.
		but if you use regulatory_hint(), it only hint "regulatory domain".
	*/
/*	regulatory_hint_11d(pCfg80211_CB->pMac80211_Hw->wiphy, pCountryIe, CountryIeLen); */
	regulatory_hint_11d(pCfg80211_CB->pCfg80211_Wdev->wiphy, pCountryIe, CountryIeLen);
#endif /* LINUX_VERSION_CODE */
}


BOOLEAN CFG80211OS_BandInfoGet(
	IN VOID *pCB,
	IN VOID *pWiphyOrg,
	OUT VOID **ppBand24,
	OUT VOID **ppBand5)
{
	CFG80211_CB *pCfg80211_CB = (CFG80211_CB *)pCB;
	struct wiphy *pWiphy = (struct wiphy *)pWiphyOrg;


	if (pWiphy == NULL)
	{
		if ((pCfg80211_CB != NULL) && (pCfg80211_CB->pCfg80211_Wdev != NULL))
			pWiphy = pCfg80211_CB->pCfg80211_Wdev->wiphy;
	}

	if (pWiphy == NULL)
		return FALSE;

	*ppBand24 = pWiphy->bands[IEEE80211_BAND_2GHZ];
	*ppBand5 = pWiphy->bands[IEEE80211_BAND_5GHZ];
	return TRUE;
}


UINT32 CFG80211OS_ChanNumGet(
	IN VOID						*pCB,
	IN VOID						*pWiphyOrg,
	IN UINT32					IdBand)
{
	CFG80211_CB *pCfg80211_CB = (CFG80211_CB *)pCB;
	struct wiphy *pWiphy = (struct wiphy *)pWiphyOrg;


	if (pWiphy == NULL)
	{
		if ((pCfg80211_CB != NULL) && (pCfg80211_CB->pCfg80211_Wdev != NULL))
			pWiphy = pCfg80211_CB->pCfg80211_Wdev->wiphy;
	}

	if (pWiphy == NULL)
		return 0;

	if (pWiphy->bands[IdBand] != NULL)
		return pWiphy->bands[IdBand]->n_channels;

	return 0;
}


BOOLEAN CFG80211OS_ChanInfoGet(
	IN VOID						*pCB,
	IN VOID						*pWiphyOrg,
	IN UINT32					IdBand,
	IN UINT32					IdChan,
	OUT UINT32					*pChanId,
	OUT UINT32					*pPower,
	OUT BOOLEAN					*pFlgIsRadar)
{
	CFG80211_CB *pCfg80211_CB = (CFG80211_CB *)pCB;
	struct wiphy *pWiphy = (struct wiphy *)pWiphyOrg;
	struct ieee80211_supported_band *pSband;
	struct ieee80211_channel *pChan;


	if (pWiphy == NULL)
	{
		if ((pCfg80211_CB != NULL) && (pCfg80211_CB->pCfg80211_Wdev != NULL))
			pWiphy = pCfg80211_CB->pCfg80211_Wdev->wiphy;
	}

	if (pWiphy == NULL)
		return FALSE;

	pSband = pWiphy->bands[IdBand];
	pChan = &pSband->channels[IdChan];

	*pChanId = ieee80211_frequency_to_channel(pChan->center_freq);

	if (pChan->flags & IEEE80211_CHAN_DISABLED)
	{
		CFG80211DBG(RT_DEBUG_ERROR,
					("Chan %03d (frq %d):\tnot allowed!\n",
					(*pChanId), pChan->center_freq));
		return FALSE;
	}

	*pPower = pChan->max_power;

	if (pChan->flags & IEEE80211_CHAN_RADAR)
		*pFlgIsRadar = TRUE;
	else
		*pFlgIsRadar = FALSE;

	return TRUE;
}


/*
========================================================================
Routine Description:
	Initialize a channel information used in scan inform.

Arguments:

Return Value:
	TRUE		- Successful
	FALSE		- Fail

Note:
========================================================================
*/
BOOLEAN CFG80211OS_ChanInfoInit(
	IN VOID						*pCB,
	IN UINT32					InfoIndex,
	IN UCHAR					ChanId,
	IN UCHAR					MaxTxPwr,
	IN BOOLEAN					FlgIsNMode,
	IN BOOLEAN					FlgIsBW20M)
{
	CFG80211_CB *pCfg80211_CB = (CFG80211_CB *)pCB;
	struct ieee80211_channel *pChan;


	if (InfoIndex >= MAX_NUM_OF_CHANNELS)
		return FALSE;

	pChan = (struct ieee80211_channel *)&(pCfg80211_CB->ChanInfo[InfoIndex]);
	memset(pChan, 0, sizeof(*pChan));

	if (ChanId > 14)
		pChan->band = IEEE80211_BAND_5GHZ;
	else
		pChan->band = IEEE80211_BAND_2GHZ;

	pChan->center_freq = ieee80211_channel_to_frequency(ChanId);

#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32))
	if (FlgIsNMode == TRUE)
	{
		if (FlgIsBW20M == TRUE)
			pChan->max_bandwidth = 20; /* 20MHz */
		else
			pChan->max_bandwidth = 40; /* 40MHz */
	}
	else
		pChan->max_bandwidth = 5; /* 5MHz for non-HT device */
#endif /* LINUX_VERSION_CODE */

	/* no use currently in 2.6.30 */
/*	if (ieee80211_is_beacon(((struct ieee80211_mgmt *)pFrame)->frame_control)) */
/*		pChan->beacon_found = 1; */

	return TRUE;
}


/*
========================================================================
Routine Description:
	Inform us that a scan is got.

Arguments:
	pAdCB				- WLAN control block pointer

Return Value:
	NONE

Note:
	Call RT_CFG80211_SCANNING_INFORM, not CFG80211_Scaning
========================================================================
*/
VOID CFG80211OS_Scaning(
	IN VOID						*pCB,
	IN UINT32					ChanId,
	IN UCHAR					*pFrame,
	IN UINT32					FrameLen,
	IN INT32					RSSI,
	IN BOOLEAN					FlgIsNMode,
	IN UINT8					BW)
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30))
#ifdef CONFIG_STA_SUPPORT
	CFG80211_CB *pCfg80211_CB = (CFG80211_CB *)pCB;
	UINT32 IdChan;
	UINT32 CenFreq;


	/* get channel information */
	CenFreq = ieee80211_channel_to_frequency(ChanId);

	for(IdChan=0; IdChan<MAX_NUM_OF_CHANNELS; IdChan++)
	{
		if (pCfg80211_CB->ChanInfo[IdChan].center_freq == CenFreq)
			break;
	}
	if (IdChan >= MAX_NUM_OF_CHANNELS)
	{
		DBGPRINT(RT_DEBUG_ERROR, ("80211> Can not find any chan info!\n"));
		return;
	}

	/* inform 80211 a scan is got */
	/* we can use cfg80211_inform_bss in 2.6.31, it is easy more than the one */
	/* in cfg80211_inform_bss_frame(), it will memcpy pFrame but pChan */
	cfg80211_inform_bss_frame(pCfg80211_CB->pCfg80211_Wdev->wiphy,
								&pCfg80211_CB->ChanInfo[IdChan],
								(struct ieee80211_mgmt *)pFrame,
								FrameLen,
								RSSI,
								GFP_ATOMIC);

	CFG80211DBG(RT_DEBUG_ERROR, ("80211> cfg80211_inform_bss_frame\n"));
#endif /* CONFIG_STA_SUPPORT */
#endif /* LINUX_VERSION_CODE */
}


/*
========================================================================
Routine Description:
	Inform us that scan ends.

Arguments:
	pAdCB			- WLAN control block pointer
	FlgIsAborted	- 1: scan is aborted

Return Value:
	NONE

Note:
========================================================================
*/
VOID CFG80211OS_ScanEnd(
	IN VOID *pCB,
	IN BOOLEAN FlgIsAborted)
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30))
#ifdef CONFIG_STA_SUPPORT
	CFG80211_CB *pCfg80211_CB = (CFG80211_CB *)pCB;


	CFG80211DBG(RT_DEBUG_ERROR, ("80211> cfg80211_scan_done\n"));
	cfg80211_scan_done(pCfg80211_CB->pCfg80211_ScanReq, FlgIsAborted);
#endif /* CONFIG_STA_SUPPORT */
#endif /* LINUX_VERSION_CODE */
}


/*
========================================================================
Routine Description:
	Inform CFG80211 about association status.

Arguments:
	pAdCB			- WLAN control block pointer
	pBSSID			- the BSSID of the AP
	pReqIe			- the element list in the association request frame
	ReqIeLen		- the request element length
	pRspIe			- the element list in the association response frame
	RspIeLen		- the response element length
	FlgIsSuccess	- 1: success; otherwise: fail

Return Value:
	None

Note:
========================================================================
*/
void CFG80211OS_ConnectResultInform(
	IN VOID *pCB,
	IN UCHAR *pBSSID,
	IN UCHAR *pReqIe,
	IN UINT32 ReqIeLen,
	IN UCHAR *pRspIe,
	IN UINT32 RspIeLen,
	IN UCHAR FlgIsSuccess)
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32))
	CFG80211_CB *pCfg80211_CB = (CFG80211_CB *)pCB;


	if ((pCfg80211_CB->pCfg80211_Wdev->netdev == NULL) || (pBSSID == NULL))
		return;

	if (FlgIsSuccess)
	{
		cfg80211_connect_result(pCfg80211_CB->pCfg80211_Wdev->netdev,
								pBSSID,
								pReqIe,
								ReqIeLen,
								pRspIe,
								RspIeLen,
								WLAN_STATUS_SUCCESS,
								GFP_KERNEL);
	}
	else
	{
		cfg80211_connect_result(pCfg80211_CB->pCfg80211_Wdev->netdev,
								pBSSID,
								NULL, 0, NULL, 0,
								WLAN_STATUS_UNSPECIFIED_FAILURE,
								GFP_KERNEL);
	}
#endif /* LINUX_VERSION_CODE */
}
#endif /* RT_CFG80211_SUPPORT */


/*
========================================================================
Routine Description:
	Flush a data cache line.

Arguments:
	AddrStart		- the start address
	Size			- memory size

Return Value:
	None

Note:
========================================================================
*/
VOID RtmpOsDCacheFlush(
	IN ULONG AddrStart,
	IN ULONG Size)
{
	RTMP_UTIL_DCACHE_FLUSH(AddrStart, Size);
}


/*
========================================================================
Routine Description:
	Assign private data pointer to the network interface.

Arguments:
	pDev			- the device
	pPriv			- the pointer

Return Value:
	None

Note:
========================================================================
*/
VOID RtmpOsSetNetDevPriv(VOID *pDev, VOID *pPriv)
{
	DEV_PRIV_INFO *pDevInfo = NULL;


	pDevInfo = (DEV_PRIV_INFO *) _RTMP_OS_NETDEV_GET_PRIV((PNET_DEV) pDev);
	if (pDevInfo == NULL)
	{
		os_alloc_mem(NULL, (UCHAR **)&pDevInfo, sizeof(DEV_PRIV_INFO));
		if (pDevInfo == NULL)
			return;
	}

	pDevInfo->pPriv = (VOID *)pPriv;
	pDevInfo->priv_flags = 0;

	_RTMP_OS_NETDEV_SET_PRIV((PNET_DEV) pDev, pDevInfo);
}


/*
========================================================================
Routine Description:
	Get private data pointer from the network interface.

Arguments:
	pDev			- the device
	pPriv			- the pointer

Return Value:
	None

Note:
========================================================================
*/
VOID *RtmpOsGetNetDevPriv(VOID *pDev)
{
	DEV_PRIV_INFO *pDevInfo = NULL;


	pDevInfo = (DEV_PRIV_INFO *) _RTMP_OS_NETDEV_GET_PRIV((PNET_DEV) pDev);
	if (pDevInfo != NULL)
		return pDevInfo->pPriv;
	return NULL;
}


/*
========================================================================
Routine Description:
	Get private flags from the network interface.

Arguments:
	pDev			- the device

Return Value:
	pPriv			- the pointer

Note:
========================================================================
*/
USHORT RtmpDevPrivFlagsGet(VOID *pDev)
{

	DEV_PRIV_INFO *pDevInfo = NULL;


	pDevInfo = (DEV_PRIV_INFO *) _RTMP_OS_NETDEV_GET_PRIV((PNET_DEV) pDev);
	if (pDevInfo != NULL)
		return pDevInfo->priv_flags;
	return 0;
}


/*
========================================================================
Routine Description:
	Get private flags from the network interface.

Arguments:
	pDev			- the device

Return Value:
	pPriv			- the pointer

Note:
========================================================================
*/
VOID RtmpDevPrivFlagsSet(VOID *pDev, USHORT PrivFlags)
{
	DEV_PRIV_INFO *pDevInfo = NULL;


	pDevInfo = (DEV_PRIV_INFO *) _RTMP_OS_NETDEV_GET_PRIV((PNET_DEV) pDev);
	if (pDevInfo != NULL)
		pDevInfo->priv_flags = PrivFlags;
}




#ifdef OS_ABL_FUNC_SUPPORT
/*
========================================================================
Routine Description:
	Change/Recover file UID/GID.

Arguments:
	pOSFSInfoOrg	- the file
	bSet			- Change (TRUE) or Recover (FALSE)

Return Value:
	None

Note:
	rt_linux.h, not rt_drv.h
========================================================================
*/
void RtmpOSFSInfoChange(RTMP_OS_FS_INFO *pOSFSInfoOrg, BOOLEAN bSet)
{
	OS_FS_INFO *pOSFSInfo;

	if (bSet == TRUE) {
		os_alloc_mem(NULL, (UCHAR **) & (pOSFSInfoOrg->pContent),
			     sizeof (OS_FS_INFO));
		if (pOSFSInfoOrg->pContent == NULL) {
			DBGPRINT(RT_DEBUG_ERROR, ("%s: alloc file info fail!\n", __FUNCTION__));
			return;
		} else
			memset(pOSFSInfoOrg->pContent, 0, sizeof (OS_FS_INFO));
	}

	pOSFSInfo = (OS_FS_INFO *) (pOSFSInfoOrg->pContent);
	if (pOSFSInfo == NULL) {
		DBGPRINT(RT_DEBUG_ERROR, ("%s: pOSFSInfo == NULL!\n", __FUNCTION__));
		return;
	}

	__RtmpOSFSInfoChange(pOSFSInfo, bSet);

	if (bSet == FALSE) {
		if (pOSFSInfoOrg->pContent != NULL) {
			os_free_mem(NULL, pOSFSInfoOrg->pContent);
			pOSFSInfoOrg->pContent = NULL;
		}
	}
}


/*
========================================================================
Routine Description:
	Activate a tasklet.

Arguments:
	pTasklet		- the tasklet

Return Value:
	TRUE or FALSE

Note:
========================================================================
*/
BOOLEAN RtmpOsTaskletSche(RTMP_NET_TASK_STRUCT *pTasklet)
{
	if (pTasklet->pContent == NULL)
		return FALSE;

#ifdef WORKQUEUE_BH
	schedule_work((struct work_struct *)(pTasklet->pContent));
#else
	tasklet_hi_schedule((OS_NET_TASK_STRUCT *) (pTasklet->pContent));
#endif /* WORKQUEUE_BH */

	return TRUE;
}


/*
========================================================================
Routine Description:
	Initialize a tasklet.

Arguments:
	pTasklet		- the tasklet

Return Value:
	TRUE or FALSE

Note:
========================================================================
*/
BOOLEAN RtmpOsTaskletInit(
	RTMP_NET_TASK_STRUCT *pTasklet,
	VOID (*pFunc) (unsigned long data),
	ULONG Data,
	LIST_HEADER *pTaskletList)
{
#ifdef WORKQUEUE_BH
	if (RTMP_OS_Alloc_RscOnly(pTasklet, sizeof (struct work_struct)) == FALSE)
		return FALSE;

	INIT_WORK((struct work_struct *)(pTasklet->pContent), pFunc);
#else

	if (RTMP_OS_Alloc_RscOnly(pTasklet, sizeof (OS_NET_TASK_STRUCT)) == FALSE)
		return FALSE;

	tasklet_init((OS_NET_TASK_STRUCT *) (pTasklet->pContent), pFunc, Data);
#endif /* WORKQUEUE_BH */

	return TRUE;
}


/*
========================================================================
Routine Description:
	Delete a tasklet.

Arguments:
	pTasklet		- the tasklet

Return Value:
	TRUE or FALSE

Note:
========================================================================
*/
BOOLEAN RtmpOsTaskletKill(RTMP_NET_TASK_STRUCT *pTasklet)
{
	if (pTasklet->pContent != NULL) {
#ifndef WORKQUEUE_BH
		tasklet_kill((OS_NET_TASK_STRUCT *) (pTasklet->pContent));
#endif /* WORKQUEUE_BH */

		os_free_mem(NULL, pTasklet->pContent);
		pTasklet->pContent = NULL;
	}

	return TRUE;
}


VOID RtmpOsTaskletDataAssign(RTMP_NET_TASK_STRUCT *pTasklet, ULONG Data)
{
#ifndef WORKQUEUE_BH
	if (pTasklet->pContent != NULL)
		((OS_NET_TASK_STRUCT *) (pTasklet->pContent))->data = Data;
#endif /* WORKQUEUE_BH */
}


INT32 RtmpOsTaskIsKilled(RTMP_OS_TASK *pTaskOrg)
{
	OS_TASK *pTask;

	pTask = (OS_TASK *) (pTaskOrg->pContent);
	if (pTask == NULL)
		return 1;
	return pTask->task_killed;
}


VOID RtmpOsTaskWakeUp(RTMP_OS_TASK *pTaskOrg)
{
	OS_TASK *pTask;

	pTask = (OS_TASK *) (pTaskOrg->pContent);
	if (pTask == NULL)
		return;

#ifdef KTHREAD_SUPPORT
	WAKE_UP(pTask);
#else
	RTMP_SEM_EVENT_UP(&pTask->taskSema);
#endif
}


/*
========================================================================
Routine Description:
	Check if the task is legal.

Arguments:
	pPkt			- the packet
	pDev			- the device

Return Value:
	None

Note:
========================================================================
*/
BOOLEAN RtmpOsCheckTaskLegality(RTMP_OS_TASK *pTaskOrg)
{
	OS_TASK *pTask;

	pTask = (OS_TASK *) (pTaskOrg->pContent);
	if (!pTask)
		return FALSE;

#ifdef KTHREAD_SUPPORT
	if (pTask->kthread_task == NULL)
#else
	CHECK_PID_LEGALITY(pTask->taskPID);
	else
#endif
	return FALSE;

	return TRUE;
}


/* timeout -- ms */
VOID RTMP_SetPeriodicTimer(NDIS_MINIPORT_TIMER *pTimerOrg, ULONG timeout)
{
	OS_NDIS_MINIPORT_TIMER *pTimer;

	pTimer = (OS_NDIS_MINIPORT_TIMER *) (pTimerOrg->pContent);
	if (pTimer)
		__RTMP_SetPeriodicTimer(pTimer, timeout);
}


/* convert NdisMInitializeTimer --> RTMP_OS_Init_Timer */
VOID RTMP_OS_Init_Timer(
	VOID *pReserved,
	NDIS_MINIPORT_TIMER *pTimerOrg,
	TIMER_FUNCTION function,
	PVOID data,
	LIST_HEADER *pTimerList)
{
	OS_NDIS_MINIPORT_TIMER *pTimer;

	if (RTMP_OS_Alloc_RscOnly(pTimerOrg, sizeof (OS_NDIS_MINIPORT_TIMER)) == FALSE)
		return;

	pTimer = (OS_NDIS_MINIPORT_TIMER *) (pTimerOrg->pContent);
	if (pTimer)
		__RTMP_OS_Init_Timer(pReserved, pTimer, function, data);
}


VOID RTMP_OS_Add_Timer(NDIS_MINIPORT_TIMER *pTimerOrg, ULONG timeout)
{
	OS_NDIS_MINIPORT_TIMER *pTimer;

	 pTimer = (OS_NDIS_MINIPORT_TIMER *) (pTimerOrg->pContent);

	if (pTimer) {
		if (timer_pending(pTimer))
			return;

		__RTMP_OS_Add_Timer(pTimer, timeout);
	}
} 


VOID RTMP_OS_Mod_Timer(NDIS_MINIPORT_TIMER *pTimerOrg, ULONG timeout)
{
	OS_NDIS_MINIPORT_TIMER *pTimer;

	pTimer = (OS_NDIS_MINIPORT_TIMER *) (pTimerOrg->pContent);
	if (pTimer)
		__RTMP_OS_Mod_Timer(pTimer, timeout);
}


VOID RTMP_OS_Del_Timer(NDIS_MINIPORT_TIMER *pTimerOrg, BOOLEAN *pCancelled)
{
	OS_NDIS_MINIPORT_TIMER *pTimer;

	pTimer = (OS_NDIS_MINIPORT_TIMER *) (pTimerOrg->pContent);
	if (pTimer)
		__RTMP_OS_Del_Timer(pTimer, pCancelled);
}


VOID RTMP_OS_Release_Timer(NDIS_MINIPORT_TIMER *pTimerOrg)
{
	OS_NDIS_MINIPORT_TIMER *pTimer;

	pTimer = (OS_NDIS_MINIPORT_TIMER *) (pTimerOrg->pContent);
	if (pTimer) {
		__RTMP_OS_Release_Timer(pTimer);

		os_free_mem(NULL, pTimer);
		pTimerOrg->pContent = NULL;
	}
}


/*
========================================================================
Routine Description:
	Allocate a OS resource.

Arguments:
	pAd				- WLAN control block pointer
	pRsc			- the resource
	RscLen			- resource length

Return Value:
	TRUE or FALSE

Note:
========================================================================
*/
BOOLEAN RTMP_OS_Alloc_Rsc(
	LIST_HEADER *pRscList,
	VOID *pRscSrc,
	UINT32 RscLen)
{
	OS_RSTRUC *pRsc = (OS_RSTRUC *) pRscSrc;

	if (pRsc->pContent == NULL) {
		/* new entry */
		os_alloc_mem(NULL, (UCHAR **) & (pRsc->pContent), RscLen);
		if (pRsc->pContent == NULL) {
			DBGPRINT(RT_DEBUG_ERROR,
				 ("%s: alloc timer fail!\n", __FUNCTION__));
			return FALSE;
		} else {
			LIST_RESOURCE_OBJ_ENTRY *pObj;

			/* allocate resource record entry */
			os_alloc_mem(NULL, (UCHAR **) & (pObj),
				     sizeof (LIST_RESOURCE_OBJ_ENTRY));
			if (pObj == NULL) {
				DBGPRINT(RT_DEBUG_ERROR,
					 ("%s: alloc timer obj fail!\n",
					  __FUNCTION__));
				os_free_mem(NULL, pRsc->pContent);
				pRsc->pContent = NULL;
				return FALSE;
			} else {
				memset(pRsc->pContent, 0, RscLen);
				pObj->pRscObj = (VOID *) pRsc;

				OS_SEM_LOCK(&UtilSemLock);
				insertTailList(pRscList, (LIST_ENTRY *) pObj);
				OS_SEM_UNLOCK(&UtilSemLock);
			}
		}
	}

	return TRUE;
}


/*
========================================================================
Routine Description:
	Allocate a OS resource.

Arguments:
	pAd				- WLAN control block pointer
	pRsc			- the resource
	RscLen			- resource length

Return Value:
	TRUE or FALSE

Note:
========================================================================
*/
BOOLEAN RTMP_OS_Alloc_RscOnly(VOID *pRscSrc, UINT32 RscLen)
{
	OS_RSTRUC *pRsc = (OS_RSTRUC *) pRscSrc;

	if (pRsc->pContent == NULL) {
		/* new entry */
		os_alloc_mem(NULL, (UCHAR **) & (pRsc->pContent), RscLen);
		if (pRsc->pContent == NULL) {
			DBGPRINT(RT_DEBUG_ERROR,
				 ("%s: alloc timer fail!\n", __FUNCTION__));
			return FALSE;
		}
		memset(pRsc->pContent, 0, RscLen);
	}

	return TRUE;
}


/*
========================================================================
Routine Description:
	Remove a OS resource.

Arguments:
	pAd				- WLAN control block pointer
	pRsc			- the resource

Return Value:
	TRUE or FALSE

Note:
========================================================================
*/
BOOLEAN RTMP_OS_Remove_Rsc(
	LIST_HEADER *pRscList,
	VOID *pRscSrc)
{
	LIST_RESOURCE_OBJ_ENTRY *pObj;
	OS_RSTRUC *pRscHead, *pRsc, *pRscRev = (OS_RSTRUC *) pRscSrc;
	pRscHead = NULL;

	OS_SEM_LOCK(&UtilSemLock);
	while(TRUE)
	{
		pObj = (LIST_RESOURCE_OBJ_ENTRY *) removeHeadList(pRscList);
		if (pRscHead == NULL)
			pRscHead = pObj->pRscObj; /* backup first entry */
		else if (((ULONG)pRscHead) == ((ULONG)(pObj->pRscObj)))
			break; /* has searched all entries */

		pRsc = (OS_RSTRUC *) (pObj->pRscObj);
		if ((ULONG)pRsc == (ULONG)pRscRev)
			break; /* find it */

		/* re-insert it */
		insertTailList(pRscList, (LIST_ENTRY *) pObj);
	}
	OS_SEM_UNLOCK(&UtilSemLock);

	return TRUE;
}


/*
========================================================================
Routine Description:
	Free all timers.

Arguments:
	pAd				- WLAN control block pointer

Return Value:
	None

Note:
========================================================================
*/
VOID RTMP_OS_Free_Rscs(LIST_HEADER *pRscList)
{
	LIST_RESOURCE_OBJ_ENTRY *pObj;
	OS_RSTRUC *pRsc;

	OS_SEM_LOCK(&UtilSemLock);
	while (TRUE) {
		pObj = (LIST_RESOURCE_OBJ_ENTRY *) removeHeadList(pRscList);
		if (pObj == NULL)
			break;
		pRsc = (OS_RSTRUC *) (pObj->pRscObj);

		if (pRsc->pContent != NULL) {
			/* free the timer memory */
			os_free_mem(NULL, pRsc->pContent);
			pRsc->pContent = NULL;
		} else {
			/*
			   The case is possible because some timers are released during
			   the driver life time, EX: we will release some timers in
			   MacTableDeleteEntry().
			   But we do not recommend the behavior, i.e. not to release
			   timers in the driver life time; Or we can not cancel the
			   timer for timer preemption problem.
			 */
		}

		os_free_mem(NULL, pObj);	/* free the timer record entry */
	}
	OS_SEM_UNLOCK(&UtilSemLock);
}


/*
========================================================================
Routine Description:
	Allocate a kernel task.

Arguments:
	pTask			- the task

Return Value:
	None

Note:
========================================================================
*/
BOOLEAN RtmpOSTaskAlloc(RTMP_OS_TASK *pTask, LIST_HEADER *pTaskList)
{
	if (RTMP_OS_Alloc_RscOnly(pTask, sizeof (OS_TASK)) == FALSE) {
		DBGPRINT(RT_DEBUG_ERROR,
			 ("%s: alloc task fail!\n", __FUNCTION__));
		return FALSE;	/* allocate fail */
	}

	return TRUE;
}


/*
========================================================================
Routine Description:
	Free a kernel task.

Arguments:
	pTask			- the task

Return Value:
	None

Note:
========================================================================
*/
VOID RtmpOSTaskFree(RTMP_OS_TASK *pTaskOrg)
{
	OS_TASK *pTask;

	pTask = (OS_TASK *) (pTaskOrg->pContent);
	if (pTask != NULL) {
		os_free_mem(NULL, pTask);
		pTaskOrg->pContent = NULL;
	}
}


/*
========================================================================
Routine Description:
	Kill a kernel task.

Arguments:
	pTaskOrg		- the task

Return Value:
	None

Note:
========================================================================
*/
NDIS_STATUS RtmpOSTaskKill(RTMP_OS_TASK *pTaskOrg)
{
	OS_TASK *pTask;
	NDIS_STATUS Status;

	pTask = (OS_TASK *) (pTaskOrg->pContent);
	if (pTask != NULL) {
		Status = __RtmpOSTaskKill(pTask);
		RtmpOSTaskFree(pTaskOrg);
		return Status;
	}

	return NDIS_STATUS_FAILURE;
}


/*
========================================================================
Routine Description:
	Notify kernel the task exit.

Arguments:
	pTaskOrg		- the task

Return Value:
	None

Note:
========================================================================
*/
INT RtmpOSTaskNotifyToExit(RTMP_OS_TASK *pTaskOrg)
{
	OS_TASK *pTask;

	pTask = (OS_TASK *) (pTaskOrg->pContent);
	if (pTask == NULL)
		return 0;
	return __RtmpOSTaskNotifyToExit(pTask);
}


/*
========================================================================
Routine Description:
	Customize the task.

Arguments:
	pTaskOrg		- the task

Return Value:
	None

Note:
========================================================================
*/
VOID RtmpOSTaskCustomize(RTMP_OS_TASK *pTaskOrg)
{
	OS_TASK *pTask;

	pTask = (OS_TASK *) (pTaskOrg->pContent);
	if (pTask)
		__RtmpOSTaskCustomize(pTask);
}


/*
========================================================================
Routine Description:
	Activate a kernel task.

Arguments:
	pTaskOrg		- the task
	fn				- task handler
	arg				- task input argument

Return Value:
	None

Note:
========================================================================
*/
NDIS_STATUS RtmpOSTaskAttach(
	RTMP_OS_TASK *pTaskOrg,
	RTMP_OS_TASK_CALLBACK fn,
	ULONG arg)
{
	OS_TASK *pTask;

	pTask = (OS_TASK *) (pTaskOrg->pContent);
	if (pTask == NULL)
		return NDIS_STATUS_FAILURE;
	return __RtmpOSTaskAttach(pTask, fn, arg);
}


/*
========================================================================
Routine Description:
	Initialize a kernel task.

Arguments:
	pTaskOrg		- the task
	pTaskName		- task name
	pPriv			- task input argument

Return Value:
	None

Note:
========================================================================
*/
NDIS_STATUS RtmpOSTaskInit(
	RTMP_OS_TASK *pTaskOrg,
	PSTRING pTaskName,
	VOID *pPriv,
	LIST_HEADER *pTaskList,
	LIST_HEADER *pSemList)
{
	OS_TASK *pTask;

	if (RtmpOSTaskAlloc(pTaskOrg, pTaskList) == FALSE)
		return NDIS_STATUS_FAILURE;

	pTask = (OS_TASK *) (pTaskOrg->pContent);
	if (pTask == NULL)
		return NDIS_STATUS_FAILURE;

	return __RtmpOSTaskInit(pTask, pTaskName, pPriv, pSemList);
}


/*
========================================================================
Routine Description:
	Wait for a event in the task.

Arguments:
	pAd				- WLAN control block pointer
	pTaskOrg		- the task

Return Value:
	TRUE
	FALSE

Note:
========================================================================
*/
BOOLEAN RtmpOSTaskWait(VOID *pReserved, RTMP_OS_TASK *pTaskOrg, INT32 *pStatus)
{
	OS_TASK *pTask;

	pTask = (OS_TASK *) (pTaskOrg->pContent);
	if (pTask == NULL)
		return FALSE;

	return __RtmpOSTaskWait(pReserved, pTask, pStatus);
}


/*
========================================================================
Routine Description:
	Get private data for the task.

Arguments:
	pTaskOrg		- the task

Return Value:
	None

Note:
========================================================================
*/
VOID *RtmpOsTaskDataGet(RTMP_OS_TASK *pTaskOrg)
{
	if (pTaskOrg->pContent == NULL)
		return NULL;

	return (((OS_TASK *) (pTaskOrg->pContent))->priv);
}


/*
========================================================================
Routine Description:
	Allocate a lock.

Arguments:
	pLock			- the lock

Return Value:
	None

Note:
========================================================================
*/
BOOLEAN RtmpOsAllocateLock(NDIS_SPIN_LOCK *pLock, LIST_HEADER *pLockList)
{
	if (RTMP_OS_Alloc_RscOnly(pLock, sizeof (OS_NDIS_SPIN_LOCK)) == FALSE) {
		DBGPRINT(RT_DEBUG_ERROR,
			 ("%s: alloc lock fail!\n", __FUNCTION__));
		return FALSE;	/* allocate fail */
	}

	OS_NdisAllocateSpinLock(pLock->pContent);
	return TRUE;
}


/*
========================================================================
Routine Description:
	Free a lock.

Arguments:
	pLockOrg		- the lock

Return Value:
	None

Note:
========================================================================
*/
VOID RtmpOsFreeSpinLock(NDIS_SPIN_LOCK *pLockOrg)
{
	/* we will free all locks memory in RTMP_OS_FREE_LOCK() */
	OS_NDIS_SPIN_LOCK *pLock;

	pLock = (OS_NDIS_MINIPORT_TIMER *) (pLockOrg->pContent);
	if (pLock != NULL) {
		OS_NdisFreeSpinLock(pLock);

		os_free_mem(NULL, pLock);
		pLockOrg->pContent = NULL;
	}
}


/*
========================================================================
Routine Description:
	Spin lock bh.

Arguments:
	pLockOrg		- the lock

Return Value:
	None

Note:
========================================================================
*/
VOID RtmpOsSpinLockBh(NDIS_SPIN_LOCK *pLockOrg)
{
	OS_NDIS_SPIN_LOCK *pLock;

	pLock = (OS_NDIS_SPIN_LOCK *) (pLockOrg->pContent);
	if (pLock != NULL) {
		OS_SEM_LOCK(pLock);
	} else
		printk("lock> warning! the lock was freed!\n");
}


/*
========================================================================
Routine Description:
	Spin unlock bh.

Arguments:
	pLockOrg		- the lock

Return Value:
	None

Note:
========================================================================
*/
VOID RtmpOsSpinUnLockBh(NDIS_SPIN_LOCK *pLockOrg)
{
	OS_NDIS_SPIN_LOCK *pLock;

	pLock = (OS_NDIS_SPIN_LOCK *) (pLockOrg->pContent);
	if (pLock != NULL) {
		OS_SEM_UNLOCK(pLock);
	} else
		printk("lock> warning! the lock was freed!\n");
}


/*
========================================================================
Routine Description:
	Interrupt lock.

Arguments:
	pLockOrg		- the lock
	pIrqFlags		- the lock flags

Return Value:
	None

Note:
========================================================================
*/
VOID RtmpOsIntLock(NDIS_SPIN_LOCK *pLockOrg, ULONG *pIrqFlags)
{
	OS_NDIS_SPIN_LOCK *pLock;

	pLock = (OS_NDIS_SPIN_LOCK *) (pLockOrg->pContent);
	if (pLock != NULL) {
		OS_INT_LOCK(pLock, *pIrqFlags);
	} else
		printk("lock> warning! the lock was freed!\n");
}


/*
========================================================================
Routine Description:
	Interrupt unlock.

Arguments:
	pLockOrg		- the lock
	IrqFlags		- the lock flags

Return Value:
	None

Note:
========================================================================
*/
VOID RtmpOsIntUnLock(NDIS_SPIN_LOCK *pLockOrg, ULONG IrqFlags)
{
	OS_NDIS_SPIN_LOCK *pLock;

	pLock = (OS_NDIS_SPIN_LOCK *) (pLockOrg->pContent);
	if (pLock != NULL) {
		OS_INT_UNLOCK(pLock, IrqFlags);
	} else
		printk("lock> warning! the lock was freed!\n");
}


/*
========================================================================
Routine Description:
	Get MAC address for the network interface.

Arguments:
	pDev			- the device

Return Value:
	None

Note:
========================================================================
*/
unsigned char *RtmpOsNetDevGetPhyAddr(VOID *pDev)
{
	return RTMP_OS_NETDEV_GET_PHYADDR((PNET_DEV) pDev);
}


/*
========================================================================
Routine Description:
	Start network interface TX queue.

Arguments:
	pDev			- the device

Return Value:
	None

Note:
========================================================================
*/
VOID RtmpOsNetQueueStart(PNET_DEV pDev)
{
	RTMP_OS_NETDEV_START_QUEUE(pDev);
}


/*
========================================================================
Routine Description:
	Stop network interface TX queue.

Arguments:
	pDev			- the device

Return Value:
	None

Note:
========================================================================
*/
VOID RtmpOsNetQueueStop(PNET_DEV pDev)
{
	RTMP_OS_NETDEV_STOP_QUEUE(pDev);
}


/*
========================================================================
Routine Description:
	Wake up network interface TX queue.

Arguments:
	pDev			- the device

Return Value:
	None

Note:
========================================================================
*/
VOID RtmpOsNetQueueWake(PNET_DEV pDev)
{
	RTMP_OS_NETDEV_WAKE_QUEUE(pDev);
}


/*
========================================================================
Routine Description:
	Assign network interface to the packet.

Arguments:
	pPkt			- the packet
	pDev			- the device

Return Value:
	None

Note:
========================================================================
*/
VOID RtmpOsSetPktNetDev(VOID *pPkt, VOID *pDev)
{
	SET_OS_PKT_NETDEV(pPkt, (PNET_DEV) pDev);
}


/*
========================================================================
Routine Description:
	Assign network interface type.

Arguments:
	pDev			- the device
	Type			- the type

Return Value:
	None

Note:
========================================================================
*/
VOID RtmpOsSetNetDevType(VOID *pDev, USHORT Type)
{
	RTMP_OS_NETDEV_SET_TYPE((PNET_DEV) pDev, Type);
}


/*
========================================================================
Routine Description:
	Assign network interface type for monitor mode.

Arguments:
	pDev			- the device
	Type			- the type

Return Value:
	None

Note:
========================================================================
*/
VOID RtmpOsSetNetDevTypeMonitor(VOID *pDev)
{
	RTMP_OS_NETDEV_SET_TYPE((PNET_DEV) pDev, ARPHRD_IEEE80211_PRISM);
}


/*
========================================================================
Routine Description:
	Get PID.

Arguments:
	pPkt			- the packet
	pDev			- the device

Return Value:
	None

Note:
========================================================================
*/
VOID RtmpOsGetPid(IN ULONG *pDst,
		  IN ULONG PID)
{
	RT_GET_OS_PID(*pDst, PID);
}


/*
========================================================================
Routine Description:
	Wait for a moment.

Arguments:
	Time			- micro second

Return Value:
	None

Note:
========================================================================
*/
VOID RtmpOsWait(UINT32 Time)
{
	OS_WAIT(Time);
}


/*
========================================================================
Routine Description:
	Check if b is smaller than a.

Arguments:
	Time			- micro second

Return Value:
	None

Note:
========================================================================
*/
UINT32 RtmpOsTimerAfter(ULONG a, ULONG b)
{
	return RTMP_TIME_AFTER(a, b);
}


/*
========================================================================
Routine Description:
	Check if b is not smaller than a.

Arguments:
	Time			- micro second

Return Value:
	None

Note:
========================================================================
*/
UINT32 RtmpOsTimerBefore(ULONG a, ULONG b)
{
	return RTMP_TIME_BEFORE(a, b);
}


/*
========================================================================
Routine Description:
	Get current system time.

Arguments:
	pTime			- system time (tick)

Return Value:
	None

Note:
========================================================================
*/
VOID RtmpOsGetSystemUpTime(ULONG *pTime)
{
	NdisGetSystemUpTime(pTime);
}

/*
========================================================================
Routine Description:
	Get OS tick unit.

Arguments:
	pOps			- Utility table

Return Value:
	None

Note:
========================================================================
*/
UINT32 RtmpOsTickUnitGet(VOID)
{
	return HZ;
}


/*
========================================================================
Routine Description:
	ntohs

Arguments:
	Value			- the value

Return Value:
	the value

Note:
========================================================================
*/
UINT16 RtmpOsNtohs(UINT16 Value)
{
	return OS_NTOHS(Value);
}


/*
========================================================================
Routine Description:
	htons

Arguments:
	Value			- the value

Return Value:
	the value

Note:
========================================================================
*/
UINT16 RtmpOsHtons(UINT16 Value)
{
	return OS_HTONS(Value);
}


/*
========================================================================
Routine Description:
	ntohl

Arguments:
	Value			- the value

Return Value:
	the value

Note:
========================================================================
*/
UINT32 RtmpOsNtohl(UINT32 Value)
{
	return OS_NTOHL(Value);
}

/*
========================================================================
Routine Description:
	htonl

Arguments:
	Value			- the value

Return Value:
	the value

Note:
========================================================================
*/
UINT32 RtmpOsHtonl(UINT32 Value)
{
	return OS_HTONL(Value);
}


/*
========================================================================
Routine Description:
	get_unaligned for 16-bit value.

Arguments:
	pWord			- the value

Return Value:
	the value

Note:
========================================================================
*/
UINT16 RtmpOsGetUnaligned(UINT16 *pWord)
{
	return get_unaligned(pWord);
}

/*
========================================================================
Routine Description:
	get_unaligned for 32-bit value.

Arguments:
	pWord			- the value

Return Value:
	the value

Note:
========================================================================
*/
UINT32 RtmpOsGetUnaligned32(UINT32 *pWord)
{
	return get_unaligned(pWord);
}

/*
========================================================================
Routine Description:
	get_unaligned for long-bit value.

Arguments:
	pWord			- the value

Return Value:
	the value

Note:
========================================================================
*/
ULONG RtmpOsGetUnalignedlong(ULONG *pWord)
{
	return get_unaligned(pWord);
}


/*
========================================================================
Routine Description:
	Get maximum scan data length.

Arguments:
	None

Return Value:
	length

Note:
	Used in site servey.
========================================================================
*/
ULONG RtmpOsMaxScanDataGet(VOID)
{
	return IW_SCAN_MAX_DATA;
}


/*
========================================================================
Routine Description:
	copy_from_user

Arguments:
	to				-
	from			-
	n				- size

Return Value:
	copy size

Note:
========================================================================
*/
ULONG RtmpOsCopyFromUser(VOID *to, const void *from, ULONG n)
{
	return (copy_from_user(to, from, n));
}


/*
========================================================================
Routine Description:
	copy_to_user

Arguments:
	to				-
	from			-
	n				- size

Return Value:
	copy size

Note:
========================================================================
*/
ULONG RtmpOsCopyToUser(VOID *to, const void *from, ULONG n)
{
	return (copy_to_user(to, from, n));
}


/*
========================================================================
Routine Description:
	Initialize a semaphore.

Arguments:
	pSem			- the semaphore

Return Value:
	TRUE			- Successfully
	FALSE			- Fail

Note:
========================================================================
*/
BOOLEAN RtmpOsSemaInitLocked(RTMP_OS_SEM *pSem, LIST_HEADER *pSemList)
{
	if (RTMP_OS_Alloc_RscOnly(pSem, sizeof (OS_SEM)) == FALSE) {
		DBGPRINT(RT_DEBUG_ERROR, ("%s: alloc semaphore fail!\n", __FUNCTION__));
		return FALSE;
	}

	OS_SEM_EVENT_INIT_LOCKED((OS_SEM *) (pSem->pContent));
	return TRUE;
}



/*
========================================================================
Routine Description:
	Initialize a semaphore.

Arguments:
	pSemOrg			- the semaphore

Return Value:
	TRUE			- Successfully
	FALSE			- Fail

Note:
========================================================================
*/
BOOLEAN RtmpOsSemaInit(RTMP_OS_SEM *pSem, LIST_HEADER *pSemList)
{
	if (RTMP_OS_Alloc_RscOnly(pSem, sizeof (OS_SEM)) == FALSE) {
		DBGPRINT(RT_DEBUG_ERROR,
			 ("%s: alloc semaphore fail!\n", __FUNCTION__));
		return FALSE;
	}

	OS_SEM_EVENT_INIT((OS_SEM *) (pSem->pContent));
	return TRUE;
}


/*
========================================================================
Routine Description:
	Destroy a semaphore.

Arguments:
	pSemOrg			- the semaphore

Return Value:
	TRUE			- Successfully
	FALSE			- Fail

Note:
========================================================================
*/
BOOLEAN RtmpOsSemaDestory(RTMP_OS_SEM *pSemOrg)
{
	OS_SEM *pSem;

	pSem = (OS_SEM *) (pSemOrg->pContent);
	if (pSem != NULL) {
		OS_SEM_EVENT_DESTORY(pSem);

		os_free_mem(NULL, pSem);
		pSemOrg->pContent = NULL;
	} else
		printk("sem> warning! double-free sem!\n");
	return TRUE;
}


/*
========================================================================
Routine Description:
	Wait a semaphore.

Arguments:
	pSemOrg			- the semaphore

Return Value:
	0				- Successfully
	Otherwise		- Fail

Note:
========================================================================
*/
INT32 RtmpOsSemaWaitInterruptible(RTMP_OS_SEM *pSemOrg)
{
	OS_SEM *pSem;
	INT Status = -1;

	pSem = (OS_SEM *) (pSemOrg->pContent);
	if (pSem != NULL)
		OS_SEM_EVENT_WAIT(pSem, Status);
	return Status;
}


/*
========================================================================
Routine Description:
	Wake up a semaphore.

Arguments:
	pSemOrg			- the semaphore

Return Value:
	None

Note:
========================================================================
*/
VOID RtmpOsSemaWakeUp(RTMP_OS_SEM *pSemOrg)
{
	OS_SEM *pSem;

	pSem = (OS_SEM *) (pSemOrg->pContent);
	if (pSem != NULL)
		OS_SEM_EVENT_UP(pSem);
}


/*
========================================================================
Routine Description:
	Check if we are in a interrupt.

Arguments:
	None

Return Value:
	0				- No
	Otherwise		- Yes

Note:
========================================================================
*/
INT32 RtmpOsIsInInterrupt(VOID)
{
	return (in_interrupt());
}


/*
========================================================================
Routine Description:
	Copy the data buffer to the packet frame body.

Arguments:
	pAd				- WLAN control block pointer
	pNetPkt			- the packet
	ThisFrameLen	- copy length
	pData			- the data buffer

Return Value:
	None

Note:
========================================================================
*/
VOID RtmpOsPktBodyCopy(
	PNET_DEV pNetDev,
	PNDIS_PACKET pNetPkt,
	ULONG ThisFrameLen,
	PUCHAR pData)
{
	memcpy(skb_put(pNetPkt, ThisFrameLen), pData, ThisFrameLen);
	SET_OS_PKT_NETDEV(pNetPkt, pNetDev);
	RTMP_SET_PACKET_SOURCE(OSPKT_TO_RTPKT(pNetPkt), PKTSRC_NDIS);
}


/*
========================================================================
Routine Description:
	Check if the packet is cloned.

Arguments:
	pNetPkt			- the packet

Return Value:
	TRUE			- Yes
	Otherwise		- No

Note:
========================================================================
*/
INT RtmpOsIsPktCloned(PNDIS_PACKET pNetPkt)
{
	return OS_PKT_CLONED(pNetPkt);
}


/*
========================================================================
Routine Description:
	Duplicate a packet.

Arguments:
	pNetPkt			- the packet

Return Value:
	the new packet

Note:
========================================================================
*/
PNDIS_PACKET RtmpOsPktCopy(PNDIS_PACKET pNetPkt)
{
	return skb_copy(RTPKT_TO_OSPKT(pNetPkt), GFP_ATOMIC);
}


/*
========================================================================
Routine Description:
	Clone a packet.

Arguments:
	pNetPkt			- the packet

Return Value:
	the cloned packet

Note:
========================================================================
*/
PNDIS_PACKET RtmpOsPktClone(PNDIS_PACKET pNetPkt)
{
	return skb_clone(RTPKT_TO_OSPKT(pNetPkt), MEM_ALLOC_FLAG);
}


/*
========================================================================
Routine Description:
	Assign the data pointer for the packet.

Arguments:
	pNetPkt			- the packet
	*pData			- the data buffer

Return Value:
	None

Note:
========================================================================
*/
VOID RtmpOsPktDataPtrAssign(PNDIS_PACKET pNetPkt, UCHAR *pData)
{
	SET_OS_PKT_DATAPTR(pNetPkt, pData);
}


/*
========================================================================
Routine Description:
	Assign the data length for the packet.

Arguments:
	pNetPkt			- the packet
	Len				- the data length

Return Value:
	None

Note:
========================================================================
*/
VOID RtmpOsPktLenAssign(PNDIS_PACKET pNetPkt, LONG Len)
{
	SET_OS_PKT_LEN(pNetPkt, Len);
}


/*
========================================================================
Routine Description:
	Adjust the tail pointer for the packet.

Arguments:
	pNetPkt			- the packet
	removedTagLen	- the size for adjustment

Return Value:
	None

Note:
========================================================================
*/
VOID RtmpOsPktTailAdjust(PNDIS_PACKET pNetPkt, UINT removedTagLen)
{
	OS_PKT_TAIL_ADJUST(pNetPkt, removedTagLen);
}


/*
========================================================================
Routine Description:
	Adjust the data pointer for the packet.

Arguments:
	pNetPkt			- the packet
	Len				- the size for adjustment

Return Value:
	the new data pointer for the packet

Note:
========================================================================
*/
PUCHAR RtmpOsPktTailBufExtend(PNDIS_PACKET pNetPkt, UINT Len)
{
	return OS_PKT_TAIL_BUF_EXTEND(pNetPkt, Len);
}


/*
========================================================================
Routine Description:
	adjust headroom for the packet.

Arguments:
	pNetPkt			- the packet
	Len				- the size for adjustment

Return Value:
	the new data pointer for the packet

Note:
========================================================================
*/
VOID RtmpOsPktReserve(PNDIS_PACKET pNetPkt, UINT Len)
{
	OS_PKT_RESERVE(pNetPkt, Len);
}


/*
========================================================================
Routine Description:
	Adjust the data pointer for the packet.

Arguments:
	pNetPkt			- the packet
	Len				- the size for adjustment

Return Value:
	the new data pointer for the packet

Note:
========================================================================
*/
PUCHAR RtmpOsPktHeadBufExtend(PNDIS_PACKET pNetPkt, UINT Len)
{
	return OS_PKT_HEAD_BUF_EXTEND(pNetPkt, Len);
}


/*
========================================================================
Routine Description:
	

Arguments:
	pPkt			- the packet

Return Value:
	None

Note:
========================================================================
*/
VOID RtmpOsPktInfPpaSend(PNDIS_PACKET pNetPkt)
{
#ifdef INF_PPA_SUPPORT
	struct sk_buff *pRxPkt = RTPKT_TO_OSPKT(pNetPkt);
	int ret = 0;
	unsigned int ppa_flags = 0;	/* reserved for now */

	pRxPkt->protocol = eth_type_trans(pRxPkt, pRxPkt->dev);

	memset(pRxPkt->head, 0, pRxPkt->data - pRxPkt->head - 14);
	DBGPRINT(RT_DEBUG_TRACE,
		 ("ppa_hook_directpath_send_fn rx :ret:%d headroom:%d dev:%s pktlen:%d<===\n",
		  ret, skb_headroom(pRxPkt) , pRxPkt->dev->name, pRxPkt->len));
	hex_dump("rx packet", pRxPkt->data, 32);
	ret = ppa_hook_directpath_send_fn(pAd->g_if_id, pRxPkt, pRxPkt->len, ppa_flags);
#endif /* INF_PPA_SUPPORT */
}


INT32 RtmpThreadPidKill(RTMP_OS_PID PID)
{
	return KILL_THREAD_PID(PID, SIGTERM, 1);
}


long RtmpOsSimpleStrtol(const char *cp, char **endp, unsigned int base)
{
	return simple_strtol(cp, endp, base);
}


BOOLEAN RtmpOsPktOffsetInit(VOID)
{
	struct sk_buff *pPkt = NULL;

	if ((RTPktOffsetData == 0) && (RTPktOffsetLen == 0)
	    && (RTPktOffsetCB == 0)) {
		pPkt = kmalloc(sizeof (struct sk_buff), GFP_ATOMIC);
		if (pPkt == NULL)
			return FALSE;

		RTPktOffsetData = (ULONG) (&(pPkt->data)) - (ULONG) pPkt;
		RTPktOffsetLen = (ULONG) (&(pPkt->len)) - (ULONG) pPkt;
		RTPktOffsetCB = (ULONG) (pPkt->cb) - (ULONG) pPkt;
		kfree(pPkt);

		DBGPRINT(RT_DEBUG_TRACE,
			 ("packet> data offset = %lu\n", RTPktOffsetData));
		DBGPRINT(RT_DEBUG_TRACE,
			 ("packet> len offset = %lu\n", RTPktOffsetLen));
		DBGPRINT(RT_DEBUG_TRACE,
			 ("packet> cb offset = %lu\n", RTPktOffsetCB));
	}

	return TRUE;
}


/*
========================================================================
Routine Description:
	Initialize the OS atomic_t.

Arguments:
	pAtomic			- the atomic

Return Value:
	TRUE			- allocation successfully
	FALSE			- allocation fail

Note:
========================================================================
*/
BOOLEAN RtmpOsAtomicInit(RTMP_OS_ATOMIC *pAtomic, LIST_HEADER *pAtomicList)
{
	if (RTMP_OS_Alloc_RscOnly(pAtomic, sizeof (atomic_t)) == FALSE) {
		DBGPRINT(RT_DEBUG_ERROR, ("%s: alloc atomic fail!\n", __FUNCTION__));
		return FALSE;	/* allocate fail */
	}

	return TRUE;
}

/*
========================================================================
Routine Description:
	Initialize the OS atomic_t.

Arguments:
	pAtomic			- the atomic

Return Value:
	TRUE			- allocation successfully
	FALSE			- allocation fail

Note:
========================================================================
*/
VOID RtmpOsAtomicDestroy(RTMP_OS_ATOMIC *pAtomic)
{
	if (pAtomic->pContent) {
		os_free_mem(NULL, pAtomic->pContent);
		pAtomic->pContent = NULL;
	}
}

/*
========================================================================
Routine Description:
	Atomic read a variable.

Arguments:
	pAtomic			- the atomic

Return Value:
	content

Note:
========================================================================
*/
LONG RtmpOsAtomicRead(RTMP_OS_ATOMIC *pAtomicSrc)
{
	if (pAtomicSrc->pContent)
		return atomic_read((atomic_t *) (pAtomicSrc->pContent));
	else
		return 0;
}


/*
========================================================================
Routine Description:
	Atomic dec a variable.

Arguments:
	pAtomic			- the atomic

Return Value:
	content

Note:
========================================================================
*/
VOID RtmpOsAtomicDec(RTMP_OS_ATOMIC *pAtomicSrc)
{
	if (pAtomicSrc->pContent)
		atomic_dec((atomic_t *) (pAtomicSrc->pContent));
}


/*
========================================================================
Routine Description:
	Sets a 32-bit variable to the specified value as an atomic operation.

Arguments:
	pAtomic			- the atomic
	Value			- the value to be exchanged

Return Value:
	the initial value of the pAtomicSrc parameter

Note:
========================================================================
*/
VOID RtmpOsAtomicInterlockedExchange(
	RTMP_OS_ATOMIC *pAtomicSrc,
	LONG Value)
{
	if (pAtomicSrc->pContent)
		InterlockedExchange((atomic_t *) (pAtomicSrc->pContent), Value);
}


/*
========================================================================
Routine Description:
	Initialize the OS utilities.

Arguments:
	pOps			- Utility table

Return Value:
	None

Note:
========================================================================
*/
VOID RtmpOsOpsInit(RTMP_OS_ABL_OPS *pOps)
{
	pOps->ra_printk = (RTMP_PRINTK)printk;
	pOps->ra_snprintf = (RTMP_SNPRINTF)snprintf;
}

#else /* OS_ABL_FUNC_SUPPORT */


void RtmpOSFSInfoChange(RTMP_OS_FS_INFO *pOSFSInfoOrg, BOOLEAN bSet)
{
	__RtmpOSFSInfoChange(pOSFSInfoOrg, bSet);
}


/* timeout -- ms */
VOID RTMP_SetPeriodicTimer(NDIS_MINIPORT_TIMER *pTimerOrg, unsigned long timeout)
{
	__RTMP_SetPeriodicTimer(pTimerOrg, timeout);
}


/* convert NdisMInitializeTimer --> RTMP_OS_Init_Timer */
VOID RTMP_OS_Init_Timer(
	VOID *pReserved,
	NDIS_MINIPORT_TIMER *pTimerOrg,
	TIMER_FUNCTION function,
	PVOID data,
	LIST_HEADER *pTimerList)
{
	__RTMP_OS_Init_Timer(pReserved, pTimerOrg, function, data);
}


VOID RTMP_OS_Add_Timer(NDIS_MINIPORT_TIMER *pTimerOrg, unsigned long timeout)
{
	__RTMP_OS_Add_Timer(pTimerOrg, timeout);
}


VOID RTMP_OS_Mod_Timer(NDIS_MINIPORT_TIMER *pTimerOrg, unsigned long timeout)
{
	__RTMP_OS_Mod_Timer(pTimerOrg, timeout);
}


VOID RTMP_OS_Del_Timer(NDIS_MINIPORT_TIMER *pTimerOrg, BOOLEAN *pCancelled)
{
	__RTMP_OS_Del_Timer(pTimerOrg, pCancelled);
}


VOID RTMP_OS_Release_Timer(NDIS_MINIPORT_TIMER *pTimerOrg)
{
	__RTMP_OS_Release_Timer(pTimerOrg);
}


NDIS_STATUS RtmpOSTaskKill(RTMP_OS_TASK *pTask)
{
	return __RtmpOSTaskKill(pTask);
}


INT RtmpOSTaskNotifyToExit(RTMP_OS_TASK *pTask)
{
	return __RtmpOSTaskNotifyToExit(pTask);
}


void RtmpOSTaskCustomize(RTMP_OS_TASK *pTask)
{
	__RtmpOSTaskCustomize(pTask);
}


NDIS_STATUS RtmpOSTaskAttach(
	RTMP_OS_TASK *pTask,
	RTMP_OS_TASK_CALLBACK fn,
	ULONG arg)
{
	return __RtmpOSTaskAttach(pTask, fn, arg);
}


NDIS_STATUS RtmpOSTaskInit(
	RTMP_OS_TASK *pTask,
	PSTRING pTaskName,
	VOID *pPriv,
	LIST_HEADER *pTaskList,
	LIST_HEADER *pSemList)
{
	return __RtmpOSTaskInit(pTask, pTaskName, pPriv, pSemList);
}


BOOLEAN RtmpOSTaskWait(VOID *pReserved, RTMP_OS_TASK * pTask, INT32 *pStatus)
{
	return __RtmpOSTaskWait(pReserved, pTask, pStatus);
}


VOID RtmpOsTaskWakeUp(RTMP_OS_TASK *pTask)
{
#ifdef KTHREAD_SUPPORT
	WAKE_UP(pTask);
#else
	RTMP_SEM_EVENT_UP(&pTask->taskSema);
#endif
}

#endif /* OS_ABL_FUNC_SUPPORT */

