text: /*
   * Linux OS Independent Layer
   *
   * Copyright (C) 1999-2016, Broadcom Corporation
   * 
   *      Unless you and Broadcom execute a separate written software license
   * agreement governing use of this software, this software is licensed to you
   * under the terms of the GNU General Public License version 2 (the "GPL"),
   * available at http://www.broadcom.com/licenses/GPLv2.php, with the
   * following added to such license:
   * 
   *      As a special exception, the copyright holders of this software give you
   * permission to link this software with independent modules, and to copy and
   * distribute the resulting executable under terms of your choice, provided that
   * you also meet, for each linked independent module, the terms and conditions of
   * the license of that module.  An independent module is a module which is not
   * derived from this software.  The special exception does not apply to any
   * modifications of the software.
   * 
   *      Notwithstanding the above, under no circumstances may you combine this
   * software in any way with any other Broadcom software provided under a license
   * other than the GPL, without Broadcom's express prior written consent.
   *
   *
   * <<Broadcom-WL-IPTag/Open:>>
   *
   * $Id: linux_osl.c 633414 2016-04-22 11:22:49Z $
   */
  
  #define LINUX_PORT
  
  #include <typedefs.h>
  #include <bcmendian.h>
  #include <linuxver.h>
  #include <bcmdefs.h>
  
  #if defined(USE_KMALLOC_FOR_FLOW_RING) && defined(__ARM_ARCH_7A__)
  #include <asm/cacheflush.h>
  #endif
  
  #include <linux/random.h>
  
  #include <osl.h>
  #include <bcmutils.h>
  #include <linux/delay.h>
  #include <pcicfg.h>
  
  
  
  #ifdef CONFIG_ARM64
  #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0))
  #include <linux/pci-dma-compat.h>
  #else
  #include <asm-generic/pci-dma-compat.h>
  #endif
  #endif
  #include <linux/fs.h>
  
  
  #ifdef BCMPCIE
  #include <bcmpcie.h>
  #endif /* BCMPCIE */
  #ifdef BCM_OBJECT_TRACE
  #include <bcmutils.h>
  #endif /* BCM_OBJECT_TRACE */
  
  #ifdef HW_WIFI_DMD_LOG
  #include <hw_wifi.h>
  #endif
  
  #define PCI_CFG_RETRY		10
  
  #define OS_HANDLE_MAGIC		0x1234abcd	/* Magic # to recognize osh */
  #define BCM_MEM_FILENAME_LEN	24		/* Mem. filename length */
  #define DUMPBUFSZ 1024
  
  /* dependancy check */
  #if !defined(BCMPCIE) && defined(DHD_USE_STATIC_CTRLBUF)
  #error "DHD_USE_STATIC_CTRLBUF suppored PCIE target only"
  #endif /* !BCMPCIE && DHD_USE_STATIC_CTRLBUF */
  
  #ifdef CONFIG_DHD_USE_STATIC_BUF
  #ifdef DHD_USE_STATIC_CTRLBUF
  #define DHD_SKB_1PAGE_BUFSIZE	(PAGE_SIZE*1)
  #define DHD_SKB_2PAGE_BUFSIZE	(PAGE_SIZE*2)
  #define DHD_SKB_4PAGE_BUFSIZE	(PAGE_SIZE*4)
  
  #define PREALLOC_FREE_MAGIC	0xFEDC
  #define PREALLOC_USED_MAGIC	0xFCDE
  #else
  #define DHD_SKB_HDRSIZE		336
  #define DHD_SKB_1PAGE_BUFSIZE	((PAGE_SIZE*1)-DHD_SKB_HDRSIZE)
  #define DHD_SKB_2PAGE_BUFSIZE	((PAGE_SIZE*2)-DHD_SKB_HDRSIZE)
  #define DHD_SKB_4PAGE_BUFSIZE	((PAGE_SIZE*4)-DHD_SKB_HDRSIZE)
  #endif /* DHD_USE_STATIC_CTRLBUF */
  
  #define STATIC_BUF_MAX_NUM	16
  #ifdef HW_SOFTAP_THROUGHPUT_OPTIMIZE
  #define STATIC_BUF_SIZE	(PAGE_SIZE*4)
  #else
  #define STATIC_BUF_SIZE	(PAGE_SIZE*2)
  #endif
  #define STATIC_BUF_TOTAL_LEN	(STATIC_BUF_MAX_NUM * STATIC_BUF_SIZE)
  
  typedef struct bcm_static_buf {
  #ifndef BCM_PCIE_UPDATE
  	spinlock_t static_lock;
  #else
  	struct semaphore static_sem;
  #endif
  	unsigned char *buf_ptr;
  	unsigned char buf_use[STATIC_BUF_MAX_NUM];
  } bcm_static_buf_t;
  
  static bcm_static_buf_t *bcm_static_buf = 0;
  
  #ifdef DHD_USE_STATIC_CTRLBUF
  #define STATIC_PKT_4PAGE_NUM	0
  #define DHD_SKB_MAX_BUFSIZE	DHD_SKB_2PAGE_BUFSIZE
  #elif defined(ENHANCED_STATIC_BUF)
  #define STATIC_PKT_4PAGE_NUM	1
  #define DHD_SKB_MAX_BUFSIZE	DHD_SKB_4PAGE_BUFSIZE
  #else
  #define STATIC_PKT_4PAGE_NUM	0
  #define DHD_SKB_MAX_BUFSIZE	DHD_SKB_2PAGE_BUFSIZE
  #endif /* DHD_USE_STATIC_CTRLBUF */
  
  #ifdef DHD_USE_STATIC_CTRLBUF
  #define STATIC_PKT_1PAGE_NUM	0
  #define STATIC_PKT_2PAGE_NUM	64
  #else
  #define STATIC_PKT_1PAGE_NUM	8
  #define STATIC_PKT_2PAGE_NUM	8
  #endif /* DHD_USE_STATIC_CTRLBUF */
  
  #ifdef CONFIG_HW_WIFI_DMA_ADDR_CTRL
  #define DMA_ERROR_COUNT	  5
  #endif
  
  #define STATIC_PKT_1_2PAGE_NUM	\
  	((STATIC_PKT_1PAGE_NUM) + (STATIC_PKT_2PAGE_NUM))
  #define STATIC_PKT_MAX_NUM	\
  	((STATIC_PKT_1_2PAGE_NUM) + (STATIC_PKT_4PAGE_NUM))
  
  typedef struct bcm_static_pkt {
  #ifdef DHD_USE_STATIC_CTRLBUF
  	struct sk_buff *skb_8k[STATIC_PKT_2PAGE_NUM];
  	unsigned char pkt_invalid[STATIC_PKT_2PAGE_NUM];
  	spinlock_t osl_pkt_lock;
  	uint32 last_allocated_index;
  #else
  	struct sk_buff *skb_4k[STATIC_PKT_1PAGE_NUM];
  	struct sk_buff *skb_8k[STATIC_PKT_2PAGE_NUM];
  #ifdef ENHANCED_STATIC_BUF
  	struct sk_buff *skb_16k;
  #endif /* ENHANCED_STATIC_BUF */
  	struct semaphore osl_pkt_sem;
  #endif /* DHD_USE_STATIC_CTRLBUF */
  	unsigned char pkt_use[STATIC_PKT_MAX_NUM];
  } bcm_static_pkt_t;
  
  static bcm_static_pkt_t *bcm_static_skb = 0;
  
  
  
  void* wifi_platform_prealloc(void *adapter, int section, unsigned long size);
  #endif /* CONFIG_DHD_USE_STATIC_BUF */
  
  typedef struct bcm_mem_link {
  	struct bcm_mem_link *prev;
  	struct bcm_mem_link *next;
  	uint	size;
  	int	line;
  	void 	*osh;
  	char	file[BCM_MEM_FILENAME_LEN];
  } bcm_mem_link_t;
  
  struct osl_cmn_info {
  	atomic_t malloced;
  	atomic_t pktalloced;    /* Number of allocated packet buffers */
  	spinlock_t dbgmem_lock;
  	bcm_mem_link_t *dbgmem_list;
  	spinlock_t pktalloc_lock;
  	atomic_t refcount; /* Number of references to this shared structure. */
  };
  typedef struct osl_cmn_info osl_cmn_t;
  
  struct osl_info {
  	osl_pubinfo_t pub;
  	uint32  flags;		/* If specific cases to be handled in the OSL */
  #ifdef CTFPOOL
  	ctfpool_t *ctfpool;
  #endif /* CTFPOOL */
  	uint magic;
  	void *pdev;
  	uint failed;
  	uint bustype;
  	osl_cmn_t *cmn; /* Common OSL related data shred between two OSH's */
  
  	void *bus_handle;
  };
  
  #define OSL_PKTTAG_CLEAR(p) \
  do { \
  	struct sk_buff *s = (struct sk_buff *)(p); \
  	ASSERT(OSL_PKTTAG_SZ == 32); \
  	*(uint32 *)(&s->cb[0]) = 0; *(uint32 *)(&s->cb[4]) = 0; \
  	*(uint32 *)(&s->cb[8]) = 0; *(uint32 *)(&s->cb[12]) = 0; \
  	*(uint32 *)(&s->cb[16]) = 0; *(uint32 *)(&s->cb[20]) = 0; \
  	*(uint32 *)(&s->cb[24]) = 0; *(uint32 *)(&s->cb[28]) = 0; \
  } while (0)
  
  /* PCMCIA attribute space access macros */
  
  /* Global ASSERT type flag */
  uint32 g_assert_type = 0;
  module_param(g_assert_type, int, 0);
  
  static int16 linuxbcmerrormap[] =
  {	0, 			/* 0 */
  	-EINVAL,		/* BCME_ERROR */
  	-EINVAL,		/* BCME_BADARG */
  	-EINVAL,		/* BCME_BADOPTION */
  	-EINVAL,		/* BCME_NOTUP */
  	-EINVAL,		/* BCME_NOTDOWN */
  	-EINVAL,		/* BCME_NOTAP */
  	-EINVAL,		/* BCME_NOTSTA */
  	-EINVAL,		/* BCME_BADKEYIDX */
  	-EINVAL,		/* BCME_RADIOOFF */
  	-EINVAL,		/* BCME_NOTBANDLOCKED */
  	-EINVAL, 		/* BCME_NOCLK */
  	-EINVAL, 		/* BCME_BADRATESET */
  	-EINVAL, 		/* BCME_BADBAND */
  	-E2BIG,			/* BCME_BUFTOOSHORT */
  	-E2BIG,			/* BCME_BUFTOOLONG */
  	-EBUSY, 		/* BCME_BUSY */
  	-EINVAL, 		/* BCME_NOTASSOCIATED */
  	-EINVAL, 		/* BCME_BADSSIDLEN */
  	-EINVAL, 		/* BCME_OUTOFRANGECHAN */
  	-EINVAL, 		/* BCME_BADCHAN */
  	-EFAULT, 		/* BCME_BADADDR */
  	-ENOMEM, 		/* BCME_NORESOURCE */
  	-EOPNOTSUPP,		/* BCME_UNSUPPORTED */
  	-EMSGSIZE,		/* BCME_BADLENGTH */
  	-EINVAL,		/* BCME_NOTREADY */
  	-EPERM,			/* BCME_EPERM */
  	-ENOMEM, 		/* BCME_NOMEM */
  	-EINVAL, 		/* BCME_ASSOCIATED */
  	-ERANGE, 		/* BCME_RANGE */
  	-EINVAL, 		/* BCME_NOTFOUND */
  	-EINVAL, 		/* BCME_WME_NOT_ENABLED */
  	-EINVAL, 		/* BCME_TSPEC_NOTFOUND */
  	-EINVAL, 		/* BCME_ACM_NOTSUPPORTED */
  	-EINVAL,		/* BCME_NOT_WME_ASSOCIATION */
  	-EIO,			/* BCME_SDIO_ERROR */
  	-ENODEV,		/* BCME_DONGLE_DOWN */
  	-EINVAL,		/* BCME_VERSION */
  	-EIO,			/* BCME_TXFAIL */
  	-EIO,			/* BCME_RXFAIL */
  	-ENODEV,		/* BCME_NODEVICE */
  	-EINVAL,		/* BCME_NMODE_DISABLED */
  	-ENODATA,		/* BCME_NONRESIDENT */
  	-EINVAL,		/* BCME_SCANREJECT */
  	-EINVAL,		/* BCME_USAGE_ERROR */
  	-EIO,     		/* BCME_IOCTL_ERROR */
  	-EIO,			/* BCME_SERIAL_PORT_ERR */
  	-EOPNOTSUPP,	/* BCME_DISABLED, BCME_NOTENABLED */
  	-EIO,			/* BCME_DECERR */
  	-EIO,			/* BCME_ENCERR */
  	-EIO,			/* BCME_MICERR */
  	-ERANGE,		/* BCME_REPLAY */
  	-EINVAL,		/* BCME_IE_NOTFOUND */
  
  /* When an new error code is added to bcmutils.h, add os
   * specific error translation here as well
   */
  /* check if BCME_LAST changed since the last time this function was updated */
  #if BCME_LAST != -52
  #error "You need to add a OS error translation in the linuxbcmerrormap \
  	for new error code defined in bcmutils.h"
  #endif
  };
  
  /* translate bcmerrors into linux errors */
  int
  osl_error(int bcmerror)
  {
  	if (bcmerror > 0)
  		bcmerror = 0;
  	else if (bcmerror < BCME_LAST)
  		bcmerror = BCME_ERROR;
  
  	/* Array bounds covered by ASSERT in osl_attach */
  	return linuxbcmerrormap[-bcmerror];
  }
  #ifdef BCM_ALLOC_STATIC_24K
  #define MAX_STATIC_FLOWRING 40
  #define FLOWRING_BUF_SIZE (24*1024)
  typedef struct {
  	void * va[MAX_STATIC_FLOWRING];
  	dmaaddr_t pap[MAX_STATIC_FLOWRING];
  	unsigned char used[MAX_STATIC_FLOWRING];
  } bcm_static_flowring_t;
  
  static bcm_static_flowring_t *static_flowring = NULL;
  static unsigned int atomic_allocated_size = 0;
  
  static void*
  osl_dma_alloc_static_flowring(dmaaddr_t *pap, int *idx)
  {
  	int i = 0;
  
  	if (!static_flowring)
  		return NULL;
  
  	for (i=0;i<MAX_STATIC_FLOWRING;i++) {
  		if (!static_flowring->used[i]) {
  			printf("[WLAN] %s: get static flow buf idx=%d\n", __FUNCTION__, i);
  			*idx = i;
  			*pap = static_flowring->pap[i];
  			static_flowring->used[i] = 1;
  			return static_flowring->va[i];
  		}
  	}
  
          printf("[WLAN] %s: out of static flowring\n", __FUNCTION__);
  	return NULL;
  }
  
  static bool
  osl_dma_clear_static_flowring(void *va)
  {
  	int i = 0;
  	if (!static_flowring)
  		return false;
  
  	for (i=0;i<MAX_STATIC_FLOWRING;i++) {
  		if (static_flowring->va[i] == va) {
  			printf("[WLAN] %s: return static flow buf idx=%d\n", __FUNCTION__, i);
  			static_flowring->used[i] = 0;
  			return true;
  		}
  	}
  
  	return false;
  }
  
  static void
  osl_dma_free_static_flowring(osl_t *osh)
  {
  	int i = 0;
  
  	if (!static_flowring) 
  		return;
  
  	for (i=0;i<MAX_STATIC_FLOWRING;i++) {
  		pci_free_consistent(osh->pdev, FLOWRING_BUF_SIZE,
  			static_flowring->va[i], static_flowring->pap[i]);
  	}
  
  	kfree(static_flowring);
  }
  #endif
  #ifdef SHARED_OSL_CMN
  osl_t *
  osl_attach(void *pdev, uint bustype, bool pkttag, void **osl_cmn)
  {
  #else
  osl_t *
  osl_attach(void *pdev, uint bustype, bool pkttag)
  {
  	void **osl_cmn = NULL;
  #endif /* SHARED_OSL_CMN */
  	osl_t *osh;
  	gfp_t flags;
  
  	flags = CAN_SLEEP() ? GFP_KERNEL: GFP_ATOMIC;
  	if (!(osh = kmalloc(sizeof(osl_t), flags)))
  		return osh;
  
  	//ASSERT(osh);
  
  	bzero(osh, sizeof(osl_t));
  
  	if (osl_cmn == NULL || *osl_cmn == NULL) {
  		if (!(osh->cmn = kmalloc(sizeof(osl_cmn_t), flags))) {
  			kfree(osh);
  			return NULL;
  		}
  		bzero(osh->cmn, sizeof(osl_cmn_t));
  		if (osl_cmn)
  			*osl_cmn = osh->cmn;
  		atomic_set(&osh->cmn->malloced, 0);
  		osh->cmn->dbgmem_list = NULL;
  		spin_lock_init(&(osh->cmn->dbgmem_lock));
  
  		spin_lock_init(&(osh->cmn->pktalloc_lock));
  
  	} else {
  		osh->cmn = *osl_cmn;
  	}
  	atomic_add(1, &osh->cmn->refcount);
  
  	/* Check that error map has the right number of entries in it */
  	ASSERT(ABS(BCME_LAST) == (ARRAYSIZE(linuxbcmerrormap) - 1));
  
  	osh->failed = 0;
  	osh->pdev = pdev;
  	osh->pub.pkttag = pkttag;
  	osh->bustype = bustype;
  	osh->magic = OS_HANDLE_MAGIC;
  
  	switch (bustype) {
  		case PCI_BUS:
  #ifdef BCM_ALLOC_STATIC_24K
  			if ((static_flowring = kmalloc(sizeof(bcm_static_flowring_t), flags))){
  				int i;
  				for (i=0; i<MAX_STATIC_FLOWRING; i++) {
  					static_flowring->va[i] =
  						dma_alloc_coherent(pdev, FLOWRING_BUF_SIZE, &(static_flowring->pap[i]), flags);
  					static_flowring->used[i] = 0;
  					if (!static_flowring->va[i])
  						printf("[WLAN] %s: cannot malloc va of static_flowring\n", __FUNCTION__);
  				}
  			} else {
  				printf("[WLAN] %s: cannot malloc static_flowring\n", __FUNCTION__);
  			}
  #endif
  		case SI_BUS:
  		case PCMCIA_BUS:
  			osh->pub.mmbus = TRUE;
  			break;
  		case JTAG_BUS:
  		case SDIO_BUS:
  		case USB_BUS:
  		case SPI_BUS:
  		case RPC_BUS:
  			osh->pub.mmbus = FALSE;
  			break;
  		default:
  			ASSERT(FALSE);
  			break;
  	}
  
  
  
  	return osh;
  }
  
  int osl_static_mem_init(osl_t *osh, void *adapter)
  {
  #ifdef CONFIG_DHD_USE_STATIC_BUF
  	if (!bcm_static_buf && adapter) {
  		if (!(bcm_static_buf = (bcm_static_buf_t *)wifi_platform_prealloc(adapter,
  			3, STATIC_BUF_SIZE + STATIC_BUF_TOTAL_LEN))) {
  				printf("can not alloc static buf!\n");
  			bcm_static_skb = NULL;
  			ASSERT(osh->magic == OS_HANDLE_MAGIC);
  			return -ENOMEM;
  		} else {
  			printf("alloc static buf at %p!\n", bcm_static_buf);
  		}
  #ifndef BCM_PCIE_UPDATE
  		spin_lock_init(&bcm_static_buf->static_lock);
  #else
  		sema_init(&bcm_static_buf->static_sem, 1);
  #endif
  		bcm_static_buf->buf_ptr = (unsigned char *)bcm_static_buf + STATIC_BUF_SIZE;
  	}
  
  #if defined(BCMSDIO) || defined(DHD_USE_STATIC_CTRLBUF)
  	if (!bcm_static_skb && adapter) {
  		int i;
  		void *skb_buff_ptr = 0;
  		bcm_static_skb = (bcm_static_pkt_t *)((char *)bcm_static_buf + 2048);
  		skb_buff_ptr = wifi_platform_prealloc(adapter, 4, 0);
  		if (!skb_buff_ptr) {
  			printf("cannot alloc static buf!\n");
  			bcm_static_buf = NULL;
  			bcm_static_skb = NULL;
  			ASSERT(osh->magic == OS_HANDLE_MAGIC);
  			return -ENOMEM;
  		}
  
  		bcopy(skb_buff_ptr, bcm_static_skb, sizeof(struct sk_buff *) *
  			(STATIC_PKT_MAX_NUM));
  		for (i = 0; i < STATIC_PKT_MAX_NUM; i++) {
  			bcm_static_skb->pkt_use[i] = 0;
  		}
  
  #ifdef DHD_USE_STATIC_CTRLBUF
  			spin_lock_init(&bcm_static_skb->osl_pkt_lock);
  #ifdef BCM_PCIE_UPDATE
  			bcm_static_skb->last_allocated_index = 0;
  #endif
  #else
  		sema_init(&bcm_static_skb->osl_pkt_sem, 1);
  #endif /* DHD_USE_STATIC_CTRLBUF */
  	}
  #endif /* BCMSDIO || DHD_USE_STATIC_CTRLBUF */
  #endif /* CONFIG_DHD_USE_STATIC_BUF */
  
  	return 0;
  }
  
  void osl_set_bus_handle(osl_t *osh, void *bus_handle)
  {
  	osh->bus_handle = bus_handle;
  }
  
  void* osl_get_bus_handle(osl_t *osh)
  {
  	return osh->bus_handle;
  }
  
  void
  osl_detach(osl_t *osh)
  {
  	if (osh == NULL)
  		return;
  
  	ASSERT(osh->magic == OS_HANDLE_MAGIC);
  	atomic_sub(1, &osh->cmn->refcount);
  	if (atomic_read(&osh->cmn->refcount) == 0) {
  			kfree(osh->cmn);
  	}
  #ifdef BCM_ALLOC_STATIC_24K
  	osl_dma_free_static_flowring(osh);
  #endif
  	kfree(osh);
  }
  
  int osl_static_mem_deinit(osl_t *osh, void *adapter)
  {
  #ifdef CONFIG_DHD_USE_STATIC_BUF
  	if (bcm_static_buf) {
  		bcm_static_buf = 0;
  	}
  #ifdef BCMSDIO
  	if (bcm_static_skb) {
  		bcm_static_skb = 0;
  	}
  #endif /* BCMSDIO */
  #endif /* CONFIG_DHD_USE_STATIC_BUF */
  	return 0;
  }
  
  
  static struct sk_buff *osl_alloc_skb(osl_t *osh, unsigned int len)
  {
  	struct sk_buff *skb;
  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)
  	gfp_t flags = (in_atomic() || irqs_disabled()) ? GFP_ATOMIC : GFP_KERNEL;
  #if defined(CONFIG_SPARSEMEM) && defined(CONFIG_ZONE_DMA)
  	flags |= GFP_ATOMIC;
  #endif
  
  	skb = __dev_alloc_skb(len, flags);
  #else
  	skb = dev_alloc_skb(len);
  #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) */
  	return skb;
  }
  
  #ifdef CTFPOOL
  
  #ifdef CTFPOOL_SPINLOCK
  #define CTFPOOL_LOCK(ctfpool, flags)	spin_lock_irqsave(&(ctfpool)->lock, flags)
  #define CTFPOOL_UNLOCK(ctfpool, flags)	spin_unlock_irqrestore(&(ctfpool)->lock, flags)
  #else
  #define CTFPOOL_LOCK(ctfpool, flags)	spin_lock_bh(&(ctfpool)->lock)
  #define CTFPOOL_UNLOCK(ctfpool, flags)	spin_unlock_bh(&(ctfpool)->lock)
  #endif /* CTFPOOL_SPINLOCK */
  /*
   * Allocate and add an object to packet pool.
   */
  void *
  osl_ctfpool_add(osl_t *osh)
  {
  	struct sk_buff *skb;
  #ifdef CTFPOOL_SPINLOCK
  	unsigned long flags;
  #endif /* CTFPOOL_SPINLOCK */
  
  	if ((osh == NULL) || (osh->ctfpool == NULL))
  		return NULL;
  
  	CTFPOOL_LOCK(osh->ctfpool, flags);
  	ASSERT(osh->ctfpool->curr_obj <= osh->ctfpool->max_obj);
  
  	/* No need to allocate more objects */
  	if (osh->ctfpool->curr_obj == osh->ctfpool->max_obj) {
  		CTFPOOL_UNLOCK(osh->ctfpool, flags);
  		return NULL;
  	}
  
  	/* Allocate a new skb and add it to the ctfpool */
  	skb = osl_alloc_skb(osh, osh->ctfpool->obj_size);
  	if (skb == NULL) {
  		printf("%s: skb alloc of len %d failed\n", __FUNCTION__,
  		       osh->ctfpool->obj_size);
  		CTFPOOL_UNLOCK(osh->ctfpool, flags);
  		return NULL;
  	}
  
  	/* Add to ctfpool */
  	skb->next = (struct sk_buff *)osh->ctfpool->head;
  	osh->ctfpool->head = skb;
  	osh->ctfpool->fast_frees++;
  	osh->ctfpool->curr_obj++;
  
  	/* Hijack a skb member to store ptr to ctfpool */
  	CTFPOOLPTR(osh, skb) = (void *)osh->ctfpool;
  
  	/* Use bit flag to indicate skb from fast ctfpool */
  	PKTFAST(osh, skb) = FASTBUF;
  
  	CTFPOOL_UNLOCK(osh->ctfpool, flags);
  
  	return skb;
  }
  
  /*
   * Add new objects to the pool.
   */
  void
  osl_ctfpool_replenish(osl_t *osh, uint thresh)
  {
  	if ((osh == NULL) || (osh->ctfpool == NULL))
  		return;
  
  	/* Do nothing if no refills are required */
  	while ((osh->ctfpool->refills > 0) && (thresh--)) {
  		osl_ctfpool_add(osh);
  		osh->ctfpool->refills--;
  	}
  }
  
  /*
   * Initialize the packet pool with specified number of objects.
   */
  int32
  osl_ctfpool_init(osl_t *osh, uint numobj, uint size)
  {
  	gfp_t flags;
  
  	flags = CAN_SLEEP() ? GFP_KERNEL: GFP_ATOMIC;
  	osh->ctfpool = kzalloc(sizeof(ctfpool_t), flags);
  	ASSERT(osh->ctfpool);
  
  	osh->ctfpool->max_obj = numobj;
  	osh->ctfpool->obj_size = size;
  
  	spin_lock_init(&osh->ctfpool->lock);
  
  	while (numobj--) {
  		if (!osl_ctfpool_add(osh))
  			return -1;
  		osh->ctfpool->fast_frees--;
  	}
  
  	return 0;
  }
  
  /*
   * Cleanup the packet pool objects.
   */
  void
  osl_ctfpool_cleanup(osl_t *osh)
  {
  	struct sk_buff *skb, *nskb;
  #ifdef CTFPOOL_SPINLOCK
  	unsigned long flags;
  #endif /* CTFPOOL_SPINLOCK */
  
  	if ((osh == NULL) || (osh->ctfpool == NULL))
  		return;
  
  	CTFPOOL_LOCK(osh->ctfpool, flags);
  
  	skb = osh->ctfpool->head;
  
  	while (skb != NULL) {
  		nskb = skb->next;
  		dev_kfree_skb(skb);
  		skb = nskb;
  		osh->ctfpool->curr_obj--;
  	}
  
  	ASSERT(osh->ctfpool->curr_obj == 0);
  	osh->ctfpool->head = NULL;
  	CTFPOOL_UNLOCK(osh->ctfpool, flags);
  
  	kfree(osh->ctfpool);
  	osh->ctfpool = NULL;
  }
  
  void
  osl_ctfpool_stats(osl_t *osh, void *b)
  {
  	struct bcmstrbuf *bb;
  
  	if ((osh == NULL) || (osh->ctfpool == NULL))
  		return;
  
  #ifdef CONFIG_DHD_USE_STATIC_BUF
  	if (bcm_static_buf) {
  		bcm_static_buf = 0;
  	}
  #ifdef BCMSDIO
  	if (bcm_static_skb) {
  		bcm_static_skb = 0;
  	}
  #endif /* BCMSDIO */
  #endif /* CONFIG_DHD_USE_STATIC_BUF */
  
  	bb = b;
  
  	ASSERT((osh != NULL) && (bb != NULL));
  
  	bcm_bprintf(bb, "max_obj %d obj_size %d curr_obj %d refills %d\n",
  	            osh->ctfpool->max_obj, osh->ctfpool->obj_size,
  	            osh->ctfpool->curr_obj, osh->ctfpool->refills);
  	bcm_bprintf(bb, "fast_allocs %d fast_frees %d slow_allocs %d\n",
  	            osh->ctfpool->fast_allocs, osh->ctfpool->fast_frees,
  	            osh->ctfpool->slow_allocs);
  }
  
  static inline struct sk_buff *
  osl_pktfastget(osl_t *osh, uint len)
  {
  	struct sk_buff *skb;
  #ifdef CTFPOOL_SPINLOCK
  	unsigned long flags;
  #endif /* CTFPOOL_SPINLOCK */
  
  	/* Try to do fast allocate. Return null if ctfpool is not in use
  	 * or if there are no items in the ctfpool.
  	 */
  	if (osh->ctfpool == NULL)
  		return NULL;
  
  	CTFPOOL_LOCK(osh->ctfpool, flags);
  	if (osh->ctfpool->head == NULL) {
  		ASSERT(osh->ctfpool->curr_obj == 0);
  		osh->ctfpool->slow_allocs++;
  		CTFPOOL_UNLOCK(osh->ctfpool, flags);
  		return NULL;
  	}
  
  	if (len > osh->ctfpool->obj_size) {
  		CTFPOOL_UNLOCK(osh->ctfpool, flags);
  		return NULL;
  	}
  
  	ASSERT(len <= osh->ctfpool->obj_size);
  
  	/* Get an object from ctfpool */
  	skb = (struct sk_buff *)osh->ctfpool->head;
  	osh->ctfpool->head = (void *)skb->next;
  
  	osh->ctfpool->fast_allocs++;
  	osh->ctfpool->curr_obj--;
  	ASSERT(CTFPOOLHEAD(osh, skb) == (struct sock *)osh->ctfpool->head);
  	CTFPOOL_UNLOCK(osh->ctfpool, flags);
  
  	/* Init skb struct */
  	skb->next = skb->prev = NULL;
  #if defined(__ARM_ARCH_7A__)
  	skb->data = skb->head + NET_SKB_PAD;
  	skb->tail = skb->head + NET_SKB_PAD;
  #else
  	skb->data = skb->head + 16;
  	skb->tail = skb->head + 16;
  #endif /* __ARM_ARCH_7A__ */
  	skb->len = 0;
  	skb->cloned = 0;
  #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14)
  	skb->list = NULL;
  #endif
  	atomic_set(&skb->users, 1);
  
  	PKTSETCLINK(skb, NULL);
  	PKTCCLRATTR(skb);
  	PKTFAST(osh, skb) &= ~(CTFBUF | SKIPCT | CHAINED);
  
  	return skb;
  }
  #endif /* CTFPOOL */
  
  #if defined(BCM_GMAC3)
  /* Account for a packet delivered to downstream forwarder.
   * Decrement a GMAC forwarder interface's pktalloced count.
   */
  void BCMFASTPATH
  osl_pkt_tofwder(osl_t *osh, void *skbs, int skb_cnt)
  {
  
  	atomic_sub(skb_cnt, &osh->cmn->pktalloced);
  }
  
  /* Account for a downstream forwarder delivered packet to a WL/DHD driver.
   * Increment a GMAC forwarder interface's pktalloced count.
   */
  void BCMFASTPATH
  osl_pkt_frmfwder(osl_t *osh, void *skbs, int skb_cnt)
  {
  
  
  	atomic_add(skb_cnt, &osh->cmn->pktalloced);
  }
  
  #endif /* BCM_GMAC3 */
  
  /* Convert a driver packet to native(OS) packet
   * In the process, packettag is zeroed out before sending up
   * IP code depends on skb->cb to be setup correctly with various options
   * In our case, that means it should be 0
   */
  struct sk_buff * BCMFASTPATH
  osl_pkt_tonative(osl_t *osh, void *pkt)
  {
  	struct sk_buff *nskb;
  
  	if (osh->pub.pkttag)
  		OSL_PKTTAG_CLEAR(pkt);
  
  	/* Decrement the packet counter */
  	for (nskb = (struct sk_buff *)pkt; nskb; nskb = nskb->next) {
  		atomic_sub(PKTISCHAINED(nskb) ? PKTCCNT(nskb) : 1, &osh->cmn->pktalloced);
  
  	}
  	return (struct sk_buff *)pkt;
  }
  
  /* Convert a native(OS) packet to driver packet.
   * In the process, native packet is destroyed, there is no copying
   * Also, a packettag is zeroed out
   */
  void * BCMFASTPATH
  osl_pkt_frmnative(osl_t *osh, void *pkt)
  {
  	struct sk_buff *nskb;
  
  	if (osh->pub.pkttag)
  		OSL_PKTTAG_CLEAR(pkt);
  
  	/* Increment the packet counter */
  	for (nskb = (struct sk_buff *)pkt; nskb; nskb = nskb->next) {
  		atomic_add(PKTISCHAINED(nskb) ? PKTCCNT(nskb) : 1, &osh->cmn->pktalloced);
  
  	}
  	return (void *)pkt;
  }
  
  /* Return a new packet. zero out pkttag */
  void * BCMFASTPATH
  osl_pktget(osl_t *osh, uint len)
  {
  	struct sk_buff *skb;
  
  #ifdef CTFPOOL
  	/* Allocate from local pool */
  	skb = osl_pktfastget(osh, len);
  	if ((skb != NULL) || ((skb = osl_alloc_skb(osh, len)) != NULL)) {
  #else /* CTFPOOL */
  	if ((skb = osl_alloc_skb(osh, len))) {
  #endif /* CTFPOOL */
  		skb->tail += len;
  		skb->len  += len;
  		skb->priority = 0;
  
  		atomic_inc(&osh->cmn->pktalloced);
  	}
  	return ((void*) skb);
  }
  
  #ifdef CTFPOOL
  static inline void
  osl_pktfastfree(osl_t *osh, struct sk_buff *skb)
  {
  	ctfpool_t *ctfpool;
  #ifdef CTFPOOL_SPINLOCK
  	unsigned long flags;
  #endif /* CTFPOOL_SPINLOCK */
  
  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 14)
  	skb->tstamp.tv.sec = 0;
  #else
  	skb->stamp.tv_sec = 0;
  #endif
  
  	/* We only need to init the fields that we change */
  	skb->dev = NULL;
  #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
  	skb->dst = NULL;
  #endif
  	OSL_PKTTAG_CLEAR(skb);
  	skb->ip_summed = 0;
  
  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36)
  	skb_orphan(skb);
  #else
  	skb->destructor = NULL;
  #endif
  
  	ctfpool = (ctfpool_t *)CTFPOOLPTR(osh, skb);
  	ASSERT(ctfpool != NULL);
  
  	/* Add object to the ctfpool */
  	CTFPOOL_LOCK(ctfpool, flags);
  	skb->next = (struct sk_buff *)ctfpool->head;
  	ctfpool->head = (void *)skb;
  
  	ctfpool->fast_frees++;
  	ctfpool->curr_obj++;
  
  	ASSERT(ctfpool->curr_obj <= ctfpool->max_obj);
  	CTFPOOL_UNLOCK(ctfpool, flags);
  }
  #endif /* CTFPOOL */
  
  /* Free the driver packet. Free the tag if present */
  void BCMFASTPATH
  osl_pktfree(osl_t *osh, void *p, bool send)
  {
  	struct sk_buff *skb, *nskb;
  	if (osh == NULL)
  		return;
  
  	skb = (struct sk_buff*) p;
  
  	if (send && osh->pub.tx_fn)
  		osh->pub.tx_fn(osh->pub.tx_ctx, p, 0);
  
  	PKTDBG_TRACE(osh, (void *) skb, PKTLIST_PKTFREE);
  
  #if defined(CONFIG_DHD_USE_STATIC_BUF) && defined(DHD_USE_STATIC_CTRLBUF)
  	if (skb && (skb->mac_len == PREALLOC_USED_MAGIC)) {
  		printk("%s: pkt %p is from static pool\n",
  			__FUNCTION__, p);
  		dump_stack();
  		return;
  	}
  
  	if (skb && (skb->mac_len == PREALLOC_FREE_MAGIC)) {
  		printk("%s: pkt %p is from static pool and not in used\n",
  			__FUNCTION__, p);
  		dump_stack();
  		return;
  	}
  #endif /* CONFIG_DHD_USE_STATIC_BUF && DHD_USE_STATIC_CTRLBUF */
  
  	/* perversion: we use skb->next to chain multi-skb packets */
  	while (skb) {
  		nskb = skb->next;
  		skb->next = NULL;
  
  
  
  #ifdef CTFPOOL
  		if (PKTISFAST(osh, skb)) {
  			if (atomic_read(&skb->users) == 1)
  				smp_rmb();
  			else if (!atomic_dec_and_test(&skb->users))
  				goto next_skb;
  			osl_pktfastfree(osh, skb);
  		} else
  #endif
  		{
  			dev_kfree_skb_any(skb);
  		}
  #ifdef CTFPOOL
  next_skb:
  #endif
  		atomic_dec(&osh->cmn->pktalloced);
  		skb = nskb;
  	}
  }
  
  #ifdef CONFIG_DHD_USE_STATIC_BUF
  #ifdef BCM_PCIE_UPDATE
  void*
  osl_pktget_static(osl_t *osh, uint len)
  {
  	int i = 0;
  	struct sk_buff *skb;
  #ifdef DHD_USE_STATIC_CTRLBUF
  	unsigned long flags;
  #endif /* DHD_USE_STATIC_CTRLBUF */
  
  	if (!bcm_static_skb)
  		return osl_pktget(osh, len);
  
  	if (len > DHD_SKB_MAX_BUFSIZE) {
  		printf("%s: attempt to allocate huge packet (0x%x)\n", __FUNCTION__, len);
  		return osl_pktget(osh, len);
  	}
  
  #ifdef DHD_USE_STATIC_CTRLBUF
  	spin_lock_irqsave(&bcm_static_skb->osl_pkt_lock, flags);
  
  	if (len <= DHD_SKB_2PAGE_BUFSIZE) {
  		uint32 index;
  		for (i = 0; i < STATIC_PKT_2PAGE_NUM; i++) {
  			index = bcm_static_skb->last_allocated_index % STATIC_PKT_2PAGE_NUM;
  			bcm_static_skb->last_allocated_index++;
  			if (bcm_static_skb->skb_8k[index] &&
  				bcm_static_skb->pkt_use[index] == 0) {
  				break;
  			}
  		}
  
  		if ((i != STATIC_PKT_2PAGE_NUM) &&
  			(index >= 0) && (index < STATIC_PKT_2PAGE_NUM)) {
  			bcm_static_skb->pkt_use[index] = 1;
  			skb = bcm_static_skb->skb_8k[index];
  			skb->data = skb->head;
  #ifdef NET_SKBUFF_DATA_USES_OFFSET
  			skb_set_tail_pointer(skb, NET_SKB_PAD);
  #else
  			skb->tail = skb->data + NET_SKB_PAD;
  #endif /* NET_SKBUFF_DATA_USES_OFFSET */
  			skb->data += NET_SKB_PAD;
  			skb->cloned = 0;
  			skb->priority = 0;
  #ifdef NET_SKBUFF_DATA_USES_OFFSET
  			skb_set_tail_pointer(skb, len);
  #else
  			skb->tail = skb->data + len;
  #endif /* NET_SKBUFF_DATA_USES_OFFSET */
  			skb->len = len;
  			skb->mac_len = PREALLOC_USED_MAGIC;
  			spin_unlock_irqrestore(&bcm_static_skb->osl_pkt_lock, flags);
  			return skb;
  		}
  	}
  
  	spin_unlock_irqrestore(&bcm_static_skb->osl_pkt_lock, flags);
  	printk("%s: all static pkt in use!\n", __FUNCTION__);
  	return NULL;
  #else
  	down(&bcm_static_skb->osl_pkt_sem);
  
  	if (len <= DHD_SKB_1PAGE_BUFSIZE) {
  		for (i = 0; i < STATIC_PKT_1PAGE_NUM; i++) {
  			if (bcm_static_skb->skb_4k[i] &&
  				bcm_static_skb->pkt_use[i] == 0) {
  				break;
  			}
  		}
  
  		if (i != STATIC_PKT_1PAGE_NUM) {
  			bcm_static_skb->pkt_use[i] = 1;
  
  			skb = bcm_static_skb->skb_4k[i];
  #ifdef NET_SKBUFF_DATA_USES_OFFSET
  			skb_set_tail_pointer(skb, len);
  #else
  			skb->tail = skb->data + len;
  #endif /* NET_SKBUFF_DATA_USES_OFFSET */
  			skb->len = len;
  
  			up(&bcm_static_skb->osl_pkt_sem);
  #ifdef HW_WIFI_SECURITY_PATCH
  			memset(skb->data, 0, skb->len);
  #endif
  			return skb;
  		}
  	}
  
  	if (len <= DHD_SKB_2PAGE_BUFSIZE) {
  		for (i = STATIC_PKT_1PAGE_NUM; i < STATIC_PKT_1_2PAGE_NUM; i++) {
  			if (bcm_static_skb->skb_8k[i - STATIC_PKT_1PAGE_NUM] &&
  				bcm_static_skb->pkt_use[i] == 0) {
  				break;
  			}
  		}
  
  		if ((i >= STATIC_PKT_1PAGE_NUM) && (i < STATIC_PKT_1_2PAGE_NUM)) {
  			bcm_static_skb->pkt_use[i] = 1;
  			skb = bcm_static_skb->skb_8k[i - STATIC_PKT_1PAGE_NUM];
  #ifdef NET_SKBUFF_DATA_USES_OFFSET
  			skb_set_tail_pointer(skb, len);
  #else
  			skb->tail = skb->data + len;
  #endif /* NET_SKBUFF_DATA_USES_OFFSET */
  			skb->len = len;
  
  			up(&bcm_static_skb->osl_pkt_sem);
  #ifdef HW_WIFI_SECURITY_PATCH
  			memset(skb->data, 0, skb->len);
  #endif
  			return skb;
  		}
  	}
  
  #if defined(ENHANCED_STATIC_BUF)
  	if (bcm_static_skb->skb_16k &&
  		bcm_static_skb->pkt_use[STATIC_PKT_MAX_NUM - 1] == 0) {
  		bcm_static_skb->pkt_use[STATIC_PKT_MAX_NUM - 1] = 1;
  
  		skb = bcm_static_skb->skb_16k;
  #ifdef NET_SKBUFF_DATA_USES_OFFSET
  		skb_set_tail_pointer(skb, len);
  #else
  		skb->tail = skb->data + len;
  #endif /* NET_SKBUFF_DATA_USES_OFFSET */
  		skb->len = len;
  
  		up(&bcm_static_skb->osl_pkt_sem);
  #ifdef HW_WIFI_SECURITY_PATCH
  		memset(skb->data, 0, skb->len);
  #endif
  
  		return skb;
  	}
  #endif /* ENHANCED_STATIC_BUF */
  
  	up(&bcm_static_skb->osl_pkt_sem);
  	printf("%s: all static pkt in use!\n", __FUNCTION__);
  	return osl_pktget(osh, len);
  #endif /* DHD_USE_STATIC_CTRLBUF */
  }
  
  void
  osl_pktfree_static(osl_t *osh, void *p, bool send)
  {
  	int i;
  #ifdef DHD_USE_STATIC_CTRLBUF
  	struct sk_buff *skb = (struct sk_buff *)p;
  	unsigned long flags;
  #endif /* DHD_USE_STATIC_CTRLBUF */
  
  	if (!p) {
  		return;
  	}
  
  	if (!bcm_static_skb) {
  		osl_pktfree(osh, p, send);
  		return;
  	}
  
  #ifdef DHD_USE_STATIC_CTRLBUF
  	spin_lock_irqsave(&bcm_static_skb->osl_pkt_lock, flags);
  
  	for (i = 0; i < STATIC_PKT_2PAGE_NUM; i++) {
  		if (p == bcm_static_skb->skb_8k[i]) {
  			if (bcm_static_skb->pkt_use[i] == 0) {
  				printk("%s: static pkt idx %d(%p) is double free\n",
  					__FUNCTION__, i, p);
  			} else {
  				bcm_static_skb->pkt_use[i] = 0;
  			}
  
  			if (skb->mac_len != PREALLOC_USED_MAGIC) {
  				printk("%s: static pkt idx %d(%p) is not in used\n",
  					__FUNCTION__, i, p);
  			}
  
  			skb->mac_len = PREALLOC_FREE_MAGIC;
  			spin_unlock_irqrestore(&bcm_static_skb->osl_pkt_lock, flags);
  			return;
  		}
  	}
  
  	spin_unlock_irqrestore(&bcm_static_skb->osl_pkt_lock, flags);
  	printf("%s: packet %p does not exist in the pool\n", __FUNCTION__, p);
  #else
  	down(&bcm_static_skb->osl_pkt_sem);
  	for (i = 0; i < STATIC_PKT_1PAGE_NUM; i++) {
  		if (p == bcm_static_skb->skb_4k[i]) {
  			bcm_static_skb->pkt_use[i] = 0;
  			up(&bcm_static_skb->osl_pkt_sem);
  			return;
  		}
  	}
  
  	for (i = STATIC_PKT_1PAGE_NUM; i < STATIC_PKT_1_2PAGE_NUM; i++) {
  		if (p == bcm_static_skb->skb_8k[i - STATIC_PKT_1PAGE_NUM]) {
  			if (bcm_static_skb->pkt_use[i] == 0)
  				printf("%s: double free! pkt idx %d(%p)\n", __FUNCTION__, i, p);
  
  			bcm_static_skb->pkt_use[i] = 0;
  			up(&bcm_static_skb->osl_pkt_sem);
  			return;
  		}
  	}
  #ifdef ENHANCED_STATIC_BUF
  	if (p == bcm_static_skb->skb_16k) {
  		bcm_static_skb->pkt_use[STATIC_PKT_MAX_NUM - 1] = 0;
  		up(&bcm_static_skb->osl_pkt_sem);
  		return;
  	}
  #endif
  	up(&bcm_static_skb->osl_pkt_sem);
  	osl_pktfree(osh, p, send);
  #endif /* DHD_USE_STATIC_CTRLBUF */
  }
  
  void
  osl_pktclear_static(osl_t *osh)
  {
  	int i;
  #ifdef DHD_USE_STATIC_CTRLBUF
  	unsigned long flags;
  #endif /* DHD_USE_STATIC_CTRLBUF */
  
  	if (!bcm_static_skb) {
  		printf("%s: bcm_static_skb is NULL\n", __FUNCTION__);
  		return;
  	}
  
  #ifdef DHD_USE_STATIC_CTRLBUF
  	spin_lock_irqsave(&bcm_static_skb->osl_pkt_lock, flags);
  #else
  	down(&bcm_static_skb->osl_pkt_sem);
  #endif /* DHD_USE_STATIC_CTRLBUF */
  	for (i = 0; i < STATIC_PKT_MAX_NUM; i++) {
  		if (bcm_static_skb->pkt_use[i]) {
  			bcm_static_skb->pkt_use[i] = 0;
  		}
  	}
  
  #ifdef DHD_USE_STATIC_CTRLBUF
  	spin_unlock_irqrestore(&bcm_static_skb->osl_pkt_lock, flags);
  #else
  	up(&bcm_static_skb->osl_pkt_sem);
  #endif /* DHD_USE_STATIC_CTRLBUF */
  }
  #else  /* BCM_PCIE_UPDATE */
  void*
  osl_pktget_static(osl_t *osh, uint len)
  {
  	int i = 0;
  	struct sk_buff *skb;
  #if defined(BCMPCIE)
  	unsigned long flags;
  #endif /* BCMPCIE */
  
  	if (!bcm_static_skb)
  		return osl_pktget(osh, len);
  
  	if (len > DHD_SKB_MAX_BUFSIZE) {
  		printf("%s: attempt to allocate huge packet (0x%x)\n", __FUNCTION__, len);
  		return osl_pktget(osh, len);
  	}
  
  #if defined(BCMPCIE)
  	spin_lock_irqsave(&bcm_static_skb->osl_pkt_lock, flags);
  #else
  	down(&bcm_static_skb->osl_pkt_sem);
  
  	if (len <= DHD_SKB_1PAGE_BUFSIZE) {
  		for (i = 0; i < STATIC_PKT_1PAGE_NUM; i++) {
  			if (bcm_static_skb->skb_4k[i] &&
  				bcm_static_skb->pkt_use[i] == 0) {
  				break;
  			}
  		}
  
  		if (i != STATIC_PKT_1PAGE_NUM) {
  			bcm_static_skb->pkt_use[i] = 1;
  
  			skb = bcm_static_skb->skb_4k[i];
  			skb->len = len;
  
  #ifdef NET_SKBUFF_DATA_USES_OFFSET
  			skb_set_tail_pointer(skb, len);
  #else
  			skb->tail = skb->data + len;
  #endif /* NET_SKBUFF_DATA_USES_OFFSET */
  			up(&bcm_static_skb->osl_pkt_sem);
  #ifdef HW_WIFI_SECURITY_PATCH
  			memset(skb->data, 0, skb->len);
  #endif
  
  			return skb;
  		}
  	}
  #endif /* BCMPCIE */
  
  	if (len <= DHD_SKB_2PAGE_BUFSIZE) {
  		for (i = STATIC_PKT_1PAGE_NUM; i < STATIC_PKT_1_2PAGE_NUM; i++) {
  			if (bcm_static_skb->skb_8k[i - STATIC_PKT_1PAGE_NUM] &&
  				bcm_static_skb->pkt_use[i] == 0) {
  				break;
  			}
  		}
  
  		if ((i >= STATIC_PKT_1PAGE_NUM) && (i < STATIC_PKT_1_2PAGE_NUM)) {
  			bcm_static_skb->pkt_use[i] = 1;
  			skb = bcm_static_skb->skb_8k[i - STATIC_PKT_1PAGE_NUM];
  #if defined(BCMPCIE)
  			skb->data = skb->head;
  #ifdef NET_SKBUFF_DATA_USES_OFFSET
  			skb_set_tail_pointer(skb, NET_SKB_PAD);
  #else
  			skb->tail = skb->data + NET_SKB_PAD;
  #endif /* NET_SKBUFF_DATA_USES_OFFSET */
  			skb->data += NET_SKB_PAD;
  			skb->cloned = 0;
  			skb->priority = 0;
  #endif /* BCMPCIE */
  #ifdef NET_SKBUFF_DATA_USES_OFFSET
  			skb_set_tail_pointer(skb, len);
  #else
  			skb->tail = skb->data + len;
  #endif /* NET_SKBUFF_DATA_USES_OFFSET */
  			skb->len = len;
  #if defined(BCMPCIE)
  			spin_unlock_irqrestore(&bcm_static_skb->osl_pkt_lock, flags);
  #else
  			up(&bcm_static_skb->osl_pkt_sem);
  #endif /* BCMPCIE */
  #ifdef HW_WIFI_SECURITY_PATCH
  			memset(skb->data, 0, skb->len);
  #endif
  			return skb;
  		}
  	}
  
  #if !defined(BCMPCIE)
  #if defined(ENHANCED_STATIC_BUF)
  	if (bcm_static_skb->skb_16k &&
  		bcm_static_skb->pkt_use[STATIC_PKT_MAX_NUM - 1] == 0) {
  		bcm_static_skb->pkt_use[STATIC_PKT_MAX_NUM - 1] = 1;
  
  		skb = bcm_static_skb->skb_16k;
  #ifdef NET_SKBUFF_DATA_USES_OFFSET
  		skb_set_tail_pointer(skb, len);
  #else
  		skb->tail = skb->data + len;
  #endif
  		skb->len = len;
  
  		up(&bcm_static_skb->osl_pkt_sem);
  #ifdef HW_WIFI_SECURITY_PATCH
  		memset(skb->data, 0, skb->len);
  #endif
  
  		return skb;
  	}
  #endif /* ENHANCED_STATIC_BUF */
  #endif /* !BCMPCIE */
  
  #if defined(BCMPCIE)
  	spin_unlock_irqrestore(&bcm_static_skb->osl_pkt_lock, flags);
  #else
  	up(&bcm_static_skb->osl_pkt_sem);
  #endif /* BCMPCIE */
  	printf("%s: all static pkt in use!\n", __FUNCTION__);
  	return osl_pktget(osh, len);
  }
  
  void
  osl_pktfree_static(osl_t *osh, void *p, bool send)
  {
  	int i;
  #if defined(BCMPCIE)
  	unsigned long flags;
  #endif /* BCMPCIE */
  
  	if (!p) {
  		return;
  	}
  
  	if (!bcm_static_skb) {
  		osl_pktfree(osh, p, send);
  		return;
  	}
  
  #if defined(BCMPCIE)
  	spin_lock_irqsave(&bcm_static_skb->osl_pkt_lock, flags);
  #else
  	down(&bcm_static_skb->osl_pkt_sem);
  
  	for (i = 0; i < STATIC_PKT_1PAGE_NUM; i++) {
  		if (p == bcm_static_skb->skb_4k[i]) {
  			bcm_static_skb->pkt_use[i] = 0;
  			up(&bcm_static_skb->osl_pkt_sem);
  
  			return;
  		}
  	}
  #endif /* BCMPCIE */
  
  	for (i = STATIC_PKT_1PAGE_NUM; i < STATIC_PKT_1_2PAGE_NUM; i++) {
  		if (p == bcm_static_skb->skb_8k[i - STATIC_PKT_1PAGE_NUM]) {
  			if (bcm_static_skb->pkt_use[i] == 0)
  				printf("%s: double free! pkt idx %d(%p)\n", __FUNCTION__, i, p);
  
  			bcm_static_skb->pkt_use[i] = 0;
  #if defined(BCMPCIE)
  			spin_unlock_irqrestore(&bcm_static_skb->osl_pkt_lock, flags);
  #else
  			up(&bcm_static_skb->osl_pkt_sem);
  #endif /* BCMPCIE */
  			return;
  		}
  	}
  #if !defined(BCMPCIE)
  #ifdef ENHANCED_STATIC_BUF
  	if (p == bcm_static_skb->skb_16k) {
  		bcm_static_skb->pkt_use[STATIC_PKT_MAX_NUM - 1] = 0;
  		up(&bcm_static_skb->osl_pkt_sem);
  		return;
  	}
  #endif /* ENHANCED_STATIC_BUF */
  #endif /* !BCMPCIE */
  
  #if defined(BCMPCIE)
  	spin_unlock_irqrestore(&bcm_static_skb->osl_pkt_lock, flags);
  #else
  	up(&bcm_static_skb->osl_pkt_sem);
  #endif /* BCMPCIE */
  	osl_pktfree(osh, p, send);
  }
  
  void
  osl_pktclear_static(osl_t *osh)
  {
  	int i;
  #if defined(BCMPCIE)
  	unsigned long flags;
  #endif /* BCMPCIE */
  
  	if (!bcm_static_skb) {
  		printf("%s: bcm_static_skb is NULL\n", __FUNCTION__);
  		return;
  	}
  
  #if defined(BCMPCIE)
  	spin_lock_irqsave(&bcm_static_skb->osl_pkt_lock, flags);
  #else
  	down(&bcm_static_skb->osl_pkt_sem);
  #endif /* BCMPCIE */
  	for (i = 0; i < STATIC_PKT_MAX_NUM; i++) {
  		if (bcm_static_skb->pkt_use[i]) {
  			bcm_static_skb->pkt_use[i] = 0;
  		}
  	}
  
  #if defined(BCMPCIE)
  	spin_unlock_irqrestore(&bcm_static_skb->osl_pkt_lock, flags);
  #else
  	up(&bcm_static_skb->osl_pkt_sem);
  #endif /* BCMPCIE */
  }
  #endif /* CONFIG_DHD_USE_STATIC_BUF */
  #endif /* BCM_PCIE_UPDATE */
  
  void
  osl_pktfree_all_static(osl_t *osh)
  {
  #ifdef DHD_USE_STATIC_CTRLBUF
          int i;
          unsigned long flags;
  #endif /* DHD_USE_STATIC_CTRLBUF */
  
          if (!bcm_static_skb) {
                  printf("%s: bcm_static_skb is NULL\n", __FUNCTION__);
                  return;
          }
  
  #ifdef DHD_USE_STATIC_CTRLBUF
          spin_lock_irqsave(&bcm_static_skb->osl_pkt_lock, flags);
  
          for (i = 0; i < STATIC_PKT_2PAGE_NUM; i++) {
                  if (bcm_static_skb->pkt_use[i] != 0) {
                          bcm_static_skb->pkt_use[i] = 0;
                          bcm_static_skb->skb_8k[i]->mac_len = PREALLOC_FREE_MAGIC;
                  }
          }
          spin_unlock_irqrestore(&bcm_static_skb->osl_pkt_lock, flags);
          printf("%s: bcm_static_skb pkt_use is clear\n", __FUNCTION__);
  #endif /* DHD_USE_STATIC_CTRLBUF */
  }
  
  uint32
  osl_pci_read_config(osl_t *osh, uint offset, uint size)
  {
  	uint val = 0;
  	uint retry = PCI_CFG_RETRY;
  
  	ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
  
  	/* only 4byte access supported */
  	ASSERT(size == 4);
  
  	do {
  		pci_read_config_dword(osh->pdev, offset, &val);
  		if (val != 0xffffffff)
  			break;
  	} while (retry--);
  
  
  	return (val);
  }
  
  void
  osl_pci_write_config(osl_t *osh, uint offset, uint size, uint val)
  {
  	uint retry = PCI_CFG_RETRY;
  
  	ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
  
  	/* only 4byte access supported */
  	ASSERT(size == 4);
  
  	do {
  		pci_write_config_dword(osh->pdev, offset, val);
  		if (offset != PCI_BAR0_WIN)
  			break;
  		if (osl_pci_read_config(osh, offset, size) == val)
  			break;
  	} while (retry--);
  
  }
  
  /* return bus # for the pci device pointed by osh->pdev */
  uint
  osl_pci_bus(osl_t *osh)
  {
  	ASSERT(osh && (osh->magic == OS_HANDLE_MAGIC) && osh->pdev);
  
  #if defined(__ARM_ARCH_7A__) && LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35)
  	return pci_domain_nr(((struct pci_dev *)osh->pdev)->bus);
  #else
  	return ((struct pci_dev *)osh->pdev)->bus->number;
  #endif
  }
  
  /* return slot # for the pci device pointed by osh->pdev */
  uint
  osl_pci_slot(osl_t *osh)
  {
  	ASSERT(osh && (osh->magic == OS_HANDLE_MAGIC) && osh->pdev);
  
  #if defined(__ARM_ARCH_7A__) && LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35)
  	return PCI_SLOT(((struct pci_dev *)osh->pdev)->devfn) + 1;
  #else
  	return PCI_SLOT(((struct pci_dev *)osh->pdev)->devfn);
  #endif
  }
  
  /* return domain # for the pci device pointed by osh->pdev */
  uint
  osl_pcie_domain(osl_t *osh)
  {
  	ASSERT(osh && (osh->magic == OS_HANDLE_MAGIC) && osh->pdev);
  
  	return pci_domain_nr(((struct pci_dev *)osh->pdev)->bus);
  }
  
  /* return bus # for the pci device pointed by osh->pdev */
  uint
  osl_pcie_bus(osl_t *osh)
  {
  	ASSERT(osh && (osh->magic == OS_HANDLE_MAGIC) && osh->pdev);
  
  	return ((struct pci_dev *)osh->pdev)->bus->number;
  }
  
  /* return the pci device pointed by osh->pdev */
  struct pci_dev *
  osl_pci_device(osl_t *osh)
  {
  	ASSERT(osh && (osh->magic == OS_HANDLE_MAGIC) && osh->pdev);
  
  	return osh->pdev;
  }
  
  static void
  osl_pcmcia_attr(osl_t *osh, uint offset, char *buf, int size, bool write)
  {
  }
  
  void
  osl_pcmcia_read_attr(osl_t *osh, uint offset, void *buf, int size)
  {
  	osl_pcmcia_attr(osh, offset, (char *) buf, size, FALSE);
  }
  
  void
  osl_pcmcia_write_attr(osl_t *osh, uint offset, void *buf, int size)
  {
  	osl_pcmcia_attr(osh, offset, (char *) buf, size, TRUE);
  }
  
  void *
  osl_malloc(osl_t *osh, uint size)
  {
  	void *addr;
  	gfp_t flags;
  
  	/* only ASSERT if osh is defined */
  	if (osh)
  		ASSERT(osh->magic == OS_HANDLE_MAGIC);
  #ifdef CONFIG_DHD_USE_STATIC_BUF
  #ifdef BCM_PCIE_UPDATE
  	if (bcm_static_buf)
  	{
  		int i = 0;
  		if ((size >= PAGE_SIZE)&&(size <= STATIC_BUF_SIZE))
  		{
  			down(&bcm_static_buf->static_sem);
  
  			for (i = 0; i < STATIC_BUF_MAX_NUM; i++)
  			{
  				if (bcm_static_buf->buf_use[i] == 0)
  					break;
  			}
  
  			if (i == STATIC_BUF_MAX_NUM)
  			{
  				up(&bcm_static_buf->static_sem);
  				printf("all static buff in use!\n");
  				goto original;
  			}
  
  			bcm_static_buf->buf_use[i] = 1;
  			up(&bcm_static_buf->static_sem);
  #else
  	if (bcm_static_buf)
  	{
  		unsigned long irq_flags;
  		int i = 0;
  		if ((size >= PAGE_SIZE)&&(size <= STATIC_BUF_SIZE))
  		{
  			spin_lock_irqsave(&bcm_static_buf->static_lock, irq_flags);
  
  			for (i = 0; i < STATIC_BUF_MAX_NUM; i++)
  			{
  				if (bcm_static_buf->buf_use[i] == 0)
  					break;
  			}
  
  			if (i == STATIC_BUF_MAX_NUM)
  			{
  				spin_unlock_irqrestore(&bcm_static_buf->static_lock, irq_flags);
  				printf("all static buff in use!\n");
  				goto original;
  			}
  
  			bcm_static_buf->buf_use[i] = 1;
  			spin_unlock_irqrestore(&bcm_static_buf->static_lock, irq_flags);
  
  #endif
  			bzero(bcm_static_buf->buf_ptr+STATIC_BUF_SIZE*i, size);
  			if (osh)
  				atomic_add(size, &osh->cmn->malloced);
  
  			return ((void *)(bcm_static_buf->buf_ptr+STATIC_BUF_SIZE*i));
  		}
  	}
  original:
  #endif /* CONFIG_DHD_USE_STATIC_BUF */
  
  	flags = CAN_SLEEP() ? GFP_KERNEL: GFP_ATOMIC;
  	if ((addr = kmalloc(size, flags)) == NULL) {
  		if (osh)
  			osh->failed++;
  		return (NULL);
  	}
  	if (osh && osh->cmn)
  		atomic_add(size, &osh->cmn->malloced);
  
  	return (addr);
  }
  
  void *
  osl_mallocz(osl_t *osh, uint size)
  {
  	void *ptr;
  
  	ptr = osl_malloc(osh, size);
  
  	if (ptr != NULL) {
  		bzero(ptr, size);
  	}
  
  	return ptr;
  }
  
  void
  osl_mfree(osl_t *osh, void *addr, uint size)
  {
  #ifdef CONFIG_DHD_USE_STATIC_BUF
  #ifndef BCM_PCIE_UPDATE
  	unsigned long flags;
  #endif
  	if (bcm_static_buf)
  	{
  		if ((addr > (void *)bcm_static_buf) && ((unsigned char *)addr
  			<= ((unsigned char *)bcm_static_buf + STATIC_BUF_TOTAL_LEN)))
  		{
  			int buf_idx = 0;
  
  			buf_idx = ((unsigned char *)addr - bcm_static_buf->buf_ptr)/STATIC_BUF_SIZE;
  #ifdef BCM_PCIE_UPDATE
  			down(&bcm_static_buf->static_sem);
  			bcm_static_buf->buf_use[buf_idx] = 0;
  			up(&bcm_static_buf->static_sem);
  #else
  			spin_lock_irqsave(&bcm_static_buf->static_lock, flags);
  			bcm_static_buf->buf_use[buf_idx] = 0;
  			spin_unlock_irqrestore(&bcm_static_buf->static_lock, flags);
  #endif
  
  			if (osh && osh->cmn) {
  				ASSERT(osh->magic == OS_HANDLE_MAGIC);
  				atomic_sub(size, &osh->cmn->malloced);
  			}
  			return;
  		}
  	}
  #endif /* CONFIG_DHD_USE_STATIC_BUF */
  	if (osh && osh->cmn) {
  		ASSERT(osh->magic == OS_HANDLE_MAGIC);
  
  		ASSERT(size <= osl_malloced(osh));
  
  		atomic_sub(size, &osh->cmn->malloced);
  	}
  	kfree(addr);
  }
  
  uint
  osl_check_memleak(osl_t *osh)
  {
  	ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
  	if (atomic_read(&osh->cmn->refcount) == 1)
  		return (atomic_read(&osh->cmn->malloced));
  	else
  		return 0;
  }
  
  uint
  osl_malloced(osl_t *osh)
  {
  	ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
  	return (atomic_read(&osh->cmn->malloced));
  }
  
  uint
  osl_malloc_failed(osl_t *osh)
  {
  	ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
  	return (osh->failed);
  }
  
  
  uint
  osl_dma_consistent_align(void)
  {
  	return (PAGE_SIZE);
  }
  
  void*
  osl_dma_alloc_consistent(osl_t *osh, uint size, uint16 align_bits, uint *alloced, dmaaddr_t *pap)
  {
  	void *va;
  	uint16 align = (1 << align_bits);
  	ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
  
  	if (!ISALIGNED(DMA_CONSISTENT_ALIGN, align))
  		size += align;
  	*alloced = size;
  
  #if defined(USE_KMALLOC_FOR_FLOW_RING) && defined(__ARM_ARCH_7A__)
  	va = kmalloc(size, GFP_ATOMIC | __GFP_ZERO);
  	if (va)
  		*pap = (ulong)__virt_to_phys((ulong)va);
  #else
  	{
  		dma_addr_t pap_lin;
  #ifdef BCM_ALLOC_STATIC_24K
  		struct pci_dev *hwdev = osh->pdev;
  		gfp_t flags = 0;
  		int i = 0;
  		flags = CAN_SLEEP() ? GFP_KERNEL: GFP_ATOMIC;
  		/* use static buf when alloc GFP_ATOMIC 24K */
  		if (flags == GFP_ATOMIC && size == FLOWRING_BUF_SIZE ) {
  				va = osl_dma_alloc_static_flowring(pap, &i);
  				if (va) {
  					printf("[WLAN] %s: use static flowring. size=%d, idx=%d\n",
  						__FUNCTION__, size, i);
  					return va;
  				}
  			atomic_allocated_size += size;
  			printf("[WLAN] %s: use atomic mem. size=%d, used_size=%d\n", __FUNCTION__, size,
  				atomic_allocated_size);
  		}
  
  		va = dma_alloc_coherent(&hwdev->dev, size, &pap_lin, flags);
  #endif
  #ifdef BCM_PCIE_UPDATE
  		struct pci_dev *hwdev = osh->pdev;
  		gfp_t flags;
  #ifdef DHD_ALLOC_COHERENT_MEM_FROM_ATOMIC_POOL
  		flags = GFP_ATOMIC;
  #else
  		flags = GFP_KERNEL;
  #endif /* DHD_ALLOC_COHERENT_MEM_FROM_ATOMIC_POOL */
  		va = dma_alloc_coherent(&hwdev->dev, size, &pap_lin, flags);
  #else /* BCM_PCIE_UPDATE */
  		va = pci_alloc_consistent(osh->pdev, size, &pap_lin);
  #endif /* BCM_PCIE_UPDATE */
  #ifdef BCMDMA64OSL
  		memcpy(pap, &pap_lin, sizeof(dmaaddr_t));
  #else
  		*pap = (dmaaddr_t)pap_lin;
  #endif /* BCMDMA64OSL */
  
  	}
  #endif
  	return va;
  }
  
  void
  osl_dma_free_consistent(osl_t *osh, void *va, uint size, dmaaddr_t pa)
  {
  #ifdef BCMDMA64OSL
  	dma_addr_t *da = (dma_addr_t *)(&pa);
  #endif
  	ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
  
  #if defined(USE_KMALLOC_FOR_FLOW_RING) && defined(__ARM_ARCH_7A__)
  	kfree(va);
  #else
  #ifdef BCM_ALLOC_STATIC_24K
  	if (size == FLOWRING_BUF_SIZE) {
  		if (osl_dma_clear_static_flowring(va)) {
  			/* found static flow ring buffer */
  			return;
  		}
  	}
  #endif
  #ifdef BCMDMA64OSL
  	pci_free_consistent(osh->pdev, size, va, *da);
  #else
  	pci_free_consistent(osh->pdev, size, va, (dma_addr_t)pa);
  #endif /* BCMDMA64OSL */
  #endif
  }
  
  #ifdef CONFIG_HW_WIFI_DMA_ADDR_CTRL
  static unsigned int dma_error_count = 0;
  #endif
  
  dmaaddr_t BCMFASTPATH
  osl_dma_map(osl_t *osh, void *va, uint size, int direction, void *p, hnddma_seg_map_t *dmah)
  {
  	int dir;
  #ifdef BCM_PCIE_UPDATE
  	dmaaddr_t ret_addr;
  	dma_addr_t  map_addr;
  	int ret;
  #endif
  	ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
  	dir = (direction == DMA_TX)? PCI_DMA_TODEVICE: PCI_DMA_FROMDEVICE;
  #ifndef BCM_PCIE_UPDATE
  #if defined(__ARM_ARCH_7A__) && defined(BCMDMASGLISTOSL)
  	if (dmah != NULL) {
  		int32 nsegs, i, totsegs = 0, totlen = 0;
  		struct scatterlist *sg, _sg[MAX_DMA_SEGS * 2];
  		struct sk_buff *skb;
  		for (skb = (struct sk_buff *)p; skb != NULL; skb = PKTNEXT(osh, skb)) {
  			sg = &_sg[totsegs];
  			if (skb_is_nonlinear(skb)) {
  				nsegs = skb_to_sgvec(skb, sg, 0, PKTLEN(osh, skb));
  				ASSERT((nsegs > 0) && (totsegs + nsegs <= MAX_DMA_SEGS));
  				pci_map_sg(osh->pdev, sg, nsegs, dir);
  			} else {
  				nsegs = 1;
  				ASSERT(totsegs + nsegs <= MAX_DMA_SEGS);
  				sg->page_link = 0;
  				sg_set_buf(sg, PKTDATA(osh, skb), PKTLEN(osh, skb));
  				pci_map_single(osh->pdev, PKTDATA(osh, skb), PKTLEN(osh, skb), dir);
  			}
  			totsegs += nsegs;
  			totlen += PKTLEN(osh, skb);
  		}
  		dmah->nsegs = totsegs;
  		dmah->origsize = totlen;
  		for (i = 0, sg = _sg; i < totsegs; i++, sg++) {
  			dmah->segs[i].addr = sg_phys(sg);
  			dmah->segs[i].length = sg->length;
  		}
  		return dmah->segs[0].addr;
  	}
  #endif /* __ARM_ARCH_7A__ && BCMDMASGLISTOSL */
  
  	return (pci_map_single(osh->pdev, va, size, dir));
  #else
  	map_addr = pci_map_single(osh->pdev, va, size, dir);
  #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
  	ret = pci_dma_mapping_error(osh->pdev, map_addr);
  #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 5))
  	ret = pci_dma_mapping_error(map_addr);
  #else
  	ret = 0;
  #endif
  
  #ifdef CONFIG_HW_WIFI_DMA_ADDR_CTRL
  	if ((map_addr > ATLANTA_CODE_ADDR_BEGIN) && (map_addr < ATLANTA_CODE_ADDR_END)) {
  		if (dma_error_count < DMA_ERROR_COUNT) {
  			dma_error_count++;
  			printk("%s: map_addr is error, map_addr = %u, dma_error_count = %d\n", __FUNCTION__, (u32)map_addr, dma_error_count);
  			ret = 1;
  		} else {
  			printk("%s: map_addr is error and more than 5 times, map_addr = %u, dma_error_count = %d\n", __FUNCTION__, (u32)map_addr, dma_error_count);
  			dma_error_count = 0;
  		}
  #ifdef HW_WIFI_DMD_LOG
  		hw_wifi_dsm_client_notify(DSM_WIFI_CMD53_ERROR, "%s: map_addr is error %x, count is %d\n", __FUNCTION__, map_addr, dma_error_count);
  #endif
  	}
  #endif
  
  	if (ret) {
  		printk("%s: Failed to map memory\n", __FUNCTION__);
  		PHYSADDRLOSET(ret_addr, 0);
  		PHYSADDRHISET(ret_addr, 0);
  	} else {
  		PHYSADDRLOSET(ret_addr, map_addr & 0xffffffff);
  		PHYSADDRHISET(ret_addr, (map_addr >> 32) & 0xffffffff);
  	}
  
  	return ret_addr;
  #endif
  }
  #ifndef BCM_PCIE_UPDATE
  void BCMFASTPATH
  osl_dma_unmap(osl_t *osh, uint pa, uint size, int direction)
  #else
  void BCMFASTPATH
  osl_dma_unmap(osl_t *osh, dmaaddr_t pa, uint size, int direction)
  #endif
  {
  	int dir;
  #ifdef BCMDMA64OSL
  	dma_addr_t *da = (dma_addr_t *)(&pa);
  #endif
  
  	ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
  	dir = (direction == DMA_TX)? PCI_DMA_TODEVICE: PCI_DMA_FROMDEVICE;
  #ifdef BCMDMA64OSL
  	pci_unmap_single(osh->pdev, *da, size, dir);
  #else
  	pci_unmap_single(osh->pdev, (uint32)pa, size, dir);
  #endif
  }
  #ifdef BCM_PCIE_UPDATE
  /* OSL function for CPU relax */
  inline void BCMFASTPATH
  osl_cpu_relax(void)
  {
  	cpu_relax();
  }
  inline bool osl_pcie_linkdown(osl_t *osh)
  {
  	return (!osh->bus_handle);
  }
  #endif
  #if defined(USE_KMALLOC_FOR_FLOW_RING) && defined(__ARM_ARCH_7A__)
  
  inline void BCMFASTPATH
  osl_cache_flush(void *va, uint size)
  {
  	if (size > 0)
  		dma_sync_single_for_device(OSH_NULL, virt_to_dma(OSH_NULL, va), size, DMA_TX);
  }
  
  inline void BCMFASTPATH
  osl_cache_inv(void *va, uint size)
  {
  	dma_sync_single_for_cpu(OSH_NULL, virt_to_dma(OSH_NULL, va), size, DMA_RX);
  }
  
  inline void osl_prefetch(const void *ptr)
  {
  	/* Borrowed from linux/linux-2.6/include/asm-arm/processor.h */
  	__asm__ __volatile__(
  		"pld\t%0"
  		:
  		: "o" (*(char *)ptr)
  		: "cc");
  }
  
  int osl_arch_is_coherent(void)
  {
  #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0)
  	return 0;
  #else
  	return arch_is_coherent();
  #endif
  }
  #endif
  
  #if defined(BCMASSERT_LOG)
  void
  osl_assert(const char *exp, const char *file, int line)
  {
  	char tempbuf[256];
  	const char *basename;
  
  	basename = strrchr(file, '/');
  	/* skip the '/' */
  	if (basename)
  		basename++;
  
  	if (!basename)
  		basename = file;
  
  #ifdef BCMASSERT_LOG
  	snprintf(tempbuf, 64, "\"%s\": file \"%s\", line %d\n",
  		exp, basename, line);
  	printf("%s", tempbuf);
  #endif /* BCMASSERT_LOG */
  
  
  }
  #endif
  
  void
  osl_delay(uint usec)
  {
  	uint d;
  
  	while (usec > 0) {
  		d = MIN(usec, 1000);
  		udelay(d);
  		usec -= d;
  	}
  }
  
  void
  osl_sleep(uint ms)
  {
  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36)
  	if (ms < 20)
  		usleep_range(ms*1000, ms*1000 + 1000);
  	else
  #endif
  	msleep(ms);
  }
  
  
  
  /* Clone a packet.
   * The pkttag contents are NOT cloned.
   */
  void *
  osl_pktdup(osl_t *osh, void *skb)
  {
  	void * p;
  
  	ASSERT(!PKTISCHAINED(skb));
  
  	/* clear the CTFBUF flag if set and map the rest of the buffer
  	 * before cloning.
  	 */
  	PKTCTFMAP(osh, skb);
  
  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36)
  	if ((p = pskb_copy((struct sk_buff *)skb, GFP_ATOMIC)) == NULL)
  #else
  	if ((p = skb_clone((struct sk_buff *)skb, GFP_ATOMIC)) == NULL)
  #endif
  		return NULL;
  
  #ifdef CTFPOOL
  	if (PKTISFAST(osh, skb)) {
  		ctfpool_t *ctfpool;
  
  		/* if the buffer allocated from ctfpool is cloned then
  		 * we can't be sure when it will be freed. since there
  		 * is a chance that we will be losing a buffer
  		 * from our pool, we increment the refill count for the
  		 * object to be alloced later.
  		 */
  		ctfpool = (ctfpool_t *)CTFPOOLPTR(osh, skb);
  		ASSERT(ctfpool != NULL);
  		PKTCLRFAST(osh, p);
  		PKTCLRFAST(osh, skb);
  		ctfpool->refills++;
  	}
  #endif /* CTFPOOL */
  
  	/* Clear PKTC  context */
  	PKTSETCLINK(p, NULL);
  	PKTCCLRFLAGS(p);
  	PKTCSETCNT(p, 1);
  	PKTCSETLEN(p, PKTLEN(osh, skb));
  
  	/* skb_clone copies skb->cb.. we don't want that */
  	if (osh->pub.pkttag)
  		OSL_PKTTAG_CLEAR(p);
  
  	/* Increment the packet counter */
  	atomic_inc(&osh->cmn->pktalloced);
  	return (p);
  }
  
  
  
  /*
   * OSLREGOPS specifies the use of osl_XXX routines to be used for register access
   */
  
  /*
   * BINOSL selects the slightly slower function-call-based binary compatible osl.
   */
  
  uint
  osl_pktalloced(osl_t *osh)
  {
  	if (atomic_read(&osh->cmn->refcount) == 1)
  		return (atomic_read(&osh->cmn->pktalloced));
  	else
  		return 0;
  }
  
  uint32
  osl_rand(void)
  {
  	uint32 rand;
  
  	get_random_bytes(&rand, sizeof(rand));
  
  	return rand;
  }
  
  /* Linux Kernel: File Operations: start */
  void *
  osl_os_open_image(char *filename)
  {
  	struct file *fp;
  
  	fp = filp_open(filename, O_RDONLY, 0);
  	/*
  	 * 2.6.11 (FC4) supports filp_open() but later revs don't?
  	 * Alternative:
  	 * fp = open_namei(AT_FDCWD, filename, O_RD, 0);
  	 * ???
  	 */
  	 if (IS_ERR(fp))
  		 fp = NULL;
  
  	 return fp;
  }
  
  int
  osl_os_get_image_block(char *buf, int len, void *image)
  {
  	struct file *fp = (struct file *)image;
  	int rdlen;
  
  	if (!image)
  		return 0;
  
  	rdlen = kernel_read(fp, fp->f_pos, buf, len);
  	if (rdlen > 0)
  		fp->f_pos += rdlen;
  
  	return rdlen;
  }
  
  void
  osl_os_close_image(void *image)
  {
  	if (image)
  		filp_close((struct file *)image, NULL);
  }
  
  int
  osl_os_image_size(void *image)
  {
  	int len = 0, curroffset;
  
  	if (image) {
  		/* store the current offset */
  		curroffset = generic_file_llseek(image, 0, 1);
  		/* goto end of file to get length */
  		len = generic_file_llseek(image, 0, 2);
  		/* restore back the offset */
  		generic_file_llseek(image, curroffset, 0);
  	}
  	return len;
  }
  
  /* Linux Kernel: File Operations: end */
  
  
  /* APIs to set/get specific quirks in OSL layer */
  void
  osl_flag_set(osl_t *osh, uint32 mask)
  {
  	osh->flags |= mask;
  }
  
  bool
  osl_is_flag_set(osl_t *osh, uint32 mask)
  {
  	return (osh->flags & mask);
  }
  